2 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 #pragma ident "%Z%%M% %I% %E% SMI"
9 * usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c
13 * Copyright (c) 1991, 1993
14 * The Regents of the University of California. All rights reserved.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * Copyright (C) 1998 by the FundsXpress, INC.
48 * All rights reserved.
50 * Export of this software from the United States of America may require
51 * a specific license from the United States Government. It is the
52 * responsibility of any person or organization contemplating export to
53 * obtain such a license before exporting.
55 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
56 * distribute this software and its documentation for any purpose and
57 * without fee is hereby granted, provided that the above copyright
58 * notice appear in all copies and that both that copyright notice and
59 * this permission notice appear in supporting documentation, and that
60 * the name of FundsXpress. not be used in advertising or publicity pertaining
61 * to distribution of the software without specific, written prior
62 * permission. FundsXpress makes no representations about the suitability of
63 * this software for any purpose. It is provided "as is" without express
64 * or implied warranty.
66 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
67 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
68 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
71 /* based on @(#)enc_des.c 8.1 (Berkeley) 6/4/93 */
73 * Copyright (c) 2016 by Delphix. All rights reserved.
78 #include <arpa/telnet.h>
86 extern boolean_t encrypt_debug_mode
;
87 extern krb5_context telnet_context
;
89 #define KEYFLAG_SHIFT 2
90 #define SHIFT_VAL(a, b) (KEYFLAG_SHIFT*((a)+((b)*2)))
94 int state
[2]; /* state for each direction */
95 int keyid
[2]; /* keyid for each direction */
97 unsigned char fb_feed
[64];
104 unsigned char str_keybytes
[DES_BLOCKSIZE
];
105 krb5_keyblock str_key
;
108 } streams
[2]; /* one for encrypt, one for decrypt */
111 static void cfb64_stream_iv(Block
, struct stinfo
*);
112 static void cfb64_stream_key(Block
, struct stinfo
*);
115 ecb_encrypt(struct stinfo
*stp
, Block in
, Block out
)
117 krb5_error_code code
;
121 din
.length
= DES_BLOCKSIZE
;
122 din
.data
= (char *)in
;
124 dout
.ciphertext
.length
= DES_BLOCKSIZE
;
125 dout
.ciphertext
.data
= (char *)out
;
126 /* this is a kerberos enctype, not a telopt enctype */
127 dout
.enctype
= ENCTYPE_UNKNOWN
;
129 code
= krb5_c_encrypt(telnet_context
, &stp
->str_key
, NULL
, NULL
,
132 (void) fprintf(stderr
, gettext(
133 "Error encrypting stream data (%s)\r\n"), code
);
139 register struct _fb
*fbp
= &des_cfb
;
141 (void) memset((void *)fbp
, 0, sizeof (*fbp
));
142 fbp
->state
[0] = des_cfb
.state
[1] = ENCR_STATE_FAILED
;
143 fbp
->fb_feed
[0] = IAC
;
144 fbp
->fb_feed
[1] = SB
;
145 fbp
->fb_feed
[2] = TELOPT_ENCRYPT
;
146 fbp
->fb_feed
[3] = ENCRYPT_IS
;
148 fbp
->fb_feed
[4] = TELOPT_ENCTYPE_DES_CFB64
;
149 fbp
->streams
[TELNET_DIR_DECRYPT
].str_flagshift
=
151 fbp
->streams
[TELNET_DIR_ENCRYPT
].str_flagshift
=
158 * -1: some error. Negotiation is done, encryption not ready.
159 * 0: Successful, initial negotiation all done.
160 * 1: successful, negotiation not done yet.
161 * 2: Not yet. Other things (like getting the key from
162 * Kerberos) have to happen before we can continue.
167 struct _fb
*fbp
= &des_cfb
;
173 case TELNET_DIR_DECRYPT
:
175 * This is simply a request to have the other side
176 * start output (our input). The other side will negotiate an
177 * IV so we need not look for it.
179 state
= fbp
->state
[dir
];
180 if (state
== ENCR_STATE_FAILED
)
181 state
= ENCR_STATE_IN_PROGRESS
;
184 case TELNET_DIR_ENCRYPT
:
185 state
= fbp
->state
[dir
];
186 if (state
== ENCR_STATE_FAILED
)
187 state
= ENCR_STATE_IN_PROGRESS
;
188 else if ((state
& ENCR_STATE_NO_SEND_IV
) == 0)
191 if (!fbp
->validkey
) {
192 fbp
->need_start
= B_TRUE
;
195 state
&= ~ENCR_STATE_NO_SEND_IV
;
196 state
|= ENCR_STATE_NO_RECV_IV
;
197 if (encrypt_debug_mode
)
198 (void) printf(gettext("Creating new feed\r\n"));
200 * Create a random feed and send it over.
204 krb5_error_code code
;
206 d
.data
= (char *)fbp
->temp_feed
;
207 d
.length
= sizeof (fbp
->temp_feed
);
209 code
= krb5_c_random_make_octets(telnet_context
, &d
);
211 return (ENCR_STATE_FAILED
);
214 p
= fbp
->fb_feed
+ 3;
218 for (x
= 0; x
< sizeof (Block
); ++x
) {
219 if ((*p
++ = fbp
->temp_feed
[x
]) == IAC
)
224 printsub('>', &fbp
->fb_feed
[2], p
- &fbp
->fb_feed
[2]);
225 (void) net_write(fbp
->fb_feed
, p
- fbp
->fb_feed
);
228 return (ENCR_STATE_FAILED
);
230 return (fbp
->state
[dir
] = state
);
235 * -1: some error. Negotiation is done, encryption not ready.
236 * 0: Successful, initial negotiation all done.
237 * 1: successful, negotiation not done yet.
240 cfb64_is(unsigned char *data
, int cnt
)
243 struct _fb
*fbp
= &des_cfb
;
244 register int state
= fbp
->state
[TELNET_DIR_DECRYPT
];
251 if (cnt
!= sizeof (Block
)) {
252 if (encrypt_debug_mode
)
253 (void) printf(gettext(
254 "CFB64: initial vector failed "
256 state
= ENCR_STATE_FAILED
;
260 if (encrypt_debug_mode
)
261 (void) printf(gettext(
262 "CFB64: initial vector received\r\n"));
264 if (encrypt_debug_mode
)
265 (void) printf(gettext(
266 "Initializing Decrypt stream\r\n"));
268 cfb64_stream_iv((void *)data
,
269 &fbp
->streams
[TELNET_DIR_DECRYPT
]);
271 p
= fbp
->fb_feed
+ 3;
272 *p
++ = ENCRYPT_REPLY
;
277 printsub('>', &fbp
->fb_feed
[2], p
- &fbp
->fb_feed
[2]);
278 (void) net_write(fbp
->fb_feed
, p
- fbp
->fb_feed
);
280 state
= fbp
->state
[TELNET_DIR_DECRYPT
] = ENCR_STATE_IN_PROGRESS
;
284 if (encrypt_debug_mode
) {
285 (void) printf(gettext(
286 "Unknown option type: %d\r\n"), *(data
-1));
288 (void) printf("\r\n");
293 * We failed. Send an FB64_IV_BAD option
294 * to the other side so it will know that
297 p
= fbp
->fb_feed
+ 3;
298 *p
++ = ENCRYPT_REPLY
;
303 printsub('>', &fbp
->fb_feed
[2], p
- &fbp
->fb_feed
[2]);
304 (void) net_write(fbp
->fb_feed
, p
- fbp
->fb_feed
);
308 return (fbp
->state
[TELNET_DIR_DECRYPT
] = state
);
313 * -1: some error. Negotiation is done, encryption not ready.
314 * 0: Successful, initial negotiation all done.
315 * 1: successful, negotiation not done yet.
318 cfb64_reply(unsigned char *data
, int cnt
)
320 struct _fb
*fbp
= &des_cfb
;
321 register int state
= fbp
->state
[TELNET_DIR_ENCRYPT
];
328 cfb64_stream_iv(fbp
->temp_feed
,
329 &fbp
->streams
[TELNET_DIR_ENCRYPT
]);
330 if (state
== ENCR_STATE_FAILED
)
331 state
= ENCR_STATE_IN_PROGRESS
;
332 state
&= ~ENCR_STATE_NO_RECV_IV
;
333 encrypt_send_keyid(TELNET_DIR_ENCRYPT
,
334 (unsigned char *)"\0", 1, 1);
338 (void) memset(fbp
->temp_feed
, 0, sizeof (Block
));
339 cfb64_stream_iv(fbp
->temp_feed
,
340 &fbp
->streams
[TELNET_DIR_ENCRYPT
]);
341 state
= ENCR_STATE_FAILED
;
345 if (encrypt_debug_mode
) {
346 (void) printf(gettext(
347 "Unknown option type: %d\r\n"), data
[-1]);
349 (void) printf("\r\n");
353 state
= ENCR_STATE_FAILED
;
356 return (fbp
->state
[TELNET_DIR_ENCRYPT
] = state
);
360 cfb64_session(Session_Key
*key
)
362 struct _fb
*fbp
= &des_cfb
;
364 if (!key
|| key
->type
!= SK_DES
) {
365 if (encrypt_debug_mode
)
366 (void) printf(gettext(
367 "Can't set DES's session key (%d != %d)\r\n"),
368 key
? key
->type
: -1, SK_DES
);
372 fbp
->validkey
= B_TRUE
;
374 cfb64_stream_key(key
->data
, &fbp
->streams
[TELNET_DIR_ENCRYPT
]);
375 cfb64_stream_key(key
->data
, &fbp
->streams
[TELNET_DIR_DECRYPT
]);
378 * Now look to see if cfb64_start() was was waiting for
379 * the key to show up. If so, go ahead an call it now
380 * that we have the key.
382 if (fbp
->need_start
) {
383 fbp
->need_start
= B_FALSE
;
384 (void) cfb64_start(TELNET_DIR_ENCRYPT
);
389 * We only accept a keyid of 0. If we get a keyid of
390 * 0, then mark the state as SUCCESS.
393 cfb64_keyid(dir
, kp
, lenp
)
397 struct _fb
*fbp
= &des_cfb
;
398 register int state
= fbp
->state
[dir
];
400 if (*lenp
!= 1 || (*kp
!= '\0')) {
405 if (state
== ENCR_STATE_FAILED
)
406 state
= ENCR_STATE_IN_PROGRESS
;
408 state
&= ~ENCR_STATE_NO_KEYID
;
410 return (fbp
->state
[dir
] = state
);
414 * Print ENCRYPT suboptions to NetTrace when "set opt" is used
417 cfb64_printsub(unsigned char *data
, int cnt
, unsigned char *buf
, int buflen
)
419 char lbuf
[ENCR_LBUF_BUFSIZ
];
422 unsigned char type
[] = "CFB64";
424 buf
[buflen
-1] = '\0'; /* make sure it's NULL terminated */
429 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, "%s_IV", type
);
434 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, "%s_IV_OK", type
);
439 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, "%s_IV_BAD", type
);
444 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, " %d (unknown)",
448 for (; (buflen
> 0) && (*buf
= *cp
++); buf
++)
450 for (i
= 3; i
< cnt
; i
++) {
451 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, " %d", data
[i
]);
452 for (cp
= lbuf
; (buflen
> 0) && (*buf
= *cp
++); buf
++)
461 cfb64_stream_iv(Block seed
, register struct stinfo
*stp
)
463 (void) memcpy((void *)stp
->str_iv
, (void *)seed
, sizeof (Block
));
464 (void) memcpy((void *)stp
->str_output
, (void *)seed
, sizeof (Block
));
466 stp
->str_index
= sizeof (Block
);
470 cfb64_stream_key(Block key
, register struct stinfo
*stp
)
472 (void) memcpy((void *)stp
->str_keybytes
, (void *)key
, sizeof (Block
));
473 stp
->str_key
.length
= DES_BLOCKSIZE
;
474 stp
->str_key
.contents
= stp
->str_keybytes
;
476 * the original version of this code uses des ecb mode, but
477 * it only ever does one block at a time. cbc with a zero iv
480 /* this is a kerberos enctype, not a telopt enctype */
481 stp
->str_key
.enctype
= ENCTYPE_DES_CBC_RAW
;
483 (void) memcpy((void *)stp
->str_output
, (void *)stp
->str_iv
,
486 stp
->str_index
= sizeof (Block
);
490 * DES 64 bit Cipher Feedback
496 * INPUT --(--------->(+)+---> DATA
502 * iV: Initial vector, 64 bits (8 bytes) long.
503 * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
504 * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
508 * V(n+1) = DES(On, key)
512 cfb64_encrypt(register unsigned char *s
, int c
)
514 register struct stinfo
*stp
=
515 &des_cfb
.streams
[TELNET_DIR_ENCRYPT
];
518 index
= stp
->str_index
;
520 if (index
== sizeof (Block
)) {
522 ecb_encrypt(stp
, stp
->str_output
, b
);
523 (void) memcpy((void *)stp
->str_feed
, (void *)b
,
528 /* On encryption, we store (feed ^ data) which is cypher */
529 *s
= stp
->str_output
[index
] = (stp
->str_feed
[index
] ^ *s
);
533 stp
->str_index
= index
;
537 cfb64_decrypt(int data
)
539 register struct stinfo
*stp
=
540 &des_cfb
.streams
[TELNET_DIR_DECRYPT
];
545 * Back up one byte. It is assumed that we will
546 * never back up more than one byte. If we do, this
547 * may or may not work.
554 index
= stp
->str_index
++;
555 if (index
== sizeof (Block
)) {
557 ecb_encrypt(stp
, stp
->str_output
, b
);
558 (void) memcpy((void *)stp
->str_feed
, (void *)b
, sizeof (Block
));
559 stp
->str_index
= 1; /* Next time will be 1 */
560 index
= 0; /* But now use 0 */
563 /* On decryption we store (data) which is cypher. */
564 stp
->str_output
[index
] = data
;
565 return (data
^ stp
->str_feed
[index
]);