summaryrefslogtreecommitdiff
path: root/gpxe/src/crypto/sha1extra.c
blob: c144a0f69ea00f051f435fac7452a8f8bd3a0ccb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

FILE_LICENCE ( GPL2_OR_LATER );

#include <gpxe/crypto.h>
#include <gpxe/sha1.h>
#include <gpxe/hmac.h>
#include <stdint.h>
#include <byteswap.h>

/**
 * SHA1 pseudorandom function for creating derived keys
 *
 * @v key	Master key with which this call is associated
 * @v key_len	Length of key
 * @v label	NUL-terminated ASCII string describing purpose of PRF data
 * @v data	Further data that should be included in the PRF
 * @v data_len	Length of further PRF data
 * @v prf_len	Bytes of PRF to generate
 * @ret prf	Pseudorandom function bytes
 *
 * This is the PRF variant used by 802.11, defined in IEEE 802.11-2007
 * 8.5.5.1. EAP-FAST uses a different SHA1-based PRF, and TLS uses an
 * MD5-based PRF.
 */
void prf_sha1 ( const void *key, size_t key_len, const char *label,
		const void *data, size_t data_len, void *prf, size_t prf_len )
{
	u32 blk;
	u8 keym[key_len];	/* modifiable copy of key */
	u8 in[strlen ( label ) + 1 + data_len + 1]; /* message to HMAC */
	u8 *in_blknr;		/* pointer to last byte of in, block number */
	u8 out[SHA1_SIZE];	/* HMAC-SHA1 result */
	u8 sha1_ctx[SHA1_CTX_SIZE]; /* SHA1 context */
	const size_t label_len = strlen ( label );

	/* The HMAC-SHA-1 is calculated using the given key on the
	   message text `label', followed by a NUL, followed by one
	   byte indicating the block number (0 for first). */

	memcpy ( keym, key, key_len );

	memcpy ( in, label, strlen ( label ) + 1 );
	memcpy ( in + label_len + 1, data, data_len );
	in_blknr = in + label_len + 1 + data_len;

	for ( blk = 0 ;; blk++ ) {
		*in_blknr = blk;

		hmac_init ( &sha1_algorithm, sha1_ctx, keym, &key_len );
		hmac_update ( &sha1_algorithm, sha1_ctx, in, sizeof ( in ) );
		hmac_final ( &sha1_algorithm, sha1_ctx, keym, &key_len, out );

		if ( prf_len <= SHA1_SIZE ) {
			memcpy ( prf, out, prf_len );
			break;
		}

		memcpy ( prf, out, SHA1_SIZE );
		prf_len -= SHA1_SIZE;
		prf += SHA1_SIZE;
	}
}

/**
 * PBKDF2 key derivation function inner block operation
 *
 * @v passphrase	Passphrase from which to derive key
 * @v pass_len		Length of passphrase
 * @v salt		Salt to include in key
 * @v salt_len		Length of salt
 * @v iterations	Number of iterations of SHA1 to perform
 * @v blocknr		Index of this block, starting at 1
 * @ret block		SHA1_SIZE bytes of PBKDF2 data
 *
 * The operation of this function is described in RFC 2898.
 */
static void pbkdf2_sha1_f ( const void *passphrase, size_t pass_len,
			    const void *salt, size_t salt_len,
			    int iterations, u32 blocknr, u8 *block )
{
	u8 pass[pass_len];	/* modifiable passphrase */
	u8 in[salt_len + 4];	/* input buffer to first round */
	u8 last[SHA1_SIZE];	/* output of round N, input of N+1 */
	u8 sha1_ctx[SHA1_CTX_SIZE];
	u8 *next_in = in;	/* changed to `last' after first round */
	int next_size = sizeof ( in );
	int i, j;

	blocknr = htonl ( blocknr );

	memcpy ( pass, passphrase, pass_len );
	memcpy ( in, salt, salt_len );
	memcpy ( in + salt_len, &blocknr, 4 );
	memset ( block, 0, SHA1_SIZE );

	for ( i = 0; i < iterations; i++ ) {
		hmac_init ( &sha1_algorithm, sha1_ctx, pass, &pass_len );
		hmac_update ( &sha1_algorithm, sha1_ctx, next_in, next_size );
		hmac_final ( &sha1_algorithm, sha1_ctx, pass, &pass_len, last );

		for ( j = 0; j < SHA1_SIZE; j++ ) {
			block[j] ^= last[j];
		}

		next_in = last;
		next_size = SHA1_SIZE;
	}
}

/**
 * PBKDF2 key derivation function using SHA1
 *
 * @v passphrase	Passphrase from which to derive key
 * @v pass_len		Length of passphrase
 * @v salt		Salt to include in key
 * @v salt_len		Length of salt
 * @v iterations	Number of iterations of SHA1 to perform
 * @v key_len		Length of key to generate
 * @ret key		Generated key bytes
 *
 * This is used most notably in 802.11 WPA passphrase hashing, in
 * which case the salt is the SSID, 4096 iterations are used, and a
 * 32-byte key is generated that serves as the Pairwise Master Key for
 * EAPOL authentication.
 *
 * The operation of this function is further described in RFC 2898.
 */
void pbkdf2_sha1 ( const void *passphrase, size_t pass_len,
		   const void *salt, size_t salt_len,
		   int iterations, void *key, size_t key_len )
{
	u32 blocks = ( key_len + SHA1_SIZE - 1 ) / SHA1_SIZE;
	u32 blk;
	u8 buf[SHA1_SIZE];

	for ( blk = 1; blk <= blocks; blk++ ) {
		pbkdf2_sha1_f ( passphrase, pass_len, salt, salt_len,
				iterations, blk, buf );
		if ( key_len <= SHA1_SIZE ) {
			memcpy ( key, buf, key_len );
			break;
		}

		memcpy ( key, buf, SHA1_SIZE );
		key_len -= SHA1_SIZE;
		key += SHA1_SIZE;
	}
}