2 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c
11 * Copyright (c) 1991, 1993
12 * The Regents of the University of California. All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the University of
25 * California, Berkeley and its contributors.
26 * 4. Neither the name of the University nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * Copyright (C) 1998 by the FundsXpress, INC.
46 * All rights reserved.
48 * Export of this software from the United States of America may require
49 * a specific license from the United States Government. It is the
50 * responsibility of any person or organization contemplating export to
51 * obtain such a license before exporting.
53 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
54 * distribute this software and its documentation for any purpose and
55 * without fee is hereby granted, provided that the above copyright
56 * notice appear in all copies and that both that copyright notice and
57 * this permission notice appear in supporting documentation, and that
58 * the name of FundsXpress. not be used in advertising or publicity pertaining
59 * to distribution of the software without specific, written prior
60 * permission. FundsXpress makes no representations about the suitability of
61 * this software for any purpose. It is provided "as is" without express
62 * or implied warranty.
64 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
65 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
66 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
69 /* based on @(#)enc_des.c 8.1 (Berkeley) 6/4/93 */
71 * Copyright (c) 2016 by Delphix. All rights reserved.
76 #include <arpa/telnet.h>
84 extern boolean_t encrypt_debug_mode
;
85 extern krb5_context telnet_context
;
87 #define KEYFLAG_SHIFT 2
88 #define SHIFT_VAL(a, b) (KEYFLAG_SHIFT*((a)+((b)*2)))
92 int state
[2]; /* state for each direction */
93 int keyid
[2]; /* keyid for each direction */
95 unsigned char fb_feed
[64];
102 unsigned char str_keybytes
[DES_BLOCKSIZE
];
103 krb5_keyblock str_key
;
106 } streams
[2]; /* one for encrypt, one for decrypt */
109 static void cfb64_stream_iv(Block
, struct stinfo
*);
110 static void cfb64_stream_key(Block
, struct stinfo
*);
113 ecb_encrypt(struct stinfo
*stp
, Block in
, Block out
)
115 krb5_error_code code
;
119 din
.length
= DES_BLOCKSIZE
;
120 din
.data
= (char *)in
;
122 dout
.ciphertext
.length
= DES_BLOCKSIZE
;
123 dout
.ciphertext
.data
= (char *)out
;
124 /* this is a kerberos enctype, not a telopt enctype */
125 dout
.enctype
= ENCTYPE_UNKNOWN
;
127 code
= krb5_c_encrypt(telnet_context
, &stp
->str_key
, 0, NULL
,
130 (void) fprintf(stderr
, gettext(
131 "Error encrypting stream data (%s)\r\n"), code
);
137 register struct _fb
*fbp
= &des_cfb
;
139 (void) memset(fbp
, 0, sizeof (*fbp
));
140 fbp
->state
[0] = des_cfb
.state
[1] = ENCR_STATE_FAILED
;
141 fbp
->fb_feed
[0] = IAC
;
142 fbp
->fb_feed
[1] = SB
;
143 fbp
->fb_feed
[2] = TELOPT_ENCRYPT
;
144 fbp
->fb_feed
[3] = ENCRYPT_IS
;
146 fbp
->fb_feed
[4] = TELOPT_ENCTYPE_DES_CFB64
;
147 fbp
->streams
[TELNET_DIR_DECRYPT
].str_flagshift
=
149 fbp
->streams
[TELNET_DIR_ENCRYPT
].str_flagshift
=
156 * -1: some error. Negotiation is done, encryption not ready.
157 * 0: Successful, initial negotiation all done.
158 * 1: successful, negotiation not done yet.
159 * 2: Not yet. Other things (like getting the key from
160 * Kerberos) have to happen before we can continue.
165 struct _fb
*fbp
= &des_cfb
;
171 case TELNET_DIR_DECRYPT
:
173 * This is simply a request to have the other side
174 * start output (our input). The other side will negotiate an
175 * IV so we need not look for it.
177 state
= fbp
->state
[dir
];
178 if (state
== ENCR_STATE_FAILED
)
179 state
= ENCR_STATE_IN_PROGRESS
;
182 case TELNET_DIR_ENCRYPT
:
183 state
= fbp
->state
[dir
];
184 if (state
== ENCR_STATE_FAILED
)
185 state
= ENCR_STATE_IN_PROGRESS
;
186 else if ((state
& ENCR_STATE_NO_SEND_IV
) == 0)
189 if (!fbp
->validkey
) {
190 fbp
->need_start
= B_TRUE
;
193 state
&= ~ENCR_STATE_NO_SEND_IV
;
194 state
|= ENCR_STATE_NO_RECV_IV
;
195 if (encrypt_debug_mode
)
196 (void) printf(gettext("Creating new feed\r\n"));
198 * Create a random feed and send it over.
202 krb5_error_code code
;
204 d
.data
= (char *)fbp
->temp_feed
;
205 d
.length
= sizeof (fbp
->temp_feed
);
207 code
= krb5_c_random_make_octets(telnet_context
, &d
);
209 return (ENCR_STATE_FAILED
);
212 p
= fbp
->fb_feed
+ 3;
216 for (x
= 0; x
< sizeof (Block
); ++x
) {
217 if ((*p
++ = fbp
->temp_feed
[x
]) == IAC
)
222 printsub('>', &fbp
->fb_feed
[2], p
- &fbp
->fb_feed
[2]);
223 (void) net_write(fbp
->fb_feed
, p
- fbp
->fb_feed
);
226 return (ENCR_STATE_FAILED
);
228 return (fbp
->state
[dir
] = state
);
233 * -1: some error. Negotiation is done, encryption not ready.
234 * 0: Successful, initial negotiation all done.
235 * 1: successful, negotiation not done yet.
238 cfb64_is(unsigned char *data
, int cnt
)
241 struct _fb
*fbp
= &des_cfb
;
242 register int state
= fbp
->state
[TELNET_DIR_DECRYPT
];
249 if (cnt
!= sizeof (Block
)) {
250 if (encrypt_debug_mode
)
251 (void) printf(gettext(
252 "CFB64: initial vector failed "
254 state
= ENCR_STATE_FAILED
;
258 if (encrypt_debug_mode
)
259 (void) printf(gettext(
260 "CFB64: initial vector received\r\n"));
262 if (encrypt_debug_mode
)
263 (void) printf(gettext(
264 "Initializing Decrypt stream\r\n"));
266 cfb64_stream_iv((void *)data
,
267 &fbp
->streams
[TELNET_DIR_DECRYPT
]);
269 p
= fbp
->fb_feed
+ 3;
270 *p
++ = ENCRYPT_REPLY
;
275 printsub('>', &fbp
->fb_feed
[2], p
- &fbp
->fb_feed
[2]);
276 (void) net_write(fbp
->fb_feed
, p
- fbp
->fb_feed
);
278 state
= fbp
->state
[TELNET_DIR_DECRYPT
] = ENCR_STATE_IN_PROGRESS
;
282 if (encrypt_debug_mode
) {
283 (void) printf(gettext(
284 "Unknown option type: %d\r\n"), *(data
-1));
286 (void) printf("\r\n");
291 * We failed. Send an FB64_IV_BAD option
292 * to the other side so it will know that
295 p
= fbp
->fb_feed
+ 3;
296 *p
++ = ENCRYPT_REPLY
;
301 printsub('>', &fbp
->fb_feed
[2], p
- &fbp
->fb_feed
[2]);
302 (void) net_write(fbp
->fb_feed
, p
- fbp
->fb_feed
);
306 return (fbp
->state
[TELNET_DIR_DECRYPT
] = state
);
311 * -1: some error. Negotiation is done, encryption not ready.
312 * 0: Successful, initial negotiation all done.
313 * 1: successful, negotiation not done yet.
316 cfb64_reply(unsigned char *data
, int cnt
)
318 struct _fb
*fbp
= &des_cfb
;
319 register int state
= fbp
->state
[TELNET_DIR_ENCRYPT
];
326 cfb64_stream_iv(fbp
->temp_feed
,
327 &fbp
->streams
[TELNET_DIR_ENCRYPT
]);
328 if (state
== ENCR_STATE_FAILED
)
329 state
= ENCR_STATE_IN_PROGRESS
;
330 state
&= ~ENCR_STATE_NO_RECV_IV
;
331 encrypt_send_keyid(TELNET_DIR_ENCRYPT
,
332 (unsigned char *)"\0", 1, 1);
336 (void) memset(fbp
->temp_feed
, 0, sizeof (Block
));
337 cfb64_stream_iv(fbp
->temp_feed
,
338 &fbp
->streams
[TELNET_DIR_ENCRYPT
]);
339 state
= ENCR_STATE_FAILED
;
343 if (encrypt_debug_mode
) {
344 (void) printf(gettext(
345 "Unknown option type: %d\r\n"), data
[-1]);
347 (void) printf("\r\n");
351 state
= ENCR_STATE_FAILED
;
354 return (fbp
->state
[TELNET_DIR_ENCRYPT
] = state
);
358 cfb64_session(Session_Key
*key
)
360 struct _fb
*fbp
= &des_cfb
;
362 if (!key
|| key
->type
!= SK_DES
) {
363 if (encrypt_debug_mode
)
364 (void) printf(gettext(
365 "Can't set DES's session key (%d != %d)\r\n"),
366 key
? key
->type
: -1, SK_DES
);
370 fbp
->validkey
= B_TRUE
;
372 cfb64_stream_key(key
->data
, &fbp
->streams
[TELNET_DIR_ENCRYPT
]);
373 cfb64_stream_key(key
->data
, &fbp
->streams
[TELNET_DIR_DECRYPT
]);
376 * Now look to see if cfb64_start() was was waiting for
377 * the key to show up. If so, go ahead an call it now
378 * that we have the key.
380 if (fbp
->need_start
) {
381 fbp
->need_start
= B_FALSE
;
382 (void) cfb64_start(TELNET_DIR_ENCRYPT
);
387 * We only accept a keyid of 0. If we get a keyid of
388 * 0, then mark the state as SUCCESS.
391 cfb64_keyid(dir
, kp
, lenp
)
395 struct _fb
*fbp
= &des_cfb
;
396 register int state
= fbp
->state
[dir
];
398 if (*lenp
!= 1 || (*kp
!= '\0')) {
403 if (state
== ENCR_STATE_FAILED
)
404 state
= ENCR_STATE_IN_PROGRESS
;
406 state
&= ~ENCR_STATE_NO_KEYID
;
408 return (fbp
->state
[dir
] = state
);
412 * Print ENCRYPT suboptions to NetTrace when "set opt" is used
415 cfb64_printsub(unsigned char *data
, int cnt
, unsigned char *buf
, int buflen
)
417 char lbuf
[ENCR_LBUF_BUFSIZ
];
420 unsigned char type
[] = "CFB64";
422 buf
[buflen
-1] = '\0'; /* make sure it's NULL terminated */
427 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, "%s_IV", type
);
432 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, "%s_IV_OK", type
);
437 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, "%s_IV_BAD", type
);
442 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, " %d (unknown)",
446 for (; (buflen
> 0) && (*buf
= *cp
++); buf
++)
448 for (i
= 3; i
< cnt
; i
++) {
449 (void) snprintf(lbuf
, ENCR_LBUF_BUFSIZ
, " %d", data
[i
]);
450 for (cp
= lbuf
; (buflen
> 0) && (*buf
= *cp
++); buf
++)
459 cfb64_stream_iv(Block seed
, register struct stinfo
*stp
)
461 (void) memcpy((void *)stp
->str_iv
, (void *)seed
, sizeof (Block
));
462 (void) memcpy((void *)stp
->str_output
, (void *)seed
, sizeof (Block
));
464 stp
->str_index
= sizeof (Block
);
468 cfb64_stream_key(Block key
, register struct stinfo
*stp
)
470 (void) memcpy((void *)stp
->str_keybytes
, (void *)key
, sizeof (Block
));
471 stp
->str_key
.length
= DES_BLOCKSIZE
;
472 stp
->str_key
.contents
= stp
->str_keybytes
;
474 * the original version of this code uses des ecb mode, but
475 * it only ever does one block at a time. cbc with a zero iv
478 /* this is a kerberos enctype, not a telopt enctype */
479 stp
->str_key
.enctype
= ENCTYPE_DES_CBC_RAW
;
481 (void) memcpy((void *)stp
->str_output
, (void *)stp
->str_iv
,
484 stp
->str_index
= sizeof (Block
);
488 * DES 64 bit Cipher Feedback
494 * INPUT --(--------->(+)+---> DATA
500 * iV: Initial vector, 64 bits (8 bytes) long.
501 * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
502 * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
506 * V(n+1) = DES(On, key)
510 cfb64_encrypt(register unsigned char *s
, int c
)
512 register struct stinfo
*stp
=
513 &des_cfb
.streams
[TELNET_DIR_ENCRYPT
];
516 index
= stp
->str_index
;
518 if (index
== sizeof (Block
)) {
520 ecb_encrypt(stp
, stp
->str_output
, b
);
521 (void) memcpy((void *)stp
->str_feed
, (void *)b
,
526 /* On encryption, we store (feed ^ data) which is cypher */
527 *s
= stp
->str_output
[index
] = (stp
->str_feed
[index
] ^ *s
);
531 stp
->str_index
= index
;
535 cfb64_decrypt(int data
)
537 register struct stinfo
*stp
=
538 &des_cfb
.streams
[TELNET_DIR_DECRYPT
];
543 * Back up one byte. It is assumed that we will
544 * never back up more than one byte. If we do, this
545 * may or may not work.
552 index
= stp
->str_index
++;
553 if (index
== sizeof (Block
)) {
555 ecb_encrypt(stp
, stp
->str_output
, b
);
556 (void) memcpy((void *)stp
->str_feed
, (void *)b
, sizeof (Block
));
557 stp
->str_index
= 1; /* Next time will be 1 */
558 index
= 0; /* But now use 0 */
561 /* On decryption we store (data) which is cypher. */
562 stp
->str_output
[index
] = data
;
563 return (data
^ stp
->str_feed
[index
]);