iPXE
mschapv2.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * MS-CHAPv2 authentication
29  *
30  * The algorithms used for MS-CHAPv2 authentication are defined in
31  * RFC 2759 section 8.
32  */
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <byteswap.h>
37 #include <ipxe/md4.h>
38 #include <ipxe/sha1.h>
39 #include <ipxe/des.h>
40 #include <ipxe/mschapv2.h>
41 
42 /**
43  * MS-CHAPv2 context block
44  *
45  * For no particularly discernible reason, MS-CHAPv2 uses two
46  * different digest algorithms and one block cipher. The uses do not
47  * overlap, so share the context storage between these to reduce stack
48  * usage.
49  */
51  /** SHA-1 digest context */
53  /** MD4 digest context */
55  /** DES cipher context */
57 };
58 
59 /**
60  * MS-CHAPv2 challenge hash
61  *
62  * MS-CHAPv2 calculates the SHA-1 digest of the peer challenge, the
63  * authenticator challenge, and the username, and then uses only the
64  * first 8 bytes of the result (as a DES plaintext block).
65  */
67  /** SHA-1 digest */
69  /** DES plaintext block */
71 };
72 
73 /**
74  * MS-CHAPv2 password hash
75  *
76  * MS-CHAPv2 calculates the MD4 digest of an unspecified two-byte
77  * little-endian Unicode encoding (presumably either UCS-2LE or
78  * UTF-16LE) of the password.
79  *
80  * For constructing the challenge response, the MD4 digest is then
81  * zero-padded to 21 bytes and used as three separate 56-bit DES keys.
82  *
83  * For constructing the authenticator response, the MD4 digest is then
84  * used as an input to a SHA-1 digest along with the NT response and a
85  * magic constant.
86  */
88  /** MD4 digest */
90  /** SHA-1 digest */
92  /** DES keys */
94  /** DES key expansion */
96 };
97 
98 /** MS-CHAPv2 magic constant 1 */
99 static const char mschapv2_magic1[39] =
100  "Magic server to client signing constant";
101 
102 /** MS-CHAPv2 magic constant 2 */
103 static const char mschapv2_magic2[41] =
104  "Pad to make it do more than one iteration";
105 
106 /**
107  * Calculate MS-CHAPv2 challenge hash
108  *
109  * @v ctx Context block
110  * @v challenge Authenticator challenge
111  * @v peer Peer challenge
112  * @v username User name (or NULL to use empty string)
113  * @v chash Challenge hash to fill in
114  *
115  * This is the ChallengeHash() function as documented in RFC 2759
116  * section 8.2.
117  */
118 static void
120  const struct mschapv2_challenge *challenge,
121  const struct mschapv2_challenge *peer,
122  const char *username,
123  union mschapv2_challenge_hash *chash ) {
124  struct digest_algorithm *sha1 = &sha1_algorithm;
125 
126  /* Calculate SHA-1 hash of challenges and username */
127  digest_init ( sha1, ctx->sha1 );
128  digest_update ( sha1, ctx->sha1, peer, sizeof ( *peer ) );
129  digest_update ( sha1, ctx->sha1, challenge, sizeof ( *challenge ) );
130  if ( username ) {
131  digest_update ( sha1, ctx->sha1, username,
132  strlen ( username ) );
133  }
134  digest_final ( sha1, ctx->sha1, chash->sha1 );
135  DBGC ( ctx, "MSCHAPv2 authenticator challenge:\n" );
136  DBGC_HDA ( ctx, 0, challenge, sizeof ( *challenge ) );
137  DBGC ( ctx, "MSCHAPv2 peer challenge:\n" );
138  DBGC_HDA ( ctx, 0, peer, sizeof ( *peer ) );
139  DBGC ( ctx, "MSCHAPv2 challenge hash:\n" );
140  DBGC_HDA ( ctx, 0, chash->des, sizeof ( chash->des ) );
141 }
142 
143 /**
144  * Calculate MS-CHAPv2 password hash
145  *
146  * @v ctx Context block
147  * @v password Password (or NULL to use empty string)
148  * @v phash Password hash to fill in
149  *
150  * This is the NtPasswordHash() function as documented in RFC 2759
151  * section 8.3.
152  */
154  const char *password,
155  union mschapv2_password_hash *phash ) {
156  struct digest_algorithm *md4 = &md4_algorithm;
157  uint16_t wc;
158  uint8_t c;
159 
160  /* Construct zero-padded MD4 hash of encoded password */
161  memset ( phash, 0, sizeof ( *phash ) );
162  digest_init ( md4, ctx->md4 );
163  if ( password ) {
164  while ( ( c = *(password++) ) ) {
165  wc = cpu_to_le16 ( c );
166  digest_update ( md4, ctx->md4, &wc, sizeof ( wc ) );
167  }
168  }
169  digest_final ( md4, ctx->md4, phash->md4 );
170  DBGC ( ctx, "MSCHAPv2 password hash:\n" );
171  DBGC_HDA ( ctx, 0, phash->md4, sizeof ( phash->md4 ) );
172 }
173 
174 /**
175  * Hash the MS-CHAPv2 password hash
176  *
177  * @v ctx Context block
178  * @v phash Password hash to be rehashed
179  *
180  * This is the HashNtPasswordHash() function as documented in RFC 2759
181  * section 8.4.
182  */
184  union mschapv2_password_hash *phash ) {
185  struct digest_algorithm *md4 = &md4_algorithm;
186 
187  /* Calculate MD4 hash of existing MD4 hash */
188  digest_init ( md4, ctx->md4 );
189  digest_update ( md4, ctx->md4, phash->md4, sizeof ( phash->md4 ) );
190  digest_final ( md4, ctx->md4, phash->md4 );
191  DBGC ( ctx, "MSCHAPv2 password hash hash:\n" );
192  DBGC_HDA ( ctx, 0, phash->md4, sizeof ( phash->md4 ) );
193 }
194 
195 /**
196  * Expand MS-CHAPv2 password hash by inserting DES dummy parity bits
197  *
198  * @v ctx Context block
199  * @v phash Password hash to expand
200  *
201  * This is part of the DesEncrypt() function as documented in RFC 2759
202  * section 8.6.
203  */
205  union mschapv2_password_hash *phash ) {
206  uint8_t *dst;
207  uint8_t *src;
208  unsigned int i;
209 
210  /* Expand password hash by inserting (unused) DES parity bits */
211  for ( i = ( sizeof ( phash->expand ) - 1 ) ; i > 0 ; i-- ) {
212  dst = &phash->expand[i];
213  src = ( dst - ( i / 8 ) );
214  *dst = ( ( ( src[-1] << 8 ) | src[0] ) >> ( i % 8 ) );
215  }
216  DBGC ( ctx, "MSCHAPv2 expanded password hash:\n" );
217  DBGC_HDA ( ctx, 0, phash->expand, sizeof ( phash->expand ) );
218 }
219 
220 /**
221  * Calculate MS-CHAPv2 challenge response
222  *
223  * @v ctx Context block
224  * @v chash Challenge hash
225  * @v phash Password hash (after expansion)
226  * @v nt NT response to fill in
227  *
228  * This is the ChallengeResponse() function as documented in RFC 2759
229  * section 8.5.
230  */
231 static void
233  const union mschapv2_challenge_hash *chash,
234  const union mschapv2_password_hash *phash,
235  struct mschapv2_nt_response *nt ) {
236  struct cipher_algorithm *des = &des_algorithm;
237  unsigned int i;
238  int rc;
239 
240  /* Construct response. The design of the algorithm here is
241  * interesting, suggesting that an intern at Microsoft had
242  * heard the phrase "Triple DES" and hazarded a blind guess at
243  * what it might mean.
244  */
245  for ( i = 0 ; i < ( sizeof ( phash->des ) /
246  sizeof ( phash->des[0] ) ) ; i++ ) {
247  rc = cipher_setkey ( des, ctx->des, phash->des[i],
248  sizeof ( phash->des[i] ) );
249  assert ( rc == 0 ); /* no failure mode exists */
250  cipher_encrypt ( des, ctx->des, chash->des, nt->block[i],
251  sizeof ( chash->des ) );
252  }
253  DBGC ( ctx, "MSCHAPv2 NT response:\n" );
254  DBGC_HDA ( ctx, 0, nt, sizeof ( *nt ) );
255 }
256 
257 /**
258  * Calculate MS-CHAPv2 challenge response
259  *
260  * @v username User name (or NULL to use empty string)
261  * @v password Password (or NULL to use empty string)
262  * @v challenge Authenticator challenge
263  * @v peer Peer challenge
264  * @v response Challenge response to fill in
265  *
266  * This is essentially the GenerateNTResponse() function as documented
267  * in RFC 2759 section 8.1.
268  */
269 void mschapv2_response ( const char *username, const char *password,
270  const struct mschapv2_challenge *challenge,
271  const struct mschapv2_challenge *peer,
272  struct mschapv2_response *response ) {
273  union mschapv2_context ctx;
274  union mschapv2_challenge_hash chash;
275  union mschapv2_password_hash phash;
276 
277  /* Zero reserved fields */
278  memset ( response, 0, sizeof ( *response ) );
279 
280  /* Copy peer challenge to response */
281  memcpy ( &response->peer, peer, sizeof ( response->peer ) );
282 
283  /* Construct challenge hash */
284  mschapv2_challenge_hash ( &ctx, challenge, peer, username, &chash );
285 
286  /* Construct expanded password hash */
287  mschapv2_password_hash ( &ctx, password, &phash );
288  mschapv2_expand_hash ( &ctx, &phash );
289 
290  /* Construct NT response */
291  mschapv2_challenge_response ( &ctx, &chash, &phash, &response->nt );
292  DBGC ( &ctx, "MSCHAPv2 challenge response:\n" );
293  DBGC_HDA ( &ctx, 0, response, sizeof ( *response ) );
294 }
295 
296 /**
297  * Calculate MS-CHAPv2 authenticator response
298  *
299  * @v username User name (or NULL to use empty string)
300  * @v password Password (or NULL to use empty string)
301  * @v challenge Authenticator challenge
302  * @v response Challenge response
303  * @v auth Authenticator response to fill in
304  *
305  * This is essentially the GenerateAuthenticatorResponse() function as
306  * documented in RFC 2759 section 8.7.
307  */
308 void mschapv2_auth ( const char *username, const char *password,
309  const struct mschapv2_challenge *challenge,
310  const struct mschapv2_response *response,
311  struct mschapv2_auth *auth ) {
312  struct digest_algorithm *sha1 = &sha1_algorithm;
313  union mschapv2_context ctx;
314  union mschapv2_challenge_hash chash;
315  union mschapv2_password_hash phash;
316  char tmp[3];
317  char *wtf;
318  unsigned int i;
319 
320  /* Construct hash of password hash */
321  mschapv2_password_hash ( &ctx, password, &phash );
322  mschapv2_hash_hash ( &ctx, &phash );
323 
324  /* Construct unnamed intermediate hash */
325  digest_init ( sha1, ctx.sha1 );
326  digest_update ( sha1, ctx.sha1, phash.md4, sizeof ( phash.md4 ) );
327  digest_update ( sha1, ctx.sha1, &response->nt,
328  sizeof ( response->nt ) );
329  digest_update ( sha1, ctx.sha1, mschapv2_magic1,
330  sizeof ( mschapv2_magic1 ) );
331  digest_final ( sha1, ctx.sha1, phash.sha1 );
332  DBGC ( &ctx, "MSCHAPv2 NT response:\n" );
333  DBGC_HDA ( &ctx, 0, &response->nt, sizeof ( response->nt ) );
334  DBGC ( &ctx, "MSCHAPv2 unnamed intermediate hash:\n" );
335  DBGC_HDA ( &ctx, 0, phash.sha1, sizeof ( phash.sha1 ) );
336 
337  /* Construct challenge hash */
338  mschapv2_challenge_hash ( &ctx, challenge, &response->peer,
339  username, &chash );
340 
341  /* Construct authenticator response hash */
342  digest_init ( sha1, ctx.sha1 );
343  digest_update ( sha1, ctx.sha1, phash.sha1, sizeof ( phash.sha1 ) );
344  digest_update ( sha1, ctx.sha1, chash.des, sizeof ( chash.des ) );
345  digest_update ( sha1, ctx.sha1, mschapv2_magic2,
346  sizeof ( mschapv2_magic2 ) );
347  digest_final ( sha1, ctx.sha1, phash.sha1 );
348  DBGC ( &ctx, "MSCHAPv2 authenticator response hash:\n" );
349  DBGC_HDA ( &ctx, 0, phash.sha1, sizeof ( phash.sha1 ) );
350 
351  /* Encode authenticator response hash */
352  wtf = auth->wtf;
353  *(wtf++) = 'S';
354  *(wtf++) = '=';
355  DBGC ( &ctx, "MSCHAPv2 authenticator response: S=" );
356  for ( i = 0 ; i < sizeof ( phash.sha1 ) ; i++ ) {
357  snprintf ( tmp, sizeof ( tmp ), "%02X", phash.sha1[i] );
358  *(wtf++) = tmp[0];
359  *(wtf++) = tmp[1];
360  DBGC ( &ctx, "%s", tmp );
361  }
362  DBGC ( &ctx, "\n" );
363 }
#define MD4_DIGEST_SIZE
MD4 digest size.
Definition: md4.h:72
MS-CHAPv2 context block.
Definition: mschapv2.c:50
uint32_t c
Definition: md4.c:30
static wchar_t wc
Definition: wchar.h:22
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
static const char mschapv2_magic1[39]
MS-CHAPv2 magic constant 1.
Definition: mschapv2.c:99
MS-CHAPv2 password hash.
Definition: mschapv2.c:87
static void mschapv2_hash_hash(union mschapv2_context *ctx, union mschapv2_password_hash *phash)
Hash the MS-CHAPv2 password hash.
Definition: mschapv2.c:183
MS-CHAPv2 challenge hash.
Definition: mschapv2.c:66
uint8_t des[3][DES_BLOCKSIZE]
DES keys.
Definition: mschapv2.c:93
static void const void void * dst
Definition: crypto.h:244
static void const void * src
Definition: crypto.h:244
#define DBGC(...)
Definition: compiler.h:505
uint8_t sha1[SHA1_DIGEST_SIZE]
SHA-1 digest.
Definition: mschapv2.c:91
void mschapv2_response(const char *username, const char *password, const struct mschapv2_challenge *challenge, const struct mschapv2_challenge *peer, struct mschapv2_response *response)
Calculate MS-CHAPv2 challenge response.
Definition: mschapv2.c:269
struct cipher_algorithm des_algorithm
Basic DES algorithm.
Definition: des.c:676
#define DES_CTX_SIZE
DES context size.
Definition: des.h:85
static void void * auth
Definition: crypto.h:264
unsigned long tmp
Definition: linux_pci.h:53
#define cipher_encrypt(cipher, ctx, src, dst, len)
Definition: crypto.h:248
An MS-CHAPv2 challenge.
Definition: mschapv2.h:15
void * memcpy(void *dest, const void *src, size_t len) __nonnull
uint8_t des[DES_CTX_SIZE]
DES cipher context.
Definition: mschapv2.c:56
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
uint8_t md4[MD4_DIGEST_SIZE]
MD4 digest.
Definition: mschapv2.c:89
char wtf[42]
Authenticator response string.
Definition: mschapv2.h:18
#define DBGC_HDA(...)
Definition: compiler.h:506
MS-CHAPv2 authentication.
uint8_t des[DES_BLOCKSIZE]
DES plaintext block.
Definition: mschapv2.c:70
uint8_t md4[MD4_CTX_SIZE]
MD4 digest context.
Definition: mschapv2.c:54
uint8_t sha1[SHA1_CTX_SIZE]
SHA-1 digest context.
Definition: mschapv2.c:52
void mschapv2_auth(const char *username, const char *password, const struct mschapv2_challenge *challenge, const struct mschapv2_response *response, struct mschapv2_auth *auth)
Calculate MS-CHAPv2 authenticator response.
Definition: mschapv2.c:308
struct golan_eq_context ctx
Definition: CIB_PRM.h:28
size_t strlen(const char *src)
Get length of string.
Definition: string.c:243
unsigned char uint8_t
Definition: stdint.h:10
An MS-CHAPv2 challenge response.
Definition: mschapv2.h:27
DES algorithm.
struct mschapv2_nt_response nt
NT response.
Definition: mschapv2.h:33
static void mschapv2_challenge_response(union mschapv2_context *ctx, const union mschapv2_challenge_hash *chash, const union mschapv2_password_hash *phash, struct mschapv2_nt_response *nt)
Calculate MS-CHAPv2 challenge response.
Definition: mschapv2.c:232
static void mschapv2_challenge_hash(union mschapv2_context *ctx, const struct mschapv2_challenge *challenge, const struct mschapv2_challenge *peer, const char *username, union mschapv2_challenge_hash *chash)
Calculate MS-CHAPv2 challenge hash.
Definition: mschapv2.c:119
#define MD4_CTX_SIZE
MD4 context size.
Definition: md4.h:66
An MS-CHAPv2 authenticator response.
Definition: mschapv2.h:39
#define SHA1_DIGEST_SIZE
Definition: Tpm20.h:25
SHA-1 algorithm.
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
#define SHA1_CTX_SIZE
SHA-1 context size.
Definition: sha1.h:66
uint8_t sha1[SHA1_DIGEST_SIZE]
SHA-1 digest.
Definition: mschapv2.c:68
static const char mschapv2_magic2[41]
MS-CHAPv2 magic constant 2.
Definition: mschapv2.c:103
#define DES_BLOCKSIZE
DES blocksize.
Definition: des.h:49
#define cpu_to_le16(value)
Definition: byteswap.h:106
A message digest algorithm.
Definition: crypto.h:17
A cipher algorithm.
Definition: crypto.h:49
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition: vsprintf.c:382
struct digest_algorithm md4_algorithm
MD4 algorithm.
Definition: md4.c:261
struct mschapv2_challenge peer
Peer challenge.
Definition: mschapv2.h:12
uint8_t expand[3 *DES_BLOCKSIZE]
DES key expansion.
Definition: mschapv2.c:95
uint8_t block[3][8]
DES-encrypted blocks.
Definition: mschapv2.h:23
struct mschapv2_challenge peer
Peer challenge.
Definition: mschapv2.h:29
MD4 algorithm.
String functions.
static void mschapv2_password_hash(union mschapv2_context *ctx, const char *password, union mschapv2_password_hash *phash)
Calculate MS-CHAPv2 password hash.
Definition: mschapv2.c:153
struct mschapv2_nt_response nt
NT response.
Definition: mschapv2.h:16
An MS-CHAPv2 NT response.
Definition: mschapv2.h:21
static void mschapv2_expand_hash(union mschapv2_context *ctx, union mschapv2_password_hash *phash)
Expand MS-CHAPv2 password hash by inserting DES dummy parity bits.
Definition: mschapv2.c:204
struct digest_algorithm sha1_algorithm
SHA-1 algorithm.
Definition: sha1.c:257
void * memset(void *dest, int character, size_t len) __nonnull