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