diff options
Diffstat (limited to 'contrib/wpa/src/crypto')
56 files changed, 11307 insertions, 708 deletions
diff --git a/contrib/wpa/src/crypto/aes-cbc.c b/contrib/wpa/src/crypto/aes-cbc.c index 2833cfcc..0835f2c 100644 --- a/contrib/wpa/src/crypto/aes-cbc.c +++ b/contrib/wpa/src/crypto/aes-cbc.c @@ -28,6 +28,9 @@ int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) u8 *pos = data; int i, j, blocks; + if (TEST_FAIL()) + return -1; + ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; @@ -61,6 +64,9 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) u8 *pos = data; int i, j, blocks; + if (TEST_FAIL()) + return -1; + ctx = aes_decrypt_init(key, 16); if (ctx == NULL) return -1; diff --git a/contrib/wpa/src/crypto/aes-ctr.c b/contrib/wpa/src/crypto/aes-ctr.c index d4d874d..e27f3bb 100644 --- a/contrib/wpa/src/crypto/aes-ctr.c +++ b/contrib/wpa/src/crypto/aes-ctr.c @@ -1,5 +1,5 @@ /* - * AES-128 CTR + * AES-128/192/256 CTR * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * @@ -14,15 +14,16 @@ #include "aes_wrap.h" /** - * aes_128_ctr_encrypt - AES-128 CTR mode encryption - * @key: Key for encryption (16 bytes) + * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption + * @key: Key for encryption (key_len bytes) + * @key_len: Length of the key (16, 24, or 32 bytes) * @nonce: Nonce for counter mode (16 bytes) * @data: Data to encrypt in-place * @data_len: Length of data in bytes * Returns: 0 on success, -1 on failure */ -int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len) +int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, + u8 *data, size_t data_len) { void *ctx; size_t j, len, left = data_len; @@ -30,7 +31,7 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *pos = data; u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; - ctx = aes_encrypt_init(key, 16); + ctx = aes_encrypt_init(key, key_len); if (ctx == NULL) return -1; os_memcpy(counter, nonce, AES_BLOCK_SIZE); @@ -53,3 +54,18 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, aes_encrypt_deinit(ctx); return 0; } + + +/** + * aes_128_ctr_encrypt - AES-128 CTR mode encryption + * @key: Key for encryption (key_len bytes) + * @nonce: Nonce for counter mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * Returns: 0 on success, -1 on failure + */ +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) +{ + return aes_ctr_encrypt(key, 16, nonce, data, data_len); +} diff --git a/contrib/wpa/src/crypto/aes-internal-dec.c b/contrib/wpa/src/crypto/aes-internal-dec.c index 720c703..7482295 100644 --- a/contrib/wpa/src/crypto/aes-internal-dec.c +++ b/contrib/wpa/src/crypto/aes-internal-dec.c @@ -147,10 +147,12 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] PUTU32(pt + 12, s3); } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { u32 *rk = ctx; rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); + return 0; } diff --git a/contrib/wpa/src/crypto/aes-internal-enc.c b/contrib/wpa/src/crypto/aes-internal-enc.c index f3c61b8..baeffca 100644 --- a/contrib/wpa/src/crypto/aes-internal-enc.c +++ b/contrib/wpa/src/crypto/aes-internal-enc.c @@ -99,6 +99,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; int res; + + if (TEST_FAIL()) + return NULL; + rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; @@ -112,10 +116,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { u32 *rk = ctx; rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); + return 0; } diff --git a/contrib/wpa/src/crypto/aes-omac1.c b/contrib/wpa/src/crypto/aes-omac1.c index 375db57..8642516 100644 --- a/contrib/wpa/src/crypto/aes-omac1.c +++ b/contrib/wpa/src/crypto/aes-omac1.c @@ -48,6 +48,9 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *pos, *end; size_t i, e, left, total_len; + if (TEST_FAIL()) + return -1; + ctx = aes_encrypt_init(key, key_len); if (ctx == NULL) return -1; diff --git a/contrib/wpa/src/crypto/aes-siv.c b/contrib/wpa/src/crypto/aes-siv.c index 5ac82c2..b682f3a 100644 --- a/contrib/wpa/src/crypto/aes-siv.c +++ b/contrib/wpa/src/crypto/aes-siv.c @@ -61,26 +61,33 @@ static void pad_block(u8 *pad, const u8 *addr, size_t len) } -static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], - size_t *len, u8 *mac) +static int aes_s2v(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) { u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; u8 *buf = NULL; int ret; size_t i; + const u8 *data[1]; + size_t data_len[1]; if (!num_elem) { os_memcpy(tmp, zero, sizeof(zero)); tmp[AES_BLOCK_SIZE - 1] = 1; - return omac1_aes_128(key, tmp, sizeof(tmp), mac); + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); } - ret = omac1_aes_128(key, zero, sizeof(zero), tmp); + data[0] = zero; + data_len[0] = sizeof(zero); + ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp); if (ret) return ret; for (i = 0; i < num_elem - 1; i++) { - ret = omac1_aes_128(key, addr[i], len[i], tmp2); + ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i], + tmp2); if (ret) return ret; @@ -88,13 +95,13 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], xor(tmp, tmp2); } if (len[i] >= AES_BLOCK_SIZE) { - buf = os_malloc(len[i]); + buf = os_memdup(addr[i], len[i]); if (!buf) return -ENOMEM; - os_memcpy(buf, addr[i], len[i]); xorend(buf, len[i], tmp, AES_BLOCK_SIZE); - ret = omac1_aes_128(key, buf, len[i], mac); + data[0] = buf; + ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac); bin_clear_free(buf, len[i]); return ret; } @@ -103,24 +110,32 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], pad_block(tmp2, addr[i], len[i]); xor(tmp, tmp2); - return omac1_aes_128(key, tmp, sizeof(tmp), mac); + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); } -int aes_siv_encrypt(const u8 *key, const u8 *pw, - size_t pwlen, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *out) +int aes_siv_encrypt(const u8 *key, size_t key_len, + const u8 *pw, size_t pwlen, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out) { const u8 *_addr[6]; size_t _len[6]; - const u8 *k1 = key, *k2 = key + 16; + const u8 *k1, *k2; u8 v[AES_BLOCK_SIZE]; size_t i; u8 *iv, *crypt_pw; - if (num_elem > ARRAY_SIZE(_addr) - 1) + if (num_elem > ARRAY_SIZE(_addr) - 1 || + (key_len != 32 && key_len != 48 && key_len != 64)) return -1; + key_len /= 2; + k1 = key; + k2 = key + key_len; + for (i = 0; i < num_elem; i++) { _addr[i] = addr[i]; _len[i] = len[i]; @@ -128,7 +143,7 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw, _addr[num_elem] = pw; _len[num_elem] = pwlen; - if (aes_s2v(k1, num_elem + 1, _addr, _len, v)) + if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v)) return -1; iv = out; @@ -140,26 +155,31 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw, /* zero out 63rd and 31st bits of ctr (from right) */ v[8] &= 0x7f; v[12] &= 0x7f; - return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen); + return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen); } -int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, +int aes_siv_decrypt(const u8 *key, size_t key_len, + const u8 *iv_crypt, size_t iv_c_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *out) { const u8 *_addr[6]; size_t _len[6]; - const u8 *k1 = key, *k2 = key + 16; + const u8 *k1, *k2; size_t crypt_len; size_t i; int ret; u8 iv[AES_BLOCK_SIZE]; u8 check[AES_BLOCK_SIZE]; - if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1) + if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 || + (key_len != 32 && key_len != 48 && key_len != 64)) return -1; crypt_len = iv_c_len - AES_BLOCK_SIZE; + key_len /= 2; + k1 = key; + k2 = key + key_len; for (i = 0; i < num_elem; i++) { _addr[i] = addr[i]; @@ -174,11 +194,11 @@ int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, iv[8] &= 0x7f; iv[12] &= 0x7f; - ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len); + ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len); if (ret) return ret; - ret = aes_s2v(k1, num_elem + 1, _addr, _len, check); + ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check); if (ret) return ret; if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0) diff --git a/contrib/wpa/src/crypto/aes.h b/contrib/wpa/src/crypto/aes.h index 2de59e0..8ab3de2 100644 --- a/contrib/wpa/src/crypto/aes.h +++ b/contrib/wpa/src/crypto/aes.h @@ -12,10 +12,10 @@ #define AES_BLOCK_SIZE 16 void * aes_encrypt_init(const u8 *key, size_t len); -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); void aes_encrypt_deinit(void *ctx); void * aes_decrypt_init(const u8 *key, size_t len); -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); void aes_decrypt_deinit(void *ctx); #endif /* AES_H */ diff --git a/contrib/wpa/src/crypto/aes_siv.h b/contrib/wpa/src/crypto/aes_siv.h index 463cf65..fb05d80 100644 --- a/contrib/wpa/src/crypto/aes_siv.h +++ b/contrib/wpa/src/crypto/aes_siv.h @@ -9,10 +9,12 @@ #ifndef AES_SIV_H #define AES_SIV_H -int aes_siv_encrypt(const u8 *key, const u8 *pw, - size_t pwlen, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *out); -int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, +int aes_siv_encrypt(const u8 *key, size_t key_len, + const u8 *pw, size_t pwlen, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out); +int aes_siv_decrypt(const u8 *key, size_t key_len, + const u8 *iv_crypt, size_t iv_c_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *out); diff --git a/contrib/wpa/src/crypto/aes_wrap.h b/contrib/wpa/src/crypto/aes_wrap.h index 4a14209..b70b1d2 100644 --- a/contrib/wpa/src/crypto/aes_wrap.h +++ b/contrib/wpa/src/crypto/aes_wrap.h @@ -3,7 +3,7 @@ * * - AES Key Wrap Algorithm (RFC3394) * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256 - * - AES-128 CTR mode encryption + * - AES-128/192/256 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC * - AES-GCM @@ -33,6 +33,8 @@ int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac); int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, + u8 *data, size_t data_len); int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *data, size_t data_len); int __must_check aes_128_eax_encrypt(const u8 *key, diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h index 534c4bd..12109ce 100644 --- a/contrib/wpa/src/crypto/crypto.h +++ b/contrib/wpa/src/crypto/crypto.h @@ -1,6 +1,6 @@ /* * Wrapper functions for crypto libraries - * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -80,12 +80,35 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** + * sha384_vector - SHA384 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * sha512_vector - SHA512 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** * des_encrypt - Encrypt one block with DES * @clear: 8 octets (in) * @key: 7 octets (in) (no parity bits included) * @cypher: 8 octets (out) + * Returns: 0 on success, -1 on failure */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); /** * aes_encrypt_init - Initialize AES for encryption @@ -100,8 +123,9 @@ void * aes_encrypt_init(const u8 *key, size_t len); * @ctx: Context pointer from aes_encrypt_init() * @plain: Plaintext data to be encrypted (16 bytes) * @crypt: Buffer for the encrypted data (16 bytes) + * Returns: 0 on success, -1 on failure */ -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); /** * aes_encrypt_deinit - Deinitialize AES encryption @@ -122,8 +146,9 @@ void * aes_decrypt_init(const u8 *key, size_t len); * @ctx: Context pointer from aes_encrypt_init() * @crypt: Encrypted data (16 bytes) * @plain: Buffer for the decrypted data (16 bytes) + * Returns: 0 on success, -1 on failure */ -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); /** * aes_decrypt_deinit - Deinitialize AES decryption @@ -135,7 +160,8 @@ void aes_decrypt_deinit(void *ctx); enum crypto_hash_alg { CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, - CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256, + CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512 }; struct crypto_hash; @@ -391,6 +417,14 @@ int __must_check crypto_public_key_decrypt_pkcs1( struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len); +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey); +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len); + /** * crypto_global_init - Initialize crypto wrapper * @@ -503,6 +537,14 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, u8 *buf, size_t buflen, size_t padlen); /** + * crypto_bignum_rand - Create a random number in range of modulus + * @r: Bignum; set to a random value + * @m: Bignum; modulus + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m); + +/** * crypto_bignum_add - c = a + b * @a: Bignum * @b: Bignum @@ -584,6 +626,16 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, struct crypto_bignum *d); /** + * crypto_bignum_rshift - r = a >> n + * @a: Bignum + * @n: Number of bits + * @r: Bignum; used to store the result of a >> n + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r); + +/** * crypto_bignum_cmp - Compare two bignums * @a: Bignum * @b: Bignum @@ -614,6 +666,13 @@ int crypto_bignum_is_zero(const struct crypto_bignum *a); int crypto_bignum_is_one(const struct crypto_bignum *a); /** + * crypto_bignum_is_odd - Is the given bignum odd + * @a: Bignum + * Returns: 1 if @a is odd or 0 if not + */ +int crypto_bignum_is_odd(const struct crypto_bignum *a); + +/** * crypto_bignum_legendre - Compute the Legendre symbol (a/p) * @a: Bignum * @p: Bignum @@ -659,6 +718,13 @@ size_t crypto_ec_prime_len(struct crypto_ec *e); size_t crypto_ec_prime_len_bits(struct crypto_ec *e); /** + * crypto_ec_order_len - Get length of the order in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the order defining the group + */ +size_t crypto_ec_order_len(struct crypto_ec *e); + +/** * crypto_ec_get_prime - Get prime defining an EC group * @e: EC context from crypto_ec_init() * Returns: Prime (bignum) defining the group @@ -695,6 +761,16 @@ struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); /** + * crypto_ec_point_x - Copies the x-ordinate point into big number + * @e: EC context from crypto_ec_init() + * @p: EC point data + * @x: Big number to set to the copy of x-ordinate + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x); + +/** * crypto_ec_point_to_bin - Write EC point value as binary data * @e: EC context from crypto_ec_init() * @p: EC point data from crypto_ec_point_init() @@ -723,7 +799,7 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, const u8 *val); /** - * crypto_bignum_add - c = a + b + * crypto_ec_point_add - c = a + b * @e: EC context from crypto_ec_init() * @a: Bignum * @b: Bignum @@ -735,7 +811,7 @@ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, struct crypto_ec_point *c); /** - * crypto_bignum_mul - res = b * p + * crypto_ec_point_mul - res = b * p * @e: EC context from crypto_ec_init() * @p: EC point * @b: Bignum @@ -806,4 +882,12 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b); +struct crypto_ecdh; + +struct crypto_ecdh * crypto_ecdh_init(int group); +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y); +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len); +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh); + #endif /* CRYPTO_H */ diff --git a/contrib/wpa/src/crypto/crypto_gnutls.c b/contrib/wpa/src/crypto/crypto_gnutls.c index 0dfd54d..4ef1146 100644 --- a/contrib/wpa/src/crypto/crypto_gnutls.c +++ b/contrib/wpa/src/crypto/crypto_gnutls.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for libgcrypt - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,27 +10,42 @@ #include <gcrypt.h> #include "common.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "sha512.h" #include "crypto.h" -int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +static int gnutls_digest_vector(int algo, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; - if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR) + if (TEST_FAIL()) + return -1; + + if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR) return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_MD4); + p = gcry_md_read(hd, algo); if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4)); + memcpy(mac, p, gcry_md_get_algo_dlen(algo)); gcry_md_close(hd); return 0; } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac); +} + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { gcry_cipher_hd_t hd; u8 pkey[8], next, tmp; @@ -49,49 +64,161 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) gcry_err_code(gcry_cipher_setkey(hd, pkey, 8)); gcry_cipher_encrypt(hd, cypher, 8, clear, 8); gcry_cipher_close(hd); + return 0; } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - gcry_md_hd_t hd; - unsigned char *p; - size_t i; - - if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) - return -1; - for (i = 0; i < num_elem; i++) - gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_MD5); - if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5)); - gcry_md_close(hd); - return 0; + return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac); } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac); +} + + +static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) +{ gcry_md_hd_t hd; unsigned char *p; size_t i; - if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR) + if (TEST_FAIL()) + return -1; + + if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR) + return -1; + if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) { + gcry_md_close(hd); return -1; + } for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_SHA1); + p = gcry_md_read(hd, algo); if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1)); + memcpy(mac, p, gcry_md_get_algo_dlen(algo)); gcry_md_close(hd); return 0; } +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + void * aes_encrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; + if (TEST_FAIL()) + return NULL; + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) { printf("cipher open failed\n"); @@ -107,10 +234,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { gcry_cipher_hd_t hd = ctx; gcry_cipher_encrypt(hd, crypt, 16, plain, 16); + return 0; } @@ -125,6 +253,9 @@ void * aes_decrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; + if (TEST_FAIL()) + return NULL; + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) return NULL; @@ -137,10 +268,11 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { gcry_cipher_hd_t hd = ctx; gcry_cipher_decrypt(hd, plain, 16, crypt, 16); + return 0; } @@ -151,6 +283,81 @@ void aes_decrypt_deinit(void *ctx) } +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + gcry_mpi_t pub = NULL; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_cmp_ui(pub, 1) <= 0) + goto fail; + + if (order) { + gcry_mpi_t p = NULL, q = NULL, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + tmp = gcry_mpi_new(prime_len * 8); + failed = !tmp || + gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len, + NULL) != GPG_ERR_NO_ERROR || + gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len, + NULL) != GPG_ERR_NO_ERROR; + if (!failed) { + gcry_mpi_powm(tmp, pub, q, p); + failed = gcry_mpi_cmp_ui(tmp, 1) != 0; + } + gcry_mpi_release(p); + gcry_mpi_release(q); + gcry_mpi_release(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + gcry_mpi_release(pub); + return res; +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/contrib/wpa/src/crypto/crypto_internal-modexp.c b/contrib/wpa/src/crypto/crypto_internal-modexp.c index 9dcabb9..6819f1a 100644 --- a/contrib/wpa/src/crypto/crypto_internal-modexp.c +++ b/contrib/wpa/src/crypto/crypto_internal-modexp.c @@ -13,6 +13,79 @@ #include "crypto.h" +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + struct bignum *pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + pub = bignum_init(); + if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 || + bignum_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + struct bignum *p, *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + p = bignum_init(); + q = bignum_init(); + tmp = bignum_init(); + failed = !p || !q || !tmp || + bignum_set_unsigned_bin(p, prime, prime_len) < 0 || + bignum_set_unsigned_bin(q, order, order_len) < 0 || + bignum_exptmod(pub, q, p, tmp) < 0 || + bignum_cmp_d(tmp, 1) != 0; + bignum_deinit(p); + bignum_deinit(q); + bignum_deinit(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + bignum_deinit(pub); + return res; +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/contrib/wpa/src/crypto/crypto_internal.c b/contrib/wpa/src/crypto/crypto_internal.c index f3602da..aad40af 100644 --- a/contrib/wpa/src/crypto/crypto_internal.c +++ b/contrib/wpa/src/crypto/crypto_internal.c @@ -11,6 +11,8 @@ #include "common.h" #include "crypto.h" #include "sha256_i.h" +#include "sha384_i.h" +#include "sha512_i.h" #include "sha1_i.h" #include "md5_i.h" @@ -22,6 +24,12 @@ struct crypto_hash { #ifdef CONFIG_SHA256 struct sha256_state sha256; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + struct sha384_state sha384; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + struct sha512_state sha512; +#endif /* CONFIG_INTERNAL_SHA512 */ } u; u8 key[64]; size_t key_len; @@ -54,6 +62,16 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, sha256_init(&ctx->u.sha256); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + sha384_init(&ctx->u.sha384); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + sha512_init(&ctx->u.sha512); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (key_len > sizeof(k_pad)) { MD5Init(&ctx->u.md5); @@ -142,6 +160,16 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) sha256_process(&ctx->u.sha256, data, len); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + sha384_process(&ctx->u.sha384, data, len); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + sha512_process(&ctx->u.sha512, data, len); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ default: break; } @@ -191,6 +219,28 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) sha256_done(&ctx->u.sha256, mac); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + if (*len < 48) { + *len = 48; + os_free(ctx); + return -1; + } + *len = 48; + sha384_done(&ctx->u.sha384, mac); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + if (*len < 64) { + *len = 64; + os_free(ctx); + return -1; + } + *len = 64; + sha512_done(&ctx->u.sha512, mac); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; @@ -260,6 +310,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return 0; } diff --git a/contrib/wpa/src/crypto/crypto_libtomcrypt.c b/contrib/wpa/src/crypto/crypto_libtomcrypt.c index a55edd1..ed30efa 100644 --- a/contrib/wpa/src/crypto/crypto_libtomcrypt.c +++ b/contrib/wpa/src/crypto/crypto_libtomcrypt.c @@ -35,7 +35,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -53,6 +53,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) des_setup(pkey, 8, 0, &skey); des_ecb_encrypt(clear, cypher, &skey); des_done(&skey); + return 0; } @@ -96,10 +97,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { symmetric_key *skey = ctx; - aes_ecb_encrypt(plain, crypt, skey); + return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1; } @@ -125,10 +126,10 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { symmetric_key *skey = ctx; - aes_ecb_encrypt(plain, (u8 *) crypt, skey); + return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1; } @@ -277,6 +278,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return ret; } @@ -297,7 +301,7 @@ struct crypto_cipher { struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) -{ +{ struct crypto_cipher *ctx; int idx, res, rc4 = 0; @@ -693,6 +697,44 @@ void crypto_global_deinit(void) #ifdef CONFIG_MODEXP +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + /* TODO: check pubkey */ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/contrib/wpa/src/crypto/crypto_linux.c b/contrib/wpa/src/crypto/crypto_linux.c new file mode 100644 index 0000000..1724456 --- /dev/null +++ b/contrib/wpa/src/crypto/crypto_linux.c @@ -0,0 +1,1009 @@ +/* + * Crypto wrapper for Linux kernel AF_ALG + * Copyright (c) 2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <linux/if_alg.h> + +#include "common.h" +#include "crypto.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "aes.h" + + +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif /* SOL_ALG */ + + +static int linux_af_alg_socket(const char *type, const char *name) +{ + struct sockaddr_alg sa; + int s; + + if (TEST_FAIL()) + return -1; + + s = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s", + __func__, strerror(errno)); + return -1; + } + + os_memset(&sa, 0, sizeof(sa)); + sa.salg_family = AF_ALG; + os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type)); + os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type)); + if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + wpa_printf(MSG_ERROR, + "%s: Failed to bind AF_ALG socket(%s,%s): %s", + __func__, type, name, strerror(errno)); + close(s); + return -1; + } + + return s; +} + + +static int linux_af_alg_hash_vector(const char *alg, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, + u8 *mac, size_t mac_len) +{ + int s, t; + size_t i; + ssize_t res; + int ret = -1; + + s = linux_af_alg_socket("hash", alg); + if (s < 0) + return -1; + + if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + close(s); + return -1; + } + + t = accept(s, NULL, NULL); + if (t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + close(s); + return -1; + } + + for (i = 0; i < num_elem; i++) { + res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + if ((size_t) res < len[i]) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket did not accept full buffer (%d/%d)", + __func__, (int) res, (int) len[i]); + goto fail; + } + } + + res = recv(t, mac, mac_len, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + if ((size_t) res < mac_len) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket did not return full buffer (%d/%d)", + __func__, (int) res, (int) mac_len); + goto fail; + } + + ret = 0; +fail: + close(t); + close(s); + + return ret; +} + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len, + mac, 16); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len, + mac, MD5_MAC_LEN); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len, + mac, SHA1_MAC_LEN); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len, + mac, SHA256_MAC_LEN); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len, + mac, SHA384_MAC_LEN); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len, + mac, 64); +} + + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem, + addr, len, mac, 16); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem, + addr, len, mac, SHA1_MAC_LEN); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem, + addr, len, mac, SHA256_MAC_LEN); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem, + addr, len, mac, SHA384_MAC_LEN); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + + +struct crypto_hash { + int s; + int t; + size_t mac_len; + int failed; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + const char *name; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + name = "md5"; + ctx->mac_len = MD5_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA1: + name = "sha1"; + ctx->mac_len = SHA1_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + name = "hmac(md5)"; + ctx->mac_len = MD5_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + name = "hmac(sha1)"; + ctx->mac_len = SHA1_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA256: + name = "sha256"; + ctx->mac_len = SHA256_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_SHA256: + name = "hmac(sha256)"; + ctx->mac_len = SHA256_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA384: + name = "sha384"; + ctx->mac_len = SHA384_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA512: + name = "sha512"; + ctx->mac_len = 64; + break; + default: + os_free(ctx); + return NULL; + } + + ctx->s = linux_af_alg_socket("hash", name); + if (ctx->s < 0) { + os_free(ctx); + return NULL; + } + + if (key && key_len && + setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + close(ctx->s); + os_free(ctx); + return NULL; + } + + ctx->t = accept(ctx->s, NULL, NULL); + if (ctx->t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + close(ctx->s); + os_free(ctx); + return NULL; + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + ssize_t res; + + if (!ctx) + return; + + res = send(ctx->t, data, len, MSG_MORE); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket failed: %s", + __func__, strerror(errno)); + ctx->failed = 1; + return; + } + if ((size_t) res < len) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket did not accept full buffer (%d/%d)", + __func__, (int) res, (int) len); + ctx->failed = 1; + return; + } +} + + +static void crypto_hash_deinit(struct crypto_hash *ctx) +{ + close(ctx->s); + close(ctx->t); + os_free(ctx); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + ssize_t res; + + if (!ctx) + return -2; + + if (!mac || !len) { + crypto_hash_deinit(ctx); + return 0; + } + + if (ctx->failed) { + crypto_hash_deinit(ctx); + return -2; + } + + if (*len < ctx->mac_len) { + crypto_hash_deinit(ctx); + *len = ctx->mac_len; + return -1; + } + *len = ctx->mac_len; + + res = recv(ctx->t, mac, ctx->mac_len, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket failed: %s", + __func__, strerror(errno)); + crypto_hash_deinit(ctx); + return -2; + } + if ((size_t) res < ctx->mac_len) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket did not return full buffer (%d/%d)", + __func__, (int) res, (int) ctx->mac_len); + crypto_hash_deinit(ctx); + return -2; + } + + crypto_hash_deinit(ctx); + + if (TEST_FAIL()) + return -1; + return 0; +} + + +struct linux_af_alg_skcipher { + int s; + int t; +}; + + +static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher) +{ + if (!skcipher) + return; + if (skcipher->s >= 0) + close(skcipher->s); + if (skcipher->t >= 0) + close(skcipher->t); + os_free(skcipher); +} + + +static struct linux_af_alg_skcipher * +linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len) +{ + struct linux_af_alg_skcipher *skcipher; + + skcipher = os_zalloc(sizeof(*skcipher)); + if (!skcipher) + goto fail; + skcipher->t = -1; + + skcipher->s = linux_af_alg_socket("skcipher", alg); + if (skcipher->s < 0) + goto fail; + + if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + goto fail; + } + + skcipher->t = accept(skcipher->s, NULL, NULL); + if (skcipher->t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + + return skcipher; +fail: + linux_af_alg_skcipher_deinit(skcipher); + return NULL; +} + + +static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher, + int enc, const u8 *in, u8 *out) +{ + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + io[0].iov_base = (void *) in; + io[0].iov_len = AES_BLOCK_SIZE; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(skcipher->t, out, AES_BLOCK_SIZE); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + return -1; + } + if (ret < AES_BLOCK_SIZE) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/%d)", + __func__, (int) ret, AES_BLOCK_SIZE); + return -1; + } + + return 0; +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + return linux_af_alg_skcipher("ecb(aes)", key, len); +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct linux_af_alg_skcipher *skcipher = ctx; + + return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + linux_af_alg_skcipher_deinit(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + return linux_af_alg_skcipher("ecb(aes)", key, len); +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct linux_af_alg_skcipher *skcipher = ctx; + + return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + linux_af_alg_skcipher_deinit(ctx); +} + + +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + struct linux_af_alg_skcipher *skcipher; + u8 *skip_buf; + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[2]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + skip_buf = os_zalloc(skip + 1); + if (!skip_buf) + return -1; + skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen); + if (!skcipher) { + os_free(skip_buf); + return -1; + } + + io[0].iov_base = skip_buf; + io[0].iov_len = skip; + io[1].iov_base = data; + io[1].iov_len = data_len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_ENCRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + os_free(skip_buf); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + os_free(skip_buf); + + msg.msg_control = NULL; + msg.msg_controllen = 0; + ret = recvmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + linux_af_alg_skcipher_deinit(skcipher); + + if ((size_t) ret < skip + data_len) { + wpa_printf(MSG_ERROR, + "%s: recvmsg did not return full data (%d/%d)", + __func__, (int) ret, (int) (skip + data_len)); + return -1; + } + + return 0; +} + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + struct linux_af_alg_skcipher *skcipher; + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + int res = -1; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey)); + if (!skcipher) + goto fail; + + io[0].iov_base = (void *) clear; + io[0].iov_len = 8; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_ENCRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + goto fail; + } + + ret = read(skcipher->t, cypher, 8); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + goto fail; + } + if (ret < 8) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/8)", + __func__, (int) ret); + goto fail; + } + + res = 0; +fail: + linux_af_alg_skcipher_deinit(skcipher); + return res; +} + + +static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv, + u8 *data, size_t data_len) +{ + struct linux_af_alg_skcipher *skcipher; + char buf[100]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + struct af_alg_iv *alg_iv; + size_t iv_len = AES_BLOCK_SIZE; + + skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16); + if (!skcipher) + return -1; + + io[0].iov_base = (void *) data; + io[0].iov_len = data_len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)) + + CMSG_SPACE(sizeof(*alg_iv) + iv_len); + msg.msg_iov = io; + msg.msg_iovlen = 1; + + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + + hdr = CMSG_NXTHDR(&msg, hdr); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, iv, iv_len); + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + ret = recvmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + if ((size_t) ret < data_len) { + wpa_printf(MSG_ERROR, + "%s: recvmsg not return full data (%d/%d)", + __func__, (int) ret, (int) data_len); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + linux_af_alg_skcipher_deinit(skcipher); + return 0; +} + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + return aes_128_cbc_oper(key, 1, iv, data, data_len); +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + return aes_128_cbc_oper(key, 0, iv, data, data_len); +} + + +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem, + addr, len, mac, AES_BLOCK_SIZE); +} + + +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} + + +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + struct linux_af_alg_skcipher *skcipher; + char buf[100]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + struct af_alg_iv *alg_iv; + size_t iv_len = 8; + + skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len); + if (!skcipher) + return -1; + + io[0].iov_base = (void *) (cipher + iv_len); + io[0].iov_len = n * 8; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)) + + CMSG_SPACE(sizeof(*alg_iv) + iv_len); + msg.msg_iov = io; + msg.msg_iovlen = 1; + + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_DECRYPT; + + hdr = CMSG_NXTHDR(&msg, hdr); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, cipher, iv_len); + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(skcipher->t, plain, n * 8); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + if (ret < n * 8) { + wpa_printf(MSG_ERROR, + "%s: read not return full data (%d/%d)", + __func__, (int) ret, n * 8); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + linux_af_alg_skcipher_deinit(skcipher); + return 0; +} + + +struct crypto_cipher { + struct linux_af_alg_skcipher *skcipher; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + const char *name; + struct af_alg_iv *alg_iv; + size_t iv_len = 0; + char buf[100]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + name = "ecb(arc4)"; + break; + case CRYPTO_CIPHER_ALG_AES: + name = "cbc(aes)"; + iv_len = AES_BLOCK_SIZE; + break; + case CRYPTO_CIPHER_ALG_3DES: + name = "cbc(des3_ede)"; + iv_len = 8; + break; + case CRYPTO_CIPHER_ALG_DES: + name = "cbc(des)"; + iv_len = 8; + break; + default: + os_free(ctx); + return NULL; + } + + ctx->skcipher = linux_af_alg_skcipher(name, key, key_len); + if (!ctx->skcipher) { + os_free(ctx); + return NULL; + } + + if (iv && iv_len) { + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, iv, iv_len); + + ret = sendmsg(ctx->skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(ctx->skcipher); + os_free(ctx); + return NULL; + } + } + + return ctx; +} + + +static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in, + u8 *out, size_t len) +{ + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + io[0].iov_base = (void *) in; + io[0].iov_len = len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = type; + + ret = sendmsg(ctx->skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(ctx->skcipher->t, out, len); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + return -1; + } + if (ret < (ssize_t) len) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/%d)", + __func__, (int) ret, (int) len); + return -1; + } + + return 0; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len); +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len); +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + if (ctx) { + linux_af_alg_skcipher_deinit(ctx->skcipher); + os_free(ctx); + } +} + + +int crypto_global_init(void) +{ + return 0; +} + + +void crypto_global_deinit(void) +{ +} diff --git a/contrib/wpa/src/crypto/crypto_module_tests.c b/contrib/wpa/src/crypto/crypto_module_tests.c index 581005d..1cc73d8 100644 --- a/contrib/wpa/src/crypto/crypto_module_tests.c +++ b/contrib/wpa/src/crypto/crypto_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" #include "crypto/aes.h" @@ -16,6 +17,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" static int test_siv(void) @@ -91,7 +93,7 @@ static int test_siv(void) addr[0] = ad; len[0] = sizeof(ad); - if (aes_siv_encrypt(key, plaintext, sizeof(plaintext), + if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext), 1, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); return 1; @@ -102,7 +104,8 @@ static int test_siv(void) return 1; } - if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) { + if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c), + 1, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); return 1; } @@ -120,7 +123,8 @@ static int test_siv(void) addr[2] = nonce_2; len[2] = sizeof(nonce_2); - if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2), + if (aes_siv_encrypt(key_2, sizeof(key_2), + plaintext_2, sizeof(plaintext_2), 3, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); return 1; @@ -131,7 +135,8 @@ static int test_siv(void) return 1; } - if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) { + if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2), + 3, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); return 1; } @@ -1266,7 +1271,7 @@ static int test_sha1(void) } -const struct { +static const struct { char *data; u8 hash[32]; } tests[] = { @@ -1290,14 +1295,15 @@ const struct { } }; -const struct hmac_test { - u8 key[80]; +static const struct hmac_test { + u8 key[150]; size_t key_len; - u8 data[128]; + u8 data[160]; size_t data_len; - u8 hash[32]; + u8 hash[32]; /* HMAC-SHA-256 */ + u8 hash384[48]; /* HMAC-SHA-384 */ } hmac_tests[] = { - /* draft-ietf-ipsec-ciph-sha-256-01.txt */ + /* draft-ietf-ipsec-ciph-sha-256-01.txt; RFC 4231 */ { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, @@ -1312,7 +1318,8 @@ const struct hmac_test { 0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a, 0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66, 0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81 - } + }, + { } }, { { @@ -1329,7 +1336,8 @@ const struct hmac_test { 0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae, 0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49, 0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30 - } + }, + { } }, { { @@ -1347,7 +1355,8 @@ const struct hmac_test { 0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab, 0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5, 0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3 - } + }, + { } }, { { @@ -1364,9 +1373,34 @@ const struct hmac_test { 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5, 0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c, 0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7 + }, + { } + }, + { /* RFC 4231 - Test Case 1 */ + { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b + }, + 20, + "Hi There", + 8, + { + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, + 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, + 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, + 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7 + }, + { + 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, + 0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f, + 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6, + 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, + 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f, + 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6 } }, - { + { /* RFC 4231 - Test Case 2 */ "Jefe", 4, "what do ya want for nothing?", @@ -1376,6 +1410,14 @@ const struct hmac_test { 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 + }, + { + 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, + 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b, + 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47, + 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, + 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7, + 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49 } }, { @@ -1401,6 +1443,39 @@ const struct hmac_test { 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62, 0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc, 0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0 + }, + { } + }, + { /* RFC 4231 - Test Case 3 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa + }, + 20, + { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd + }, + 50, + { + 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, + 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, + 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, + 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe + }, + { + 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, + 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f, + 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb, + 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, + 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9, + 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27 } }, { @@ -1427,6 +1502,40 @@ const struct hmac_test { 0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55, 0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85, 0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17 + }, + { } + }, + { /* RFC 4231 - Test Case 4 */ + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, + }, + 25, + { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd + }, + 50, + { + 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, + 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, + 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, + 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b + }, + { + 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, + 0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7, + 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c, + 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, + 0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79, + 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb } }, { @@ -1444,7 +1553,8 @@ const struct hmac_test { 0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17, 0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27, 0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42 - } + }, + { } }, { { @@ -1467,6 +1577,45 @@ const struct hmac_test { 0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb, 0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e, 0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f + }, + { } + }, + { /* RFC 4231 - Test Case 6 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa + }, + 131, + "Test Using Larger Than Block-Size Key - Hash Key First", + 54, + { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, + 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, + 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, + 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54 + }, + { + 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, + 0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4, + 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f, + 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, + 0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82, + 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52 } }, { @@ -1491,6 +1640,45 @@ const struct hmac_test { 0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8, 0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc, 0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6 + }, + { } + }, + { /* RFC 4231 - Test Case 7 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa + }, + 131, + "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", + 152, + { + 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, + 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, + 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, + 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2 + }, + { + 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, + 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c, + 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, + 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, + 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d, + 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e } } }; @@ -1503,6 +1691,7 @@ static int test_sha256(void) const u8 *addr[2]; size_t len[2]; int errors = 0; + u8 *key; for (i = 0; i < ARRAY_SIZE(tests); i++) { wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1); @@ -1573,12 +1762,285 @@ static int test_sha256(void) hash, sizeof(hash)); /* TODO: add proper test case for this */ + key = os_malloc(8161); + if (key) { +#ifdef CONFIG_HMAC_SHA256_KDF + int res; + + res = hmac_sha256_kdf((u8 *) "secret", 6, "label", + (u8 *) "seed", 4, key, 8160); + if (res) { + wpa_printf(MSG_INFO, + "Unexpected hmac_sha256_kdf(outlen=8160) failure"); + errors++; + } + + res = hmac_sha256_kdf((u8 *) "secret", 6, "label", + (u8 *) "seed", 4, key, 8161); + if (res == 0) { + wpa_printf(MSG_INFO, + "Unexpected hmac_sha256_kdf(outlen=8161) success"); + errors++; + } +#endif /* CONFIG_HMAC_SHA256_KDF */ + + os_free(key); + } + if (!errors) wpa_printf(MSG_INFO, "SHA256 test cases passed"); return errors; } +static int test_sha384(void) +{ +#ifdef CONFIG_SHA384 + unsigned int i; + u8 hash[48]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + const char *data = "hello"; + const u8 hash_res[] = { + 0x59, 0xe1, 0x74, 0x87, 0x77, 0x44, 0x8c, 0x69, + 0xde, 0x6b, 0x80, 0x0d, 0x7a, 0x33, 0xbb, 0xfb, + 0x9f, 0xf1, 0xb4, 0x63, 0xe4, 0x43, 0x54, 0xc3, + 0x55, 0x3b, 0xcd, 0xb9, 0xc6, 0x66, 0xfa, 0x90, + 0x12, 0x5a, 0x3c, 0x79, 0xf9, 0x03, 0x97, 0xbd, + 0xf5, 0xf6, 0xa1, 0x3d, 0xe8, 0x28, 0x68, 0x4f + }; + + addr[0] = (const u8 *) data; + len[0] = 5; + if (sha384_vector(1, addr, len, hash) < 0 || + os_memcmp(hash, hash_res, 48) != 0) { + wpa_printf(MSG_INFO, "SHA384 test case 1: FAIL"); + errors++; + } else { + wpa_printf(MSG_INFO, "SHA384 test case 1: OK"); + } + + addr[0] = (const u8 *) data; + len[0] = 4; + addr[1] = (const u8 *) data + 4; + len[1] = 1; + if (sha384_vector(2, addr, len, hash) < 0 || + os_memcmp(hash, hash_res, 48) != 0) { + wpa_printf(MSG_INFO, "SHA384 test case 2: FAIL"); + errors++; + } else { + wpa_printf(MSG_INFO, "SHA384 test case 2: OK"); + } + + for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { + const struct hmac_test *t = &hmac_tests[i]; + + if (t->hash384[0] == 0 && t->hash384[1] == 0 && + t->hash384[2] == 0 && t->hash384[3] == 0) + continue; + wpa_printf(MSG_INFO, "HMAC-SHA384 test case %d:", i + 1); + + if (hmac_sha384(t->key, t->key_len, t->data, t->data_len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + addr[0] = t->data; + len[0] = t->data_len; + if (hmac_sha384_vector(t->key, t->key_len, 1, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + if (len[0]) { + addr[0] = t->data; + len[0] = 1; + addr[1] = t->data + 1; + len[1] = t->data_len - 1; + if (hmac_sha384_vector(t->key, t->key_len, 2, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + } + } + + if (!errors) + wpa_printf(MSG_INFO, "SHA384 test cases passed"); + return errors; +#else /* CONFIG_SHA384 */ + return 0; +#endif /* CONFIG_SHA384 */ +} + + +static int test_fips186_2_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + wpa_printf(MSG_INFO, + "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)"); + if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 || + os_memcmp(w, buf, sizeof(w)) != 0) { + wpa_printf(MSG_INFO, "fips186_2_prf failed"); + return 1; + } + + return 0; +} + + +static int test_extract_expand_hkdf(void) +{ + u8 prk[SHA256_MAC_LEN]; + u8 okm[82]; + + /* RFC 5869, A.1 */ + u8 ikm1[22] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + u8 salt1[13] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c + }; + u8 info1[10] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 + }; + u8 prk1[32] = { + 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5 + }; + u8 okm1[42] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 + }; + + /* RFC 5869, A.2 */ + u8 ikm2[80] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }; + u8 salt2[80] = { + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf + }; + u8 info2[80] = { + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + u8 prk2[32] = { + 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, + 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, + 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01, + 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44 + }; + u8 okm2[82] = { + 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, + 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, + 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, + 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, + 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, + 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, + 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, + 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, + 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, + 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, + 0x1d, 0x87 + }; + + wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)"); + + wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1"); + if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0) + return -1; + if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK"); + return -1; + } + if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1), + okm, sizeof(okm1)) < 0) + return -1; + if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) { + wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM"); + return -1; + } + + wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2"); + if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0) + return -1; + if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK"); + return -1; + } + if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2), + okm, sizeof(okm2)) < 0) + return -1; + if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) { + wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM"); + return -1; + } + + wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed"); + + return 0; +} + + static int test_ms_funcs(void) { #ifndef CONFIG_FIPS @@ -1695,6 +2157,9 @@ int crypto_module_tests(void) test_md5() || test_sha1() || test_sha256() || + test_sha384() || + test_fips186_2_prf() || + test_extract_expand_hkdf() || test_ms_funcs()) ret = -1; diff --git a/contrib/wpa/src/crypto/crypto_nettle.c b/contrib/wpa/src/crypto/crypto_nettle.c new file mode 100644 index 0000000..f85d365 --- /dev/null +++ b/contrib/wpa/src/crypto/crypto_nettle.c @@ -0,0 +1,469 @@ +/* + * Wrapper functions for libnettle and libgmp + * Copyright (c) 2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <nettle/nettle-meta.h> +#include <nettle/des.h> +#undef des_encrypt +#include <nettle/hmac.h> +#include <nettle/aes.h> +#undef aes_encrypt +#undef aes_decrypt +#include <nettle/arcfour.h> +#include <nettle/bignum.h> + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "sha512.h" +#include "crypto.h" + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + struct des_ctx ctx; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + nettle_des_set_key(&ctx, pkey); + nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + void *ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + ctx = os_malloc(alg->context_size); + if (!ctx) + return -1; + alg->init(ctx); + for (i = 0; i < num_elem; i++) + alg->update(ctx, len[i], addr[i]); + alg->digest(ctx, alg->digest_size, mac); + bin_clear_free(ctx, alg->context_size); + return 0; +} + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac); +} + + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_md5_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_md5_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_md5_update(&ctx, len[i], addr[i]); + hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha1_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha1_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha1_update(&ctx, len[i], addr[i]); + hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha256_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha256_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha256_update(&ctx, len[i], addr[i]); + hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha384_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha384_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha384_update(&ctx, len[i], addr[i]); + hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha512_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha512_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha512_update(&ctx, len[i], addr[i]); + hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + struct aes_ctx *ctx; + + if (TEST_FAIL()) + return NULL; + ctx = os_malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + nettle_aes_set_encrypt_key(ctx, len, key); + + return ctx; +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct aes_ctx *actx = ctx; + nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain); + return 0; +} + + +void aes_encrypt_deinit(void *ctx) +{ + struct aes_ctx *actx = ctx; + bin_clear_free(actx, sizeof(*actx)); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + struct aes_ctx *ctx; + + if (TEST_FAIL()) + return NULL; + ctx = os_malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + nettle_aes_set_decrypt_key(ctx, len, key); + + return ctx; +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct aes_ctx *actx = ctx; + nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt); + return 0; +} + + +void aes_decrypt_deinit(void *ctx) +{ + struct aes_ctx *actx = ctx; + bin_clear_free(actx, sizeof(*actx)); +} + + +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + mpz_t pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + mpz_init(pub); + mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey); + if (mpz_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + mpz_t p, q, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + mpz_inits(p, q, tmp, NULL); + mpz_import(p, prime_len, 1, 1, 1, 0, prime); + mpz_import(q, order_len, 1, 1, 1, 0, order); + mpz_powm(tmp, pub, q, p); + failed = mpz_cmp_d(tmp, 1) != 0; + mpz_clears(p, q, tmp, NULL); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + mpz_clear(pub); + return res; +} + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + mpz_t bn_base, bn_exp, bn_modulus, bn_result; + int ret = -1; + size_t len; + + mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL); + mpz_import(bn_base, base_len, 1, 1, 1, 0, base); + mpz_import(bn_exp, power_len, 1, 1, 1, 0, power); + mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus); + + mpz_powm(bn_result, bn_base, bn_exp, bn_modulus); + len = mpz_sizeinbase(bn_result, 2); + len = (len + 7) / 8; + if (*result_len < len) + goto error; + mpz_export(result, result_len, 1, 1, 1, 0, bn_result); + ret = 0; + +error: + mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL); + return ret; +} + + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct arcfour_ctx arcfour_ctx; + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key); + break; + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain); + break; + default: + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt); + break; + default: + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + bin_clear_free(ctx, sizeof(*ctx)); +} diff --git a/contrib/wpa/src/crypto/crypto_none.c b/contrib/wpa/src/crypto/crypto_none.c index 011f3f3..5479194 100644 --- a/contrib/wpa/src/crypto/crypto_none.c +++ b/contrib/wpa/src/crypto/crypto_none.c @@ -18,6 +18,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { + return 0; } diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c index 6cff75c..1b0c1ec 100644 --- a/contrib/wpa/src/crypto/crypto_openssl.c +++ b/contrib/wpa/src/crypto/crypto_openssl.c @@ -1,6 +1,6 @@ /* * Wrapper functions for OpenSSL libcrypto - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -24,16 +24,72 @@ #endif /* CONFIG_ECC */ #include "common.h" +#include "utils/const_time.h" #include "wpabuf.h" #include "dh_group5.h" #include "sha1.h" #include "sha256.h" #include "sha384.h" +#include "sha512.h" +#include "md5.h" +#include "aes_wrap.h" #include "crypto.h" +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) +/* Compatibility wrappers for older versions. */ + +static HMAC_CTX * HMAC_CTX_new(void) +{ + HMAC_CTX *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx) + HMAC_CTX_init(ctx); + return ctx; +} + + +static void HMAC_CTX_free(HMAC_CTX *ctx) +{ + if (!ctx) + return; + HMAC_CTX_cleanup(ctx); + bin_clear_free(ctx, sizeof(*ctx)); +} + + +static EVP_MD_CTX * EVP_MD_CTX_new(void) +{ + EVP_MD_CTX *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx) + EVP_MD_CTX_init(ctx); + return ctx; +} + + +static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + if (!ctx) + return; + EVP_MD_CTX_cleanup(ctx); + bin_clear_free(ctx, sizeof(*ctx)); +} + +#endif /* OpenSSL version < 1.1.0 */ + static BIGNUM * get_group5_prime(void) { -#ifdef OPENSSL_IS_BORINGSSL +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) + return BN_get_rfc3526_prime_1536(NULL); +#elif !defined(OPENSSL_IS_BORINGSSL) + return get_rfc3526_prime_1536(NULL); +#else static const unsigned char RFC3526_PRIME_1536[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, @@ -53,41 +109,76 @@ static BIGNUM * get_group5_prime(void) 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL); -#else /* OPENSSL_IS_BORINGSSL */ - return get_rfc3526_prime_1536(NULL); -#endif /* OPENSSL_IS_BORINGSSL */ +#endif +} + + +static BIGNUM * get_group5_order(void) +{ + static const unsigned char RFC3526_ORDER_1536[] = { + 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51, + 0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68, + 0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53, + 0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E, + 0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36, + 0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22, + 0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74, + 0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6, + 0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08, + 0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E, + 0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B, + 0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF, + 0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB, + 0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36, + 0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04, + 0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL); } + #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif +#ifdef OPENSSL_NO_SHA512 +#define NO_SHA384_WRAPPER +#endif static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - EVP_MD_CTX ctx; + EVP_MD_CTX *ctx; size_t i; unsigned int mac_len; - EVP_MD_CTX_init(&ctx); - if (!EVP_DigestInit_ex(&ctx, type, NULL)) { + if (TEST_FAIL()) + return -1; + + ctx = EVP_MD_CTX_new(); + if (!ctx) + return -1; + if (!EVP_DigestInit_ex(ctx, type, NULL)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } for (i = 0; i < num_elem; i++) { - if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) { + if (!EVP_DigestUpdate(ctx, addr[i], len[i])) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate " "failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } } - if (!EVP_DigestFinal(&ctx, mac, &mac_len)) { + if (!EVP_DigestFinal(ctx, mac, &mac_len)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } + EVP_MD_CTX_free(ctx); return 0; } @@ -101,7 +192,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) #endif /* CONFIG_FIPS */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -119,6 +210,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) DES_set_key((DES_cblock *) &pkey, &ks); DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); + return 0; } @@ -129,32 +221,34 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, #ifdef OPENSSL_NO_RC4 return -1; #else /* OPENSSL_NO_RC4 */ - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int outl; int res = -1; unsigned char skip_buf[16]; - EVP_CIPHER_CTX_init(&ctx); - if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) || - !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) || - !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) || - !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1)) + ctx = EVP_CIPHER_CTX_new(); + if (!ctx || + !EVP_CIPHER_CTX_set_padding(ctx, 0) || + !EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, NULL, NULL, 1) || + !EVP_CIPHER_CTX_set_key_length(ctx, keylen) || + !EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, 1)) goto out; while (skip >= sizeof(skip_buf)) { size_t len = skip; if (len > sizeof(skip_buf)) len = sizeof(skip_buf); - if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len)) + if (!EVP_CipherUpdate(ctx, skip_buf, &outl, skip_buf, len)) goto out; skip -= len; } - if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len)) + if (EVP_CipherUpdate(ctx, data, &outl, data, data_len)) res = 0; out: - EVP_CIPHER_CTX_cleanup(&ctx); + if (ctx) + EVP_CIPHER_CTX_free(ctx); return res; #endif /* OPENSSL_NO_RC4 */ } @@ -184,15 +278,31 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, #endif /* NO_SHA256_WRAPPER */ +#ifndef NO_SHA384_WRAPPER +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac); +} +#endif /* NO_SHA384_WRAPPER */ + + +#ifndef NO_SHA512_WRAPPER +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac); +} +#endif /* NO_SHA512_WRAPPER */ + + static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) { switch (keylen) { case 16: return EVP_aes_128_ecb(); -#ifndef OPENSSL_IS_BORINGSSL case 24: return EVP_aes_192_ecb(); -#endif /* OPENSSL_IS_BORINGSSL */ case 32: return EVP_aes_256_ecb(); } @@ -206,14 +316,19 @@ void * aes_encrypt_init(const u8 *key, size_t len) EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; + if (TEST_FAIL()) + return NULL; + type = aes_get_evp_cipher(len); - if (type == NULL) + if (!type) { + wpa_printf(MSG_INFO, "%s: Unsupported len=%u", + __func__, (unsigned int) len); return NULL; + } - ctx = os_malloc(sizeof(*ctx)); + ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return NULL; - EVP_CIPHER_CTX_init(ctx); if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { os_free(ctx); return NULL; @@ -223,14 +338,16 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { EVP_CIPHER_CTX *c = ctx; int clen = 16; if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; } + return 0; } @@ -247,8 +364,7 @@ void aes_encrypt_deinit(void *ctx) wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES encrypt", len); } - EVP_CIPHER_CTX_cleanup(c); - bin_clear_free(c, sizeof(*c)); + EVP_CIPHER_CTX_free(c); } @@ -257,16 +373,21 @@ void * aes_decrypt_init(const u8 *key, size_t len) EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; + if (TEST_FAIL()) + return NULL; + type = aes_get_evp_cipher(len); - if (type == NULL) + if (!type) { + wpa_printf(MSG_INFO, "%s: Unsupported len=%u", + __func__, (unsigned int) len); return NULL; + } - ctx = os_malloc(sizeof(*ctx)); + ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return NULL; - EVP_CIPHER_CTX_init(ctx); if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { - os_free(ctx); + EVP_CIPHER_CTX_free(ctx); return NULL; } EVP_CIPHER_CTX_set_padding(ctx, 0); @@ -274,14 +395,16 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { EVP_CIPHER_CTX *c = ctx; int plen = 16; if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; } + return 0; } @@ -298,8 +421,7 @@ void aes_decrypt_deinit(void *ctx) wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES decrypt", len); } - EVP_CIPHER_CTX_cleanup(c); - bin_clear_free(c, sizeof(*c)); + EVP_CIPHER_CTX_free(c); } @@ -311,6 +433,8 @@ int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) AES_KEY actx; int res; + if (TEST_FAIL()) + return -1; if (AES_set_encrypt_key(kek, kek_len << 3, &actx)) return -1; res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8); @@ -325,6 +449,8 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, AES_KEY actx; int res; + if (TEST_FAIL()) + return -1; if (AES_set_decrypt_key(kek, kek_len << 3, &actx)) return -1; res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8); @@ -338,54 +464,128 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int clen, len; u8 buf[16]; + int res = -1; - EVP_CIPHER_CTX_init(&ctx); - if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + if (TEST_FAIL()) return -1; - EVP_CIPHER_CTX_set_padding(&ctx, 0); - clen = data_len; - if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 || - clen != (int) data_len) + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) return -1; - + clen = data_len; len = sizeof(buf); - if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) - return -1; - EVP_CIPHER_CTX_cleanup(&ctx); + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 && + EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 && + EVP_EncryptUpdate(ctx, data, &clen, data, data_len) == 1 && + clen == (int) data_len && + EVP_EncryptFinal_ex(ctx, buf, &len) == 1 && len == 0) + res = 0; + EVP_CIPHER_CTX_free(ctx); - return 0; + return res; } int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int plen, len; u8 buf[16]; + int res = -1; - EVP_CIPHER_CTX_init(&ctx); - if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + if (TEST_FAIL()) return -1; - EVP_CIPHER_CTX_set_padding(&ctx, 0); + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + return -1; plen = data_len; - if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 || - plen != (int) data_len) + len = sizeof(buf); + if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 && + EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 && + EVP_DecryptUpdate(ctx, data, &plen, data, data_len) == 1 && + plen == (int) data_len && + EVP_DecryptFinal_ex(ctx, buf, &len) == 1 && len == 0) + res = 0; + EVP_CIPHER_CTX_free(ctx); + + return res; + +} + + +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } - len = sizeof(buf); - if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) return -1; - EVP_CIPHER_CTX_cleanup(&ctx); + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } return 0; } +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + BIGNUM *pub, *p; + int res = -1; + + pub = BN_bin2bn(pubkey, pubkey_len, NULL); + p = BN_bin2bn(prime, prime_len, NULL); + if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) || + BN_cmp(pub, p) >= 0) + goto fail; + + if (order) { + BN_CTX *ctx; + BIGNUM *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + q = BN_bin2bn(order, order_len, NULL); + ctx = BN_CTX_new(); + tmp = BN_new(); + failed = !q || !ctx || !tmp || + !BN_mod_exp(tmp, pub, q, p, ctx) || + !BN_is_one(tmp); + BN_clear(q); + BN_clear(tmp); + BN_CTX_free(ctx); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + BN_clear(pub); + BN_clear(p); + return res; +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, @@ -408,7 +608,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len, bn_result == NULL) goto error; - if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) + if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus, + ctx, NULL) != 1) goto error; *result_len = BN_bn2bin(bn_result, result); @@ -425,8 +626,8 @@ error: struct crypto_cipher { - EVP_CIPHER_CTX enc; - EVP_CIPHER_CTX dec; + EVP_CIPHER_CTX *enc; + EVP_CIPHER_CTX *dec; }; @@ -487,23 +688,25 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, return NULL; } - EVP_CIPHER_CTX_init(&ctx->enc); - EVP_CIPHER_CTX_set_padding(&ctx->enc, 0); - if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) || - !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); + if (!(ctx->enc = EVP_CIPHER_CTX_new()) || + !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) || + !EVP_EncryptInit_ex(ctx->enc, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(ctx->enc, key_len) || + !EVP_EncryptInit_ex(ctx->enc, NULL, NULL, key, iv)) { + if (ctx->enc) + EVP_CIPHER_CTX_free(ctx->enc); os_free(ctx); return NULL; } - EVP_CIPHER_CTX_init(&ctx->dec); - EVP_CIPHER_CTX_set_padding(&ctx->dec, 0); - if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) || - !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); - EVP_CIPHER_CTX_cleanup(&ctx->dec); + if (!(ctx->dec = EVP_CIPHER_CTX_new()) || + !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) || + !EVP_DecryptInit_ex(ctx->dec, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(ctx->dec, key_len) || + !EVP_DecryptInit_ex(ctx->dec, NULL, NULL, key, iv)) { + EVP_CIPHER_CTX_free(ctx->enc); + if (ctx->dec) + EVP_CIPHER_CTX_free(ctx->dec); os_free(ctx); return NULL; } @@ -516,7 +719,7 @@ int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { int outl; - if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len)) + if (!EVP_EncryptUpdate(ctx->enc, crypt, &outl, plain, len)) return -1; return 0; } @@ -527,7 +730,7 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, { int outl; outl = len; - if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len)) + if (!EVP_DecryptUpdate(ctx->dec, plain, &outl, crypt, len)) return -1; return 0; } @@ -535,19 +738,23 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, void crypto_cipher_deinit(struct crypto_cipher *ctx) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); - EVP_CIPHER_CTX_cleanup(&ctx->dec); + EVP_CIPHER_CTX_free(ctx->enc); + EVP_CIPHER_CTX_free(ctx->dec); os_free(ctx); } void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; *priv = NULL; + wpabuf_free(*publ); *publ = NULL; dh = DH_new(); @@ -562,6 +769,10 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) if (dh->p == NULL) goto err; + dh->q = get_group5_order(); + if (!dh->q) + goto err; + if (DH_generate_key(dh) != 1) goto err; @@ -586,11 +797,68 @@ err: wpabuf_clear_free(privkey); DH_free(dh); return NULL; +#else + DH *dh; + struct wpabuf *pubkey = NULL, *privkey = NULL; + size_t publen, privlen; + BIGNUM *p, *g, *q; + const BIGNUM *priv_key = NULL, *pub_key = NULL; + + *priv = NULL; + wpabuf_free(*publ); + *publ = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + g = BN_new(); + p = get_group5_prime(); + q = get_group5_order(); + if (!g || BN_set_word(g, 2) != 1 || !p || !q || + DH_set0_pqg(dh, p, q, g) != 1) + goto err; + p = NULL; + q = NULL; + g = NULL; + + if (DH_generate_key(dh) != 1) + goto err; + + DH_get0_key(dh, &pub_key, &priv_key); + publen = BN_num_bytes(pub_key); + pubkey = wpabuf_alloc(publen); + if (!pubkey) + goto err; + privlen = BN_num_bytes(priv_key); + privkey = wpabuf_alloc(privlen); + if (!privkey) + goto err; + + BN_bn2bin(pub_key, wpabuf_put(pubkey, publen)); + BN_bn2bin(priv_key, wpabuf_put(privkey, privlen)); + + *priv = privkey; + *publ = pubkey; + return dh; + +err: + BN_free(p); + BN_free(q); + BN_free(g); + wpabuf_clear_free(pubkey); + wpabuf_clear_free(privkey); + DH_free(dh); + return NULL; +#endif } void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) DH *dh; dh = DH_new(); @@ -621,6 +889,42 @@ void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) err: DH_free(dh); return NULL; +#else + DH *dh; + BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + g = BN_new(); + p = get_group5_prime(); + if (!g || BN_set_word(g, 2) != 1 || !p || + DH_set0_pqg(dh, p, NULL, g) != 1) + goto err; + p = NULL; + g = NULL; + + priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); + pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); + if (!priv_key || !pub_key || DH_set0_key(dh, pub_key, priv_key) != 1) + goto err; + pub_key = NULL; + priv_key = NULL; + + if (DH_generate_key(dh) != 1) + goto err; + + return dh; + +err: + BN_free(p); + BN_free(g); + BN_free(pub_key); + BN_clear_free(priv_key); + DH_free(dh); + return NULL; +#endif } @@ -672,7 +976,7 @@ void dh5_free(void *ctx) struct crypto_hash { - HMAC_CTX ctx; + HMAC_CTX *ctx; }; @@ -707,16 +1011,17 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; - HMAC_CTX_init(&ctx->ctx); + ctx->ctx = HMAC_CTX_new(); + if (!ctx->ctx) { + os_free(ctx); + return NULL; + } -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); -#else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) { + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); return NULL; } -#endif /* openssl < 0.9.9 */ return ctx; } @@ -726,7 +1031,7 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL) return; - HMAC_Update(&ctx->ctx, data, len); + HMAC_Update(ctx->ctx, data, len); } @@ -739,20 +1044,19 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) return -2; if (mac == NULL || len == NULL) { + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); return 0; } mdlen = *len; -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Final(&ctx->ctx, mac, &mdlen); - res = 1; -#else /* openssl < 0.9.9 */ - res = HMAC_Final(&ctx->ctx, mac, &mdlen); -#endif /* openssl < 0.9.9 */ - HMAC_CTX_cleanup(&ctx->ctx); + res = HMAC_Final(ctx->ctx, mac, &mdlen); + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; + if (res == 1) { *len = mdlen; return 0; @@ -767,28 +1071,26 @@ static int openssl_hmac_vector(const EVP_MD *type, const u8 *key, const u8 *addr[], const size_t *len, u8 *mac, unsigned int mdlen) { - HMAC_CTX ctx; + HMAC_CTX *ctx; size_t i; int res; - HMAC_CTX_init(&ctx); -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx, key, key_len, type, NULL); -#else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1) + if (TEST_FAIL()) + return -1; + + ctx = HMAC_CTX_new(); + if (!ctx) return -1; -#endif /* openssl < 0.9.9 */ + res = HMAC_Init_ex(ctx, key, key_len, type, NULL); + if (res != 1) + goto done; for (i = 0; i < num_elem; i++) - HMAC_Update(&ctx, addr[i], len[i]); + HMAC_Update(ctx, addr[i], len[i]); -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Final(&ctx, mac, &mdlen); - res = 1; -#else /* openssl < 0.9.9 */ - res = HMAC_Final(&ctx, mac, &mdlen); -#endif /* openssl < 0.9.9 */ - HMAC_CTX_cleanup(&ctx); + res = HMAC_Final(ctx, mac, &mdlen); +done: + HMAC_CTX_free(ctx); return res == 1 ? 0 : -1; } @@ -863,7 +1165,7 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr, - len, mac, 32); + len, mac, 48); } @@ -876,6 +1178,25 @@ int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, #endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr, + len, mac, 64); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + int crypto_get_random(void *buf, size_t len) { if (RAND_bytes(buf, len) != 1) @@ -892,6 +1213,9 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, int ret = -1; size_t outlen, i; + if (TEST_FAIL()) + return -1; + ctx = CMAC_CTX_new(); if (ctx == NULL) return -1; @@ -941,13 +1265,20 @@ int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) struct crypto_bignum * crypto_bignum_init(void) { + if (TEST_FAIL()) + return NULL; return (struct crypto_bignum *) BN_new(); } struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) { - BIGNUM *bn = BN_bin2bn(buf, len, NULL); + BIGNUM *bn; + + if (TEST_FAIL()) + return NULL; + + bn = BN_bin2bn(buf, len, NULL); return (struct crypto_bignum *) bn; } @@ -966,6 +1297,9 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, { int num_bytes, offset; + if (TEST_FAIL()) + return -1; + if (padlen > buflen) return -1; @@ -984,6 +1318,14 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, } +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) +{ + if (TEST_FAIL()) + return -1; + return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1; +} + + int crypto_bignum_add(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) @@ -1019,11 +1361,15 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a, int res; BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; - res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, - (const BIGNUM *) c, bnctx); + res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a, + (const BIGNUM *) b, (const BIGNUM *) c, + bnctx, NULL); BN_CTX_free(bnctx); return res ? 0 : -1; @@ -1037,9 +1383,16 @@ int crypto_bignum_inverse(const struct crypto_bignum *a, BIGNUM *res; BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifdef OPENSSL_IS_BORINGSSL + /* TODO: use BN_mod_inverse_blinded() ? */ +#else /* OPENSSL_IS_BORINGSSL */ + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1052,6 +1405,8 @@ int crypto_bignum_sub(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { + if (TEST_FAIL()) + return -1; return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? 0 : -1; } @@ -1065,9 +1420,15 @@ int crypto_bignum_div(const struct crypto_bignum *a, BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifndef OPENSSL_IS_BORINGSSL + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1085,6 +1446,9 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1096,6 +1460,15 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, } +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r) +{ + /* Note: BN_rshift() does not modify the first argument even though it + * has not been marked const. */ + return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1; +} + + int crypto_bignum_cmp(const struct crypto_bignum *a, const struct crypto_bignum *b) { @@ -1121,12 +1494,22 @@ int crypto_bignum_is_one(const struct crypto_bignum *a) } +int crypto_bignum_is_odd(const struct crypto_bignum *a) +{ + return BN_is_odd((const BIGNUM *) a); +} + + int crypto_bignum_legendre(const struct crypto_bignum *a, const struct crypto_bignum *p) { BN_CTX *bnctx; BIGNUM *exp = NULL, *tmp = NULL; int res = -2; + unsigned int mask; + + if (TEST_FAIL()) + return -2; bnctx = BN_CTX_new(); if (bnctx == NULL) @@ -1138,16 +1521,17 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, /* exp = (p-1) / 2 */ !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || !BN_rshift1(exp, exp) || - !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, - bnctx)) + !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp, + (const BIGNUM *) p, bnctx, NULL)) goto fail; - if (BN_is_word(tmp, 1)) - res = 1; - else if (BN_is_zero(tmp)) - res = 0; - else - res = -1; + /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use + * constant time selection to avoid branches here. */ + res = -1; + mask = const_time_eq(BN_is_word(tmp, 1), 1); + res = const_time_select_int(mask, 1, res); + mask = const_time_eq(BN_is_zero(tmp), 1); + res = const_time_select_int(mask, 0, res); fail: BN_clear_free(tmp); @@ -1161,6 +1545,7 @@ fail: struct crypto_ec { EC_GROUP *group; + int nid; BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; @@ -1218,6 +1603,7 @@ struct crypto_ec * crypto_ec_init(int group) if (e == NULL) return NULL; + e->nid = nid; e->bnctx = BN_CTX_new(); e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); @@ -1252,6 +1638,8 @@ void crypto_ec_deinit(struct crypto_ec *e) struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { + if (TEST_FAIL()) + return NULL; if (e == NULL) return NULL; return (struct crypto_ec_point *) EC_POINT_new(e->group); @@ -1270,6 +1658,12 @@ size_t crypto_ec_prime_len_bits(struct crypto_ec *e) } +size_t crypto_ec_order_len(struct crypto_ec *e) +{ + return BN_num_bytes(e->order); +} + + const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) { return (const struct crypto_bignum *) e->prime; @@ -1291,6 +1685,16 @@ void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) } +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x) +{ + return EC_POINT_get_affine_coordinates_GFp(e->group, + (const EC_POINT *) p, + (BIGNUM *) x, NULL, + e->bnctx) == 1 ? 0 : -1; +} + + int crypto_ec_point_to_bin(struct crypto_ec *e, const struct crypto_ec_point *point, u8 *x, u8 *y) { @@ -1298,6 +1702,9 @@ int crypto_ec_point_to_bin(struct crypto_ec *e, int ret = -1; int len = BN_num_bytes(e->prime); + if (TEST_FAIL()) + return -1; + x_bn = BN_new(); y_bn = BN_new(); @@ -1328,6 +1735,9 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, EC_POINT *elem; int len = BN_num_bytes(e->prime); + if (TEST_FAIL()) + return NULL; + x = BN_bin2bn(val, len, NULL); y = BN_bin2bn(val + len, len, NULL); elem = EC_POINT_new(e->group); @@ -1355,6 +1765,8 @@ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b, struct crypto_ec_point *c) { + if (TEST_FAIL()) + return -1; return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a, (const EC_POINT *) b, e->bnctx) ? 0 : -1; } @@ -1364,6 +1776,8 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, const struct crypto_bignum *b, struct crypto_ec_point *res) { + if (TEST_FAIL()) + return -1; return EC_POINT_mul(e->group, (EC_POINT *) res, NULL, (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx) ? 0 : -1; @@ -1372,6 +1786,8 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) { + if (TEST_FAIL()) + return -1; return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1; } @@ -1380,6 +1796,8 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, struct crypto_ec_point *p, const struct crypto_bignum *x, int y_bit) { + if (TEST_FAIL()) + return -1; if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p, (const BIGNUM *) x, y_bit, e->bnctx) || @@ -1395,6 +1813,9 @@ crypto_ec_point_compute_y_sqr(struct crypto_ec *e, { BIGNUM *tmp, *tmp2, *y_sqr = NULL; + if (TEST_FAIL()) + return NULL; + tmp = BN_new(); tmp2 = BN_new(); @@ -1439,4 +1860,228 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, (const EC_POINT *) b, e->bnctx); } + +struct crypto_ecdh { + struct crypto_ec *ec; + EVP_PKEY *pkey; +}; + +struct crypto_ecdh * crypto_ecdh_init(int group) +{ + struct crypto_ecdh *ecdh; + EVP_PKEY *params = NULL; + EC_KEY *ec_params; + EVP_PKEY_CTX *kctx = NULL; + + ecdh = os_zalloc(sizeof(*ecdh)); + if (!ecdh) + goto fail; + + ecdh->ec = crypto_ec_init(group); + if (!ecdh->ec) + goto fail; + + ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid); + if (!ec_params) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to generate EC_KEY parameters"); + goto fail; + } + EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); + params = EVP_PKEY_new(); + if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to generate EVP_PKEY parameters"); + goto fail; + } + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (!kctx) + goto fail; + + if (EVP_PKEY_keygen_init(kctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_keygen_init failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + + return ecdh; +fail: + crypto_ecdh_deinit(ecdh); + ecdh = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) +{ + struct wpabuf *buf = NULL; + EC_KEY *eckey; + const EC_POINT *pubkey; + BIGNUM *x, *y = NULL; + int len = BN_num_bytes(ecdh->ec->prime); + int res; + + eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey); + if (!eckey) + return NULL; + + pubkey = EC_KEY_get0_public_key(eckey); + if (!pubkey) + return NULL; + + x = BN_new(); + if (inc_y) { + y = BN_new(); + if (!y) + goto fail; + } + buf = wpabuf_alloc(inc_y ? 2 * len : len); + if (!x || !buf) + goto fail; + + if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey, + x, y, ecdh->ec->bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + res = crypto_bignum_to_bin((struct crypto_bignum *) x, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + + if (inc_y) { + res = crypto_bignum_to_bin((struct crypto_bignum *) y, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + } + +done: + BN_clear_free(x); + BN_clear_free(y); + EC_KEY_free(eckey); + + return buf; +fail: + wpabuf_free(buf); + buf = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len) +{ + BIGNUM *x, *y = NULL; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *peerkey = NULL; + struct wpabuf *secret = NULL; + size_t secret_len; + EC_POINT *pub; + EC_KEY *eckey = NULL; + + x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL); + pub = EC_POINT_new(ecdh->ec->group); + if (!x || !pub) + goto fail; + + if (inc_y) { + y = BN_bin2bn(key + len / 2, len / 2, NULL); + if (!y) + goto fail; + if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub, + x, y, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + } else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group, + pub, x, 0, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: ECDH peer public key is not on curve"); + goto fail; + } + + eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid); + if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_KEY_set_public_key failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + peerkey = EVP_PKEY_new(); + if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1) + goto fail; + + ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL); + if (!ctx || EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(1) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + secret = wpabuf_alloc(secret_len); + if (!secret) + goto fail; + if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len), + &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(2) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(pub); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(peerkey); + return secret; +fail: + wpabuf_free(secret); + secret = NULL; + goto done; +} + + +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) +{ + if (ecdh) { + crypto_ec_deinit(ecdh->ec); + EVP_PKEY_free(ecdh->pkey); + os_free(ecdh); + } +} + #endif /* CONFIG_ECC */ diff --git a/contrib/wpa/src/crypto/crypto_wolfssl.c b/contrib/wpa/src/crypto/crypto_wolfssl.c new file mode 100644 index 0000000..976a008 --- /dev/null +++ b/contrib/wpa/src/crypto/crypto_wolfssl.c @@ -0,0 +1,1786 @@ +/* + * Wrapper functions for libwolfssl + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" + +/* wolfSSL headers */ +#include <wolfssl/options.h> +#include <wolfssl/wolfcrypt/md4.h> +#include <wolfssl/wolfcrypt/md5.h> +#include <wolfssl/wolfcrypt/sha.h> +#include <wolfssl/wolfcrypt/sha256.h> +#include <wolfssl/wolfcrypt/sha512.h> +#include <wolfssl/wolfcrypt/hmac.h> +#include <wolfssl/wolfcrypt/pwdbased.h> +#include <wolfssl/wolfcrypt/arc4.h> +#include <wolfssl/wolfcrypt/des3.h> +#include <wolfssl/wolfcrypt/aes.h> +#include <wolfssl/wolfcrypt/dh.h> +#include <wolfssl/wolfcrypt/cmac.h> +#include <wolfssl/wolfcrypt/ecc.h> +#include <wolfssl/openssl/bn.h> + + +#ifndef CONFIG_FIPS + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + Md4 md4; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitMd4(&md4); + + for (i = 0; i < num_elem; i++) + wc_Md4Update(&md4, addr[i], len[i]); + + wc_Md4Final(&md4, mac); + + return 0; +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + wc_Md5 md5; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitMd5(&md5); + + for (i = 0; i < num_elem; i++) + wc_Md5Update(&md5, addr[i], len[i]); + + wc_Md5Final(&md5, mac); + + return 0; +} + +#endif /* CONFIG_FIPS */ + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + wc_Sha sha; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha(&sha); + + for (i = 0; i < num_elem; i++) + wc_ShaUpdate(&sha, addr[i], len[i]); + + wc_ShaFinal(&sha, mac); + + return 0; +} + + +#ifndef NO_SHA256_WRAPPER +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha256 sha256; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha256(&sha256); + + for (i = 0; i < num_elem; i++) + wc_Sha256Update(&sha256, addr[i], len[i]); + + wc_Sha256Final(&sha256, mac); + + return 0; +} +#endif /* NO_SHA256_WRAPPER */ + + +#ifdef CONFIG_SHA384 +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha384 sha384; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha384(&sha384); + + for (i = 0; i < num_elem; i++) + wc_Sha384Update(&sha384, addr[i], len[i]); + + wc_Sha384Final(&sha384, mac); + + return 0; +} +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha512 sha512; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha512(&sha512); + + for (i = 0; i < num_elem; i++) + wc_Sha512Update(&sha512, addr[i], len[i]); + + wc_Sha512Final(&sha512, mac); + + return 0; +} +#endif /* CONFIG_SHA512 */ + + +static int wolfssl_hmac_vector(int type, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac, + unsigned int mdlen) +{ + Hmac hmac; + size_t i; + + (void) mdlen; + + if (TEST_FAIL()) + return -1; + + if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0) + return -1; + for (i = 0; i < num_elem; i++) + if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0) + return -1; + if (wc_HmacFinal(&hmac, mac) != 0) + return -1; + return 0; +} + + +#ifndef CONFIG_FIPS + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len, + mac, 16); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_FIPS */ + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len, + mac, 20); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len, + mac, 32); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len, + mac, 48); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len, + mac, 64); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid, + ssid_len, iterations, buflen, WC_SHA) != 0) + return -1; + return 0; +} + + +#ifdef CONFIG_DES +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + Des des; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION); + wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE); + + return 0; +} +#endif /* CONFIG_DES */ + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + Aes *aes; + + if (TEST_FAIL()) + return NULL; + + aes = os_malloc(sizeof(Aes)); + if (!aes) + return NULL; + + if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) { + os_free(aes); + return NULL; + } + + return aes; +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + wc_AesEncryptDirect(ctx, crypt, plain); + return 0; +} + + +void aes_encrypt_deinit(void *ctx) +{ + os_free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + Aes *aes; + + if (TEST_FAIL()) + return NULL; + + aes = os_malloc(sizeof(Aes)); + if (!aes) + return NULL; + + if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) { + os_free(aes); + return NULL; + } + + return aes; +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + wc_AesDecryptDirect(ctx, plain, crypt); + return 0; +} + + +void aes_decrypt_deinit(void *ctx) +{ + os_free(ctx); +} + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + Aes aes; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION); + if (ret != 0) + return -1; + + ret = wc_AesCbcEncrypt(&aes, data, data, data_len); + if (ret != 0) + return -1; + return 0; +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + Aes aes; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION); + if (ret != 0) + return -1; + + ret = wc_AesCbcDecrypt(&aes, data, data, data_len); + if (ret != 0) + return -1; + return 0; +} + + +int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8, + NULL); + return ret != (n + 1) * 8 ? -1 : 0; +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8, + NULL); + return ret != n * 8 ? -1 : 0; +} + + +#ifndef CONFIG_NO_RC4 +int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, + size_t data_len) +{ +#ifndef NO_RC4 + Arc4 arc4; + unsigned char skip_buf[16]; + + wc_Arc4SetKey(&arc4, key, keylen); + + while (skip >= sizeof(skip_buf)) { + size_t len = skip; + + if (len > sizeof(skip_buf)) + len = sizeof(skip_buf); + wc_Arc4Process(&arc4, skip_buf, skip_buf, len); + skip -= len; + } + + wc_Arc4Process(&arc4, data, data, data_len); + + return 0; +#else /* NO_RC4 */ + return -1; +#endif /* NO_RC4 */ +} +#endif /* CONFIG_NO_RC4 */ + + +#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \ + || defined(EAP_SERVER_IKEV2) +union wolfssl_cipher { + Aes aes; + Des3 des3; + Arc4 arc4; +}; + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union wolfssl_cipher enc; + union wolfssl_cipher dec; +}; + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4SetKey(&ctx->enc.arc4, key, key_len); + wc_Arc4SetKey(&ctx->dec.arc4, key, key_len); + break; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + switch (key_len) { + case 16: + case 24: + case 32: + break; + default: + os_free(ctx); + return NULL; + } + if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv, + AES_ENCRYPTION) || + wc_AesSetKey(&ctx->dec.aes, key, key_len, iv, + AES_DECRYPTION)) { + os_free(ctx); + return NULL; + } + break; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != DES3_KEYLEN || + wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) || + wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) { + os_free(ctx); + return NULL; + } + break; +#endif /* NO_DES3 */ + case CRYPTO_CIPHER_ALG_RC2: + case CRYPTO_CIPHER_ALG_DES: + default: + os_free(ctx); + return NULL; + } + + ctx->alg = alg; + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + switch (ctx->alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len); + return 0; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0) + return -1; + return 0; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0) + return -1; + return 0; +#endif /* NO_DES3 */ + default: + return -1; + } + return -1; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + switch (ctx->alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len); + return 0; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0) + return -1; + return 0; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0) + return -1; + return 0; +#endif /* NO_DES3 */ + default: + return -1; + } + return -1; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + os_free(ctx); +} + +#endif + + +#ifdef CONFIG_WPS_NFC + +static const unsigned char RFC3526_PRIME_1536[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const unsigned char RFC3526_GENERATOR_1536[] = { + 0x02 +}; + +#define RFC3526_LEN sizeof(RFC3526_PRIME_1536) + + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) +{ + WC_RNG rng; + DhKey *ret = NULL; + DhKey *dh = NULL; + struct wpabuf *privkey = NULL; + struct wpabuf *pubkey = NULL; + word32 priv_sz, pub_sz; + + *priv = NULL; + wpabuf_free(*publ); + *publ = NULL; + + dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!dh) + return NULL; + wc_InitDhKey(dh); + + if (wc_InitRng(&rng) != 0) { + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + + privkey = wpabuf_alloc(RFC3526_LEN); + pubkey = wpabuf_alloc(RFC3526_LEN); + if (!privkey || !pubkey) + goto done; + + if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), + RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536)) + != 0) + goto done; + + if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz, + wpabuf_mhead(pubkey), &pub_sz) != 0) + goto done; + + wpabuf_put(privkey, priv_sz); + wpabuf_put(pubkey, pub_sz); + + ret = dh; + *priv = privkey; + *publ = pubkey; + dh = NULL; + privkey = NULL; + pubkey = NULL; +done: + wpabuf_clear_free(pubkey); + wpabuf_clear_free(privkey); + if (dh) { + wc_FreeDhKey(dh); + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + wc_FreeRng(&rng); + return ret; +} + + +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + DhKey *ret = NULL; + DhKey *dh; + byte *secret; + word32 secret_sz; + + dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!dh) + return NULL; + wc_InitDhKey(dh); + + secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!secret) + goto done; + + if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), + RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536)) + != 0) + goto done; + + if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv), + wpabuf_len(priv), RFC3526_GENERATOR_1536, + sizeof(RFC3526_GENERATOR_1536)) != 0) + goto done; + + if (secret_sz != wpabuf_len(publ) || + os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0) + goto done; + + ret = dh; + dh = NULL; +done: + if (dh) { + wc_FreeDhKey(dh); + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} + + +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private) +{ + struct wpabuf *ret = NULL; + struct wpabuf *secret; + word32 secret_sz; + + secret = wpabuf_alloc(RFC3526_LEN); + if (!secret) + goto done; + + if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz, + wpabuf_head(own_private), wpabuf_len(own_private), + wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0) + goto done; + + wpabuf_put(secret, secret_sz); + + ret = secret; + secret = NULL; +done: + wpabuf_clear_free(secret); + return ret; +} + + +void dh5_free(void *ctx) +{ + if (!ctx) + return; + + wc_FreeDhKey(ctx); + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +#endif /* CONFIG_WPS_NFC */ + + +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + int ret = -1; + WC_RNG rng; + DhKey *dh = NULL; + word32 priv_sz, pub_sz; + + if (TEST_FAIL()) + return -1; + + dh = os_malloc(sizeof(DhKey)); + if (!dh) + return -1; + wc_InitDhKey(dh); + + if (wc_InitRng(&rng) != 0) { + os_free(dh); + return -1; + } + + if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0) + goto done; + + if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz) + != 0) + goto done; + + if (priv_sz < prime_len) { + size_t pad_sz = prime_len - priv_sz; + + os_memmove(privkey + pad_sz, privkey, priv_sz); + os_memset(privkey, 0, pad_sz); + } + + if (pub_sz < prime_len) { + size_t pad_sz = prime_len - pub_sz; + + os_memmove(pubkey + pad_sz, pubkey, pub_sz); + os_memset(pubkey, 0, pad_sz); + } + ret = 0; +done: + wc_FreeDhKey(dh); + os_free(dh); + wc_FreeRng(&rng); + return ret; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + int ret = -1; + DhKey *dh; + word32 secret_sz; + + dh = os_malloc(sizeof(DhKey)); + if (!dh) + return -1; + wc_InitDhKey(dh); + + if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0) + goto done; + + if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey, + pubkey_len) != 0) + goto done; + + *len = secret_sz; + ret = 0; +done: + wc_FreeDhKey(dh); + os_free(dh); + return ret; +} + + +#ifdef CONFIG_FIPS +int crypto_get_random(void *buf, size_t len) +{ + int ret = 0; + WC_RNG rng; + + if (wc_InitRng(&rng) != 0) + return -1; + if (wc_RNG_GenerateBlock(&rng, buf, len) != 0) + ret = -1; + wc_FreeRng(&rng); + return ret; +} +#endif /* CONFIG_FIPS */ + + +#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) +struct crypto_hash { + Hmac hmac; + int size; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ret = NULL; + struct crypto_hash *hash; + int type; + + hash = os_zalloc(sizeof(*hash)); + if (!hash) + goto done; + + switch (alg) { +#ifndef NO_MD5 + case CRYPTO_HASH_ALG_HMAC_MD5: + hash->size = 16; + type = WC_MD5; + break; +#endif /* NO_MD5 */ +#ifndef NO_SHA + case CRYPTO_HASH_ALG_HMAC_SHA1: + type = WC_SHA; + hash->size = 20; + break; +#endif /* NO_SHA */ +#ifdef CONFIG_SHA256 +#ifndef NO_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + type = WC_SHA256; + hash->size = 32; + break; +#endif /* NO_SHA256 */ +#endif /* CONFIG_SHA256 */ + default: + goto done; + } + + if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0) + goto done; + + ret = hash; + hash = NULL; +done: + os_free(hash); + return ret; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (!ctx) + return; + wc_HmacUpdate(&ctx->hmac, data, len); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + int ret = 0; + + if (!ctx) + return -2; + + if (!mac || !len) + goto done; + + if (wc_HmacFinal(&ctx->hmac, mac) != 0) { + ret = -1; + goto done; + } + + *len = ctx->size; + ret = 0; +done: + bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; + return ret; +} + +#endif + + +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + Cmac cmac; + size_t i; + word32 sz; + + if (TEST_FAIL()) + return -1; + + if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0) + return -1; + + for (i = 0; i < num_elem; i++) + if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0) + return -1; + + sz = AES_BLOCK_SIZE; + if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE) + return -1; + + return 0; +} + + +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} + + +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} + + +struct crypto_bignum * crypto_bignum_init(void) +{ + mp_int *a; + + if (TEST_FAIL()) + return NULL; + + a = os_malloc(sizeof(*a)); + if (!a || mp_init(a) != MP_OKAY) { + os_free(a); + a = NULL; + } + + return (struct crypto_bignum *) a; +} + + +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) +{ + mp_int *a; + + if (TEST_FAIL()) + return NULL; + + a = (mp_int *) crypto_bignum_init(); + if (!a) + return NULL; + + if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) { + os_free(a); + a = NULL; + } + + return (struct crypto_bignum *) a; +} + + +void crypto_bignum_deinit(struct crypto_bignum *n, int clear) +{ + if (!n) + return; + + if (clear) + mp_forcezero((mp_int *) n); + mp_clear((mp_int *) n); + os_free((mp_int *) n); +} + + +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + + if (TEST_FAIL()) + return -1; + + if (padlen > buflen) + return -1; + + num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8; + if ((size_t) num_bytes > buflen) + return -1; + if (padlen > (size_t) num_bytes) + offset = padlen - num_bytes; + else + offset = 0; + + os_memset(buf, 0, offset); + mp_to_unsigned_bin((mp_int *) a, buf + offset); + + return num_bytes + offset; +} + + +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) +{ + int ret = 0; + WC_RNG rng; + + if (TEST_FAIL()) + return -1; + if (wc_InitRng(&rng) != 0) + return -1; + if (mp_rand_prime((mp_int *) r, + (mp_count_bits((mp_int *) m) + 7) / 8 * 2, + &rng, NULL) != 0) + ret = -1; + if (ret == 0 && + mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0) + ret = -1; + wc_FreeRng(&rng); + return ret; +} + + +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *r) +{ + return mp_add((mp_int *) a, (mp_int *) b, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + return mp_mod((mp_int *) a, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_exptmod(const struct crypto_bignum *b, + const struct crypto_bignum *e, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_invmod((mp_int *) a, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_add((mp_int *) a, (mp_int *) b, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *d) +{ + if (TEST_FAIL()) + return -1; + + return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d, + NULL) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *m, + struct crypto_bignum *d) +{ + if (TEST_FAIL()) + return -1; + + return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m, + (mp_int *) d) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r) +{ + if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY) + return -1; + mp_rshb((mp_int *) r, n); + return 0; +} + + +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b) +{ + return mp_cmp((mp_int *) a, (mp_int *) b); +} + + +int crypto_bignum_bits(const struct crypto_bignum *a) +{ + return mp_count_bits((mp_int *) a); +} + + +int crypto_bignum_is_zero(const struct crypto_bignum *a) +{ + return mp_iszero((mp_int *) a); +} + + +int crypto_bignum_is_one(const struct crypto_bignum *a) +{ + return mp_isone((const mp_int *) a); +} + +int crypto_bignum_is_odd(const struct crypto_bignum *a) +{ + return mp_isodd((mp_int *) a); +} + + +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + mp_int t; + int ret; + int res = -2; + + if (TEST_FAIL()) + return -2; + + if (mp_init(&t) != MP_OKAY) + return -2; + + /* t = (p-1) / 2 */ + ret = mp_sub_d((mp_int *) p, 1, &t); + if (ret == MP_OKAY) + mp_rshb(&t, 1); + if (ret == MP_OKAY) + ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t); + if (ret == MP_OKAY) { + if (mp_isone(&t)) + res = 1; + else if (mp_iszero(&t)) + res = 0; + else + res = -1; + } + + mp_clear(&t); + return res; +} + + +#ifdef CONFIG_ECC + +int ecc_map(ecc_point *, mp_int *, mp_digit); +int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, + mp_int *a, mp_int *modulus, mp_digit mp); + +struct crypto_ec { + ecc_key key; + mp_int a; + mp_int prime; + mp_int order; + mp_digit mont_b; + mp_int b; +}; + + +struct crypto_ec * crypto_ec_init(int group) +{ + int built = 0; + struct crypto_ec *e; + int curve_id; + + /* Map from IANA registry for IKE D-H groups to OpenSSL NID */ + switch (group) { + case 19: + curve_id = ECC_SECP256R1; + break; + case 20: + curve_id = ECC_SECP384R1; + break; + case 21: + curve_id = ECC_SECP521R1; + break; + case 25: + curve_id = ECC_SECP192R1; + break; + case 26: + curve_id = ECC_SECP224R1; + break; +#ifdef HAVE_ECC_BRAINPOOL + case 27: + curve_id = ECC_BRAINPOOLP224R1; + break; + case 28: + curve_id = ECC_BRAINPOOLP256R1; + break; + case 29: + curve_id = ECC_BRAINPOOLP384R1; + break; + case 30: + curve_id = ECC_BRAINPOOLP512R1; + break; +#endif /* HAVE_ECC_BRAINPOOL */ + default: + return NULL; + } + + e = os_zalloc(sizeof(*e)); + if (!e) + return NULL; + + if (wc_ecc_init(&e->key) != 0 || + wc_ecc_set_curve(&e->key, 0, curve_id) != 0 || + mp_init(&e->a) != MP_OKAY || + mp_init(&e->prime) != MP_OKAY || + mp_init(&e->order) != MP_OKAY || + mp_init(&e->b) != MP_OKAY || + mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY || + mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY || + mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY || + mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY || + mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY) + goto done; + + built = 1; +done: + if (!built) { + crypto_ec_deinit(e); + e = NULL; + } + return e; +} + + +void crypto_ec_deinit(struct crypto_ec* e) +{ + if (!e) + return; + + mp_clear(&e->b); + mp_clear(&e->order); + mp_clear(&e->prime); + mp_clear(&e->a); + wc_ecc_free(&e->key); + os_free(e); +} + + +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) +{ + if (TEST_FAIL()) + return NULL; + if (!e) + return NULL; + return (struct crypto_ec_point *) wc_ecc_new_point(); +} + + +size_t crypto_ec_prime_len(struct crypto_ec *e) +{ + return (mp_count_bits(&e->prime) + 7) / 8; +} + + +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return mp_count_bits(&e->prime); +} + + +size_t crypto_ec_order_len(struct crypto_ec *e) +{ + return (mp_count_bits(&e->order) + 7) / 8; +} + + +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->prime; +} + + +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->order; +} + + +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) +{ + ecc_point *point = (ecc_point *) p; + + if (!p) + return; + + if (clear) { + mp_forcezero(point->x); + mp_forcezero(point->y); + mp_forcezero(point->z); + } + wc_ecc_del_point(point); +} + + +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x) +{ + return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1; +} + + +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) +{ + ecc_point *p = (ecc_point *) point; + + if (TEST_FAIL()) + return -1; + + if (!mp_isone(p->z)) { + if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY) + return -1; + } + + if (x) { + if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x, + e->key.dp->size, + e->key.dp->size) <= 0) + return -1; + } + + if (y) { + if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y, + e->key.dp->size, + e->key.dp->size) <= 0) + return -1; + } + + return 0; +} + + +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) +{ + ecc_point *point = NULL; + int loaded = 0; + + if (TEST_FAIL()) + return NULL; + + point = wc_ecc_new_point(); + if (!point) + goto done; + + if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY) + goto done; + val += e->key.dp->size; + if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY) + goto done; + mp_set(point->z, 1); + + loaded = 1; +done: + if (!loaded) { + wc_ecc_del_point(point); + point = NULL; + } + return (struct crypto_ec_point *) point; +} + + +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c) +{ + mp_int mu; + ecc_point *ta = NULL, *tb = NULL; + ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b; + mp_int *modulus = &e->prime; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = mp_init(&mu); + if (ret != MP_OKAY) + return -1; + + ret = mp_montgomery_calc_normalization(&mu, modulus); + if (ret != MP_OKAY) { + mp_clear(&mu); + return -1; + } + + if (!mp_isone(&mu)) { + ta = wc_ecc_new_point(); + if (!ta) { + mp_clear(&mu); + return -1; + } + tb = wc_ecc_new_point(); + if (!tb) { + wc_ecc_del_point(ta); + mp_clear(&mu); + return -1; + } + + if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY || + mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY || + mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY || + mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY || + mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY || + mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) { + ret = -1; + goto end; + } + pa = ta; + pb = tb; + } + + ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a, + &e->prime, e->mont_b); + if (ret != 0) { + ret = -1; + goto end; + } + + if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY) + ret = -1; + else + ret = 0; +end: + wc_ecc_del_point(tb); + wc_ecc_del_point(ta); + mp_clear(&mu); + return ret; +} + + +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res, + &e->a, &e->prime, 1); + return ret == 0 ? 0 : -1; +} + + +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) +{ + ecc_point *point = (ecc_point *) p; + + if (TEST_FAIL()) + return -1; + + if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY) + return -1; + + return 0; +} + + +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit) +{ + byte buf[1 + 2 * MAX_ECC_BYTES]; + int ret; + int prime_len = crypto_ec_prime_len(e); + + if (TEST_FAIL()) + return -1; + + buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN; + ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len); + if (ret <= 0) + return -1; + ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx, + (ecc_point *) p); + if (ret != 0) + return -1; + + return 0; +} + + +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + mp_int *y2 = NULL; + mp_int t; + int calced = 0; + + if (TEST_FAIL()) + return NULL; + + if (mp_init(&t) != MP_OKAY) + return NULL; + + y2 = (mp_int *) crypto_bignum_init(); + if (!y2) + goto done; + + if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 || + mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 || + mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 || + mp_addmod(y2, &t, &e->prime, y2) != 0 || + mp_addmod(y2, &e->b, &e->prime, y2) != 0) + goto done; + + calced = 1; +done: + if (!calced) { + if (y2) { + mp_clear(y2); + os_free(y2); + } + mp_clear(&t); + } + + return (struct crypto_bignum *) y2; +} + + +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return wc_ecc_point_is_at_infinity((ecc_point *) p); +} + + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) == + MP_OKAY; +} + + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b); +} + + +struct crypto_ecdh { + struct crypto_ec *ec; +}; + +struct crypto_ecdh * crypto_ecdh_init(int group) +{ + struct crypto_ecdh *ecdh = NULL; + WC_RNG rng; + int ret; + + if (wc_InitRng(&rng) != 0) + goto fail; + + ecdh = os_zalloc(sizeof(*ecdh)); + if (!ecdh) + goto fail; + + ecdh->ec = crypto_ec_init(group); + if (!ecdh->ec) + goto fail; + + ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key, + ecdh->ec->key.dp->id); + if (ret < 0) + goto fail; + +done: + wc_FreeRng(&rng); + + return ecdh; +fail: + crypto_ecdh_deinit(ecdh); + ecdh = NULL; + goto done; +} + + +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) +{ + if (ecdh) { + crypto_ec_deinit(ecdh->ec); + os_free(ecdh); + } +} + + +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) +{ + struct wpabuf *buf = NULL; + int ret; + int len = ecdh->ec->key.dp->size; + + buf = wpabuf_alloc(inc_y ? 2 * len : len); + if (!buf) + goto fail; + + ret = crypto_bignum_to_bin((struct crypto_bignum *) + ecdh->ec->key.pubkey.x, wpabuf_put(buf, len), + len, len); + if (ret < 0) + goto fail; + if (inc_y) { + ret = crypto_bignum_to_bin((struct crypto_bignum *) + ecdh->ec->key.pubkey.y, + wpabuf_put(buf, len), len, len); + if (ret < 0) + goto fail; + } + +done: + return buf; +fail: + wpabuf_free(buf); + buf = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len) +{ + int ret; + struct wpabuf *pubkey = NULL; + struct wpabuf *secret = NULL; + word32 key_len = ecdh->ec->key.dp->size; + ecc_point *point = NULL; + size_t need_key_len = inc_y ? 2 * key_len : key_len; + + if (len < need_key_len) + goto fail; + pubkey = wpabuf_alloc(1 + 2 * key_len); + if (!pubkey) + goto fail; + wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN); + wpabuf_put_data(pubkey, key, need_key_len); + + point = wc_ecc_new_point(); + if (!point) + goto fail; + + ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len, + ecdh->ec->key.idx, point); + if (ret != MP_OKAY) + goto fail; + + secret = wpabuf_alloc(key_len); + if (!secret) + goto fail; + + ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point, + wpabuf_put(secret, key_len), &key_len); + if (ret != MP_OKAY) + goto fail; + +done: + wc_ecc_del_point(point); + wpabuf_free(pubkey); + return secret; +fail: + wpabuf_free(secret); + secret = NULL; + goto done; +} + +#endif /* CONFIG_ECC */ diff --git a/contrib/wpa/src/crypto/des-internal.c b/contrib/wpa/src/crypto/des-internal.c index dec39ef..4ed6957 100644 --- a/contrib/wpa/src/crypto/des-internal.c +++ b/contrib/wpa/src/crypto/des-internal.c @@ -48,7 +48,7 @@ static const u32 bytebit[8] = { - 0200, 0100, 040, 020, 010, 04, 02, 01 + 0200, 0100, 040, 020, 010, 04, 02, 01 }; static const u32 bigbyte[24] = @@ -58,22 +58,22 @@ static const u32 bigbyte[24] = 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL, 0x800UL, 0x400UL, 0x200UL, 0x100UL, 0x80UL, 0x40UL, 0x20UL, 0x10UL, - 0x8UL, 0x4UL, 0x2UL, 0x1L + 0x8UL, 0x4UL, 0x2UL, 0x1L }; /* Use the key schedule specific in the standard (ANSI X3.92-1981) */ static const u8 pc1[56] = { - 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, - 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, - 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; static const u8 totrot[16] = { 1, 2, 4, 6, - 8, 10, 12, 14, - 15, 17, 19, 21, + 8, 10, 12, 14, + 15, 17, 19, 21, 23, 25, 27, 28 }; @@ -396,7 +396,7 @@ static void desfunc(u32 *block, const u32 *keys) /* wpa_supplicant/hostapd specific wrapper */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -421,6 +421,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) os_memset(pkey, 0, sizeof(pkey)); os_memset(ek, 0, sizeof(ek)); + return 0; } diff --git a/contrib/wpa/src/crypto/dh_group5.c b/contrib/wpa/src/crypto/dh_group5.c index ccdbfc8..425c848 100644 --- a/contrib/wpa/src/crypto/dh_group5.c +++ b/contrib/wpa/src/crypto/dh_group5.c @@ -15,6 +15,7 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { + wpabuf_free(*publ); *publ = dh_init(dh_groups_get(5), priv); if (*publ == NULL) return NULL; diff --git a/contrib/wpa/src/crypto/dh_groups.c b/contrib/wpa/src/crypto/dh_groups.c index 3aeb2bb..5e421b2 100644 --- a/contrib/wpa/src/crypto/dh_groups.c +++ b/contrib/wpa/src/crypto/dh_groups.c @@ -1151,7 +1151,7 @@ static const u8 dh_group24_order[] = { { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } - + static const struct dh_group dh_groups[] = { DH_GROUP(5, 1), @@ -1203,32 +1203,24 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) if (*priv == NULL) return NULL; - if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) - { + pv_len = dh->prime_len; + pv = wpabuf_alloc(pv_len); + if (pv == NULL) { wpabuf_clear_free(*priv); *priv = NULL; return NULL; } - - if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) { - /* Make sure private value is smaller than prime */ - *(wpabuf_mhead_u8(*priv)) = 0; - } - wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); - - pv_len = dh->prime_len; - pv = wpabuf_alloc(pv_len); - if (pv == NULL) - return NULL; - if (crypto_mod_exp(dh->generator, dh->generator_len, - wpabuf_head(*priv), wpabuf_len(*priv), - dh->prime, dh->prime_len, wpabuf_mhead(pv), - &pv_len) < 0) { + if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len, + wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) { wpabuf_clear_free(pv); - wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpa_printf(MSG_INFO, "DH: crypto_dh_init failed"); + wpabuf_clear_free(*priv); + *priv = NULL; return NULL; } - wpabuf_put(pv, pv_len); + wpabuf_put(*priv, dh->prime_len); + wpabuf_put(pv, dh->prime_len); + wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv); return pv; @@ -1256,12 +1248,15 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, shared = wpabuf_alloc(shared_len); if (shared == NULL) return NULL; - if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), - wpabuf_head(own_private), wpabuf_len(own_private), - dh->prime, dh->prime_len, - wpabuf_mhead(shared), &shared_len) < 0) { + if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + dh->order, dh->order_len, + wpabuf_head(own_private), + wpabuf_len(own_private), + wpabuf_head(peer_public), + wpabuf_len(peer_public), + wpabuf_mhead(shared), &shared_len) < 0) { wpabuf_clear_free(shared); - wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed"); return NULL; } wpabuf_put(shared, shared_len); diff --git a/contrib/wpa/src/crypto/fips_prf_openssl.c b/contrib/wpa/src/crypto/fips_prf_openssl.c index fb03efc..4697e04 100644 --- a/contrib/wpa/src/crypto/fips_prf_openssl.c +++ b/contrib/wpa/src/crypto/fips_prf_openssl.c @@ -17,6 +17,19 @@ static void sha1_transform(u32 *state, const u8 data[64]) { SHA_CTX context; os_memset(&context, 0, sizeof(context)); +#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID) + context.h[0] = state[0]; + context.h[1] = state[1]; + context.h[2] = state[2]; + context.h[3] = state[3]; + context.h[4] = state[4]; + SHA1_Transform(&context, data); + state[0] = context.h[0]; + state[1] = context.h[1]; + state[2] = context.h[2]; + state[3] = context.h[3]; + state[4] = context.h[4]; +#else context.h0 = state[0]; context.h1 = state[1]; context.h2 = state[2]; @@ -28,6 +41,7 @@ static void sha1_transform(u32 *state, const u8 data[64]) state[2] = context.h2; state[3] = context.h3; state[4] = context.h4; +#endif } @@ -62,12 +76,11 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) /* w_i = G(t, XVAL) */ os_memcpy(_t, t, 20); sha1_transform(_t, xkey); - _t[0] = host_to_be32(_t[0]); - _t[1] = host_to_be32(_t[1]); - _t[2] = host_to_be32(_t[2]); - _t[3] = host_to_be32(_t[3]); - _t[4] = host_to_be32(_t[4]); - os_memcpy(xpos, _t, 20); + WPA_PUT_BE32(xpos, _t[0]); + WPA_PUT_BE32(xpos + 4, _t[1]); + WPA_PUT_BE32(xpos + 8, _t[2]); + WPA_PUT_BE32(xpos + 12, _t[3]); + WPA_PUT_BE32(xpos + 16, _t[4]); /* XKEY = (1 + XKEY + w_i) mod 2^b */ carry = 1; diff --git a/contrib/wpa/src/crypto/fips_prf_wolfssl.c b/contrib/wpa/src/crypto/fips_prf_wolfssl.c new file mode 100644 index 0000000..feb39db --- /dev/null +++ b/contrib/wpa/src/crypto/fips_prf_wolfssl.c @@ -0,0 +1,87 @@ +/* + * FIPS 186-2 PRF for libcrypto + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <wolfssl/options.h> +#include <wolfssl/wolfcrypt/sha.h> + +#include "common.h" +#include "crypto.h" + + +static void sha1_transform(u32 *state, const u8 data[64]) +{ + wc_Sha sha; + + os_memset(&sha, 0, sizeof(sha)); + sha.digest[0] = state[0]; + sha.digest[1] = state[1]; + sha.digest[2] = state[2]; + sha.digest[3] = state[3]; + sha.digest[4] = state[4]; + wc_ShaUpdate(&sha, data, 64); + state[0] = sha.digest[0]; + state[1] = sha.digest[1]; + state[2] = sha.digest[2]; + state[3] = sha.digest[3]; + state[4] = sha.digest[4]; +} + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, seed_len); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + os_memcpy(_t, t, 20); + sha1_transform(_t, xkey); + WPA_PUT_BE32(xpos, _t[0]); + WPA_PUT_BE32(xpos + 4, _t[1]); + WPA_PUT_BE32(xpos + 8, _t[2]); + WPA_PUT_BE32(xpos + 12, _t[3]); + WPA_PUT_BE32(xpos + 16, _t[4]); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += 20; + } + /* x_j = w_0|w_1 */ + } + + return 0; +} diff --git a/contrib/wpa/src/crypto/md4-internal.c b/contrib/wpa/src/crypto/md4-internal.c index cd5e6ca..cf408e8 100644 --- a/contrib/wpa/src/crypto/md4-internal.c +++ b/contrib/wpa/src/crypto/md4-internal.c @@ -31,6 +31,9 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) MD4_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + MD4Init(&ctx); for (i = 0; i < num_elem; i++) MD4Update(&ctx, addr[i], len[i]); @@ -82,7 +85,7 @@ MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); (cp)[1] = (value) >> 8; \ (cp)[0] = (value); } while (0) -static u8 PADDING[MD4_BLOCK_LENGTH] = { +static const u8 PADDING[MD4_BLOCK_LENGTH] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/contrib/wpa/src/crypto/md5-internal.c b/contrib/wpa/src/crypto/md5-internal.c index f0a2a5d..944698a 100644 --- a/contrib/wpa/src/crypto/md5-internal.c +++ b/contrib/wpa/src/crypto/md5-internal.c @@ -33,6 +33,9 @@ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) MD5_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + MD5Init(&ctx); for (i = 0; i < num_elem; i++) MD5Update(&ctx, addr[i], len[i]); diff --git a/contrib/wpa/src/crypto/ms_funcs.c b/contrib/wpa/src/crypto/ms_funcs.c index 053d203..aff7d33 100644 --- a/contrib/wpa/src/crypto/ms_funcs.c +++ b/contrib/wpa/src/crypto/ms_funcs.c @@ -48,7 +48,7 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, WPA_PUT_LE16(ucs2_buffer + j, ((c & 0x1F) << 6) | (c2 & 0x3F)); j += 2; - } else if (i == utf8_string_len || + } else if (i == utf8_string_len - 1 || j >= ucs2_buffer_size - 1) { /* incomplete surrogate */ return -1; @@ -140,17 +140,20 @@ int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) * @challenge: 8-octet Challenge (IN) * @password_hash: 16-octet PasswordHash (IN) * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response) +int challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) { u8 zpwd[7]; - des_encrypt(challenge, password_hash, response); - des_encrypt(challenge, password_hash + 7, response + 8); + + if (des_encrypt(challenge, password_hash, response) < 0 || + des_encrypt(challenge, password_hash + 7, response + 8) < 0) + return -1; zpwd[0] = password_hash[14]; zpwd[1] = password_hash[15]; os_memset(zpwd + 2, 0, 5); - des_encrypt(challenge, zpwd, response + 16); + return des_encrypt(challenge, zpwd, response + 16); } @@ -175,9 +178,9 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge) || - nt_password_hash(password, password_len, password_hash)) + nt_password_hash(password, password_len, password_hash) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -202,9 +205,9 @@ int generate_nt_response_pwhash(const u8 *auth_challenge, if (challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge)) + challenge) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -304,9 +307,10 @@ int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response) { u8 password_hash[16]; - if (nt_password_hash(password, password_len, password_hash)) + + if (nt_password_hash(password, password_len, password_hash) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -487,12 +491,15 @@ int new_password_encrypted_with_old_nt_password_hash( * @password_hash: 16-octer PasswordHash (IN) * @block: 16-octet Block (IN) * @cypher: 16-octer Cypher (OUT) + * Returns: 0 on success, -1 on failure */ -void nt_password_hash_encrypted_with_block(const u8 *password_hash, - const u8 *block, u8 *cypher) +int nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher) { - des_encrypt(password_hash, block, cypher); - des_encrypt(password_hash + 8, block + 7, cypher + 8); + if (des_encrypt(password_hash, block, cypher) < 0 || + des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0) + return -1; + return 0; } @@ -515,10 +522,10 @@ int old_nt_password_hash_encrypted_with_new_nt_password_hash( if (nt_password_hash(old_password, old_password_len, old_password_hash) || nt_password_hash(new_password, new_password_len, - new_password_hash)) + new_password_hash) || + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash)) return -1; - nt_password_hash_encrypted_with_block(old_password_hash, - new_password_hash, - encrypted_password_hash); return 0; } diff --git a/contrib/wpa/src/crypto/ms_funcs.h b/contrib/wpa/src/crypto/ms_funcs.h index b5b5918..b8d55f0 100644 --- a/contrib/wpa/src/crypto/ms_funcs.h +++ b/contrib/wpa/src/crypto/ms_funcs.h @@ -31,8 +31,8 @@ int generate_authenticator_response_pwhash( int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response); -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response); +int challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, u8 *challenge); int nt_password_hash(const u8 *password, size_t password_len, @@ -50,8 +50,8 @@ int __must_check new_password_encrypted_with_old_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_pw_block); -void nt_password_hash_encrypted_with_block(const u8 *password_hash, - const u8 *block, u8 *cypher); +int nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher); int old_nt_password_hash_encrypted_with_new_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, diff --git a/contrib/wpa/src/crypto/random.c b/contrib/wpa/src/crypto/random.c index 3a86a93..1cabf3f 100644 --- a/contrib/wpa/src/crypto/random.c +++ b/contrib/wpa/src/crypto/random.c @@ -25,6 +25,9 @@ #include "utils/includes.h" #ifdef __linux__ #include <fcntl.h> +#ifdef CONFIG_GETRANDOM +#include <sys/random.h> +#endif /* CONFIG_GETRANDOM */ #endif /* __linux__ */ #include "utils/common.h" @@ -54,7 +57,6 @@ static int random_fd = -1; static unsigned int own_pool_ready = 0; #define RANDOM_ENTROPY_SIZE 20 static char *random_entropy_file = NULL; -static int random_entropy_file_read = 0; #define MIN_COLLECT_ENTROPY 1000 static unsigned int entropy = 0; @@ -66,6 +68,9 @@ static void random_write_entropy(void); static u32 __ROL32(u32 x, u32 y) { + if (y == 0) + return x; + return (x << (y & 31)) | (x >> (32 - (y & 31))); } @@ -226,30 +231,52 @@ int random_pool_ready(void) return 1; /* Already initialized - good to continue */ /* - * Try to fetch some more data from the kernel high quality - * /dev/random. There may not be enough data available at this point, + * Try to fetch some more data from the kernel high quality RNG. + * There may not be enough data available at this point, * so use non-blocking read to avoid blocking the application * completely. */ - fd = open("/dev/random", O_RDONLY | O_NONBLOCK); - if (fd < 0) { - wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(errno)); - return -1; - } - res = read(fd, dummy_key + dummy_key_avail, - sizeof(dummy_key) - dummy_key_avail); +#ifdef CONFIG_GETRANDOM + res = getrandom(dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK); if (res < 0) { - wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " - "%s", strerror(errno)); - res = 0; + if (errno == ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() not supported, falling back to /dev/random"); + } else { + wpa_printf(MSG_INFO, + "random: no data from getrandom(): %s", + strerror(errno)); + res = 0; + } } - wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " - "/dev/random", (unsigned) res, +#else /* CONFIG_GETRANDOM */ + res = -1; +#endif /* CONFIG_GETRANDOM */ + if (res < 0) { + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot open /dev/random: %s", + strerror(errno)); + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot read from /dev/random: %s", + strerror(errno)); + res = 0; + } + close(fd); + } + + wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res, (unsigned) (sizeof(dummy_key) - dummy_key_avail)); dummy_key_avail += res; - close(fd); if (dummy_key_avail == sizeof(dummy_key)) { if (own_pool_ready < MIN_READY_MARK) @@ -259,7 +286,7 @@ int random_pool_ready(void) } wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " - "random data available from /dev/random", + "random data available", (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); if (own_pool_ready >= MIN_READY_MARK || @@ -354,7 +381,6 @@ static void random_read_entropy(void) own_pool_ready = (u8) buf[0]; random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); - random_entropy_file_read = 1; os_free(buf); wpa_printf(MSG_DEBUG, "random: Added entropy from %s " "(own_pool_ready=%u)", @@ -412,6 +438,19 @@ void random_init(const char *entropy_file) if (random_fd >= 0) return; +#ifdef CONFIG_GETRANDOM + { + u8 dummy; + + if (getrandom(&dummy, 0, GRND_NONBLOCK) == 0 || + errno != ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() support available"); + return; + } + } +#endif /* CONFIG_GETRANDOM */ + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (random_fd < 0) { wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", diff --git a/contrib/wpa/src/crypto/sha1-internal.c b/contrib/wpa/src/crypto/sha1-internal.c index 24bc3ff..a491707 100644 --- a/contrib/wpa/src/crypto/sha1-internal.c +++ b/contrib/wpa/src/crypto/sha1-internal.c @@ -33,6 +33,9 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) SHA1_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + SHA1Init(&ctx); for (i = 0; i < num_elem; i++) SHA1Update(&ctx, addr[i], len[i]); @@ -50,7 +53,7 @@ By Steve Reid <sreid@sea-to-sky.net> 100% Public Domain ----------------- -Modified 7/98 +Modified 7/98 By James H. Brown <jbrown@burgoyne.com> Still 100% Public Domain @@ -72,7 +75,7 @@ Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). -I also changed the declaration of variables i & j in SHA1Update to +I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since @@ -99,7 +102,7 @@ Still 100% public domain Modified 4/01 By Saul Kravitz <Saul.Kravitz@celera.com> Still 100% PD -Modified to run on Compaq Alpha hardware. +Modified to run on Compaq Alpha hardware. ----------------- Modified 4/01 @@ -159,7 +162,7 @@ void SHAPrintContext(SHA1_CTX *context, char *msg) { printf("%s (%d,%d) %x %x %x %x %x\n", msg, - context->count[0], context->count[1], + context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], @@ -294,7 +297,6 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context) 255); } /* Wipe variables */ - i = 0; os_memset(context->buffer, 0, 64); os_memset(context->state, 0, 20); os_memset(context->count, 0, 8); diff --git a/contrib/wpa/src/crypto/sha1-tlsprf.c b/contrib/wpa/src/crypto/sha1-tlsprf.c index f9bc0eb..a11649a 100644 --- a/contrib/wpa/src/crypto/sha1-tlsprf.c +++ b/contrib/wpa/src/crypto/sha1-tlsprf.c @@ -40,9 +40,6 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const unsigned char *SHA1_addr[3]; size_t SHA1_len[3]; - if (secret_len & 1) - return -1; - MD5_addr[0] = A_MD5; MD5_len[0] = MD5_MAC_LEN; MD5_addr[1] = (unsigned char *) label; diff --git a/contrib/wpa/src/crypto/sha256-internal.c b/contrib/wpa/src/crypto/sha256-internal.c index 35299b0..ff1e2ba1 100644 --- a/contrib/wpa/src/crypto/sha256-internal.c +++ b/contrib/wpa/src/crypto/sha256-internal.c @@ -28,6 +28,9 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, struct sha256_state ctx; size_t i; + if (TEST_FAIL()) + return -1; + sha256_init(&ctx); for (i = 0; i < num_elem; i++) if (sha256_process(&ctx, addr[i], len[i])) @@ -66,7 +69,7 @@ static const unsigned long K[64] = { ( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) RORc((x), (n)) #define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) #define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) @@ -97,7 +100,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) for (i = 16; i < 64; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } + } /* Compress */ #define RND(a,b,c,d,e,f,g,h,i) \ @@ -108,7 +111,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) for (i = 0; i < 64; ++i) { RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); - t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; } diff --git a/contrib/wpa/src/crypto/sha256-kdf.c b/contrib/wpa/src/crypto/sha256-kdf.c index e7509ce..af7d954 100644 --- a/contrib/wpa/src/crypto/sha256-kdf.c +++ b/contrib/wpa/src/crypto/sha256-kdf.c @@ -1,6 +1,6 @@ /* - * HMAC-SHA256 KDF (RFC 5295) - * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -16,7 +16,8 @@ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295) * @secret: Key for KDF * @secret_len: Length of the key in bytes - * @label: A unique label for each purpose of the KDF + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) * @seed: Seed value to bind into the key * @seed_len: Length of the seed * @out: Buffer for the generated pseudo-random key @@ -24,7 +25,9 @@ * Returns: 0 on success, -1 on failure. * * This function is used to derive new, cryptographically separate keys from a - * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. */ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, @@ -38,8 +41,13 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, addr[0] = T; len[0] = SHA256_MAC_LEN; - addr[1] = (const unsigned char *) label; - len[1] = os_strlen(label) + 1; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } addr[2] = seed; len[2] = seed_len; addr[3] = &iter; diff --git a/contrib/wpa/src/crypto/sha256-prf.c b/contrib/wpa/src/crypto/sha256-prf.c index 79791c0..722cad6 100644 --- a/contrib/wpa/src/crypto/sha256-prf.c +++ b/contrib/wpa/src/crypto/sha256-prf.c @@ -1,6 +1,6 @@ /* * SHA256-based PRF (IEEE 802.11r) - * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,14 +22,16 @@ * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. */ -void sha256_prf(const u8 *key, size_t key_len, const char *label, +int sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { - sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); + return sha256_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); } @@ -42,15 +44,16 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. If the requested buf_len is not divisible by eight, the least * significant 1-7 bits of the last octet in the output are not part of the * requested output. */ -void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits) +int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; @@ -75,11 +78,14 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA256_MAC_LEN) { - hmac_sha256_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA256_MAC_LEN; } else { - hmac_sha256_vector(key, key_len, 4, addr, len, hash); + if (hmac_sha256_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); pos += plen; break; @@ -97,4 +103,6 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, } os_memset(hash, 0, sizeof(hash)); + + return 0; } diff --git a/contrib/wpa/src/crypto/sha256.h b/contrib/wpa/src/crypto/sha256.h index b15f511..5219022 100644 --- a/contrib/wpa/src/crypto/sha256.h +++ b/contrib/wpa/src/crypto/sha256.h @@ -1,6 +1,6 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,11 +15,11 @@ int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -void sha256_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits); +int sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); diff --git a/contrib/wpa/src/crypto/sha384-internal.c b/contrib/wpa/src/crypto/sha384-internal.c new file mode 100644 index 0000000..646f729 --- /dev/null +++ b/contrib/wpa/src/crypto/sha384-internal.c @@ -0,0 +1,92 @@ +/* + * SHA-384 hash implementation and interface functions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384_i.h" +#include "crypto.h" + + +/** + * sha384_vector - SHA384 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha384_state ctx; + size_t i; + + sha384_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha384_process(&ctx, addr[i], len[i])) + return -1; + if (sha384_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA384 implementation ===== */ + +/* This is based on SHA384 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define CONST64(n) n ## ULL + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +void sha384_init(struct sha384_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = CONST64(0xcbbb9d5dc1059ed8); + md->state[1] = CONST64(0x629a292a367cd507); + md->state[2] = CONST64(0x9159015a3070dd17); + md->state[3] = CONST64(0x152fecd8f70e5939); + md->state[4] = CONST64(0x67332667ffc00b31); + md->state[5] = CONST64(0x8eb44a8768581511); + md->state[6] = CONST64(0xdb0c2e0d64f98fa7); + md->state[7] = CONST64(0x47b5481dbefa4fa4); +} + +int sha384_process(struct sha384_state *md, const unsigned char *in, + unsigned long inlen) +{ + return sha512_process(md, in, inlen); +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (48 bytes) + @return CRYPT_OK if successful +*/ +int sha384_done(struct sha384_state *md, unsigned char *out) +{ + unsigned char buf[64]; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + if (sha512_done(md, buf) != 0) + return -1; + + os_memcpy(out, buf, 48); + return 0; +} + +/* ===== end - public domain SHA384 implementation ===== */ diff --git a/contrib/wpa/src/crypto/sha384-kdf.c b/contrib/wpa/src/crypto/sha384-kdf.c new file mode 100644 index 0000000..1d19627 --- /dev/null +++ b/contrib/wpa/src/crypto/sha384-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" + + +/** + * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA384_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA384_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA384_MAC_LEN) + clen = SHA384_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA384_MAC_LEN); + return 0; +} diff --git a/contrib/wpa/src/crypto/sha384-prf.c b/contrib/wpa/src/crypto/sha384-prf.c index 653920b..03e3cb3 100644 --- a/contrib/wpa/src/crypto/sha384-prf.c +++ b/contrib/wpa/src/crypto/sha384-prf.c @@ -1,6 +1,6 @@ /* * SHA384-based KDF (IEEE 802.11ac) - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,14 +22,16 @@ * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. */ -void sha384_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +int sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { - sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); + return sha384_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); } @@ -42,15 +44,16 @@ void sha384_prf(const u8 *key, size_t key_len, const char *label, * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. If the requested buf_len is not divisible by eight, the least * significant 1-7 bits of the last octet in the output are not part of the * requested output. */ -void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits) +int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; @@ -75,11 +78,14 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA384_MAC_LEN) { - hmac_sha384_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha384_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA384_MAC_LEN; } else { - hmac_sha384_vector(key, key_len, 4, addr, len, hash); + if (hmac_sha384_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); pos += plen; break; @@ -97,4 +103,6 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, } os_memset(hash, 0, sizeof(hash)); + + return 0; } diff --git a/contrib/wpa/src/crypto/sha384.c b/contrib/wpa/src/crypto/sha384.c new file mode 100644 index 0000000..ee136ce --- /dev/null +++ b/contrib/wpa/src/crypto/sha384.c @@ -0,0 +1,104 @@ +/* + * SHA-384 hash implementation and interface functions + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" +#include "crypto.h" + + +/** + * hmac_sha384_vector - HMAC-SHA384 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (48 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */ + unsigned char tk[48]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 128 bytes reset it to key = SHA384(key) */ + if (key_len > 128) { + if (sha384_vector(1, &key, &key_len, tk) < 0) + return -1; + key = tk; + key_len = 48; + } + + /* the HMAC_SHA384 transform looks like: + * + * SHA384(K XOR opad, SHA384(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 128 times + * opad is the byte 0x5c repeated 128 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA384 */ + _addr[0] = k_pad; + _len[0] = 128; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha384_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA384 */ + _addr[0] = k_pad; + _len[0] = 128; + _addr[1] = mac; + _len[1] = SHA384_MAC_LEN; + return sha384_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha384 - HMAC-SHA384 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (48 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/contrib/wpa/src/crypto/sha384.h b/contrib/wpa/src/crypto/sha384.h index 3deafa5..2241425 100644 --- a/contrib/wpa/src/crypto/sha384.h +++ b/contrib/wpa/src/crypto/sha384.h @@ -1,6 +1,6 @@ /* * SHA384 hash implementation and interface functions - * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,10 +15,13 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -void sha384_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits); +int sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA384_H */ diff --git a/contrib/wpa/src/crypto/sha384_i.h b/contrib/wpa/src/crypto/sha384_i.h new file mode 100644 index 0000000..a00253f --- /dev/null +++ b/contrib/wpa/src/crypto/sha384_i.h @@ -0,0 +1,23 @@ +/* + * SHA-384 internal definitions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA384_I_H +#define SHA384_I_H + +#include "sha512_i.h" + +#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE + +#define sha384_state sha512_state + +void sha384_init(struct sha384_state *md); +int sha384_process(struct sha384_state *md, const unsigned char *in, + unsigned long inlen); +int sha384_done(struct sha384_state *md, unsigned char *out); + +#endif /* SHA384_I_H */ diff --git a/contrib/wpa/src/crypto/sha512-internal.c b/contrib/wpa/src/crypto/sha512-internal.c new file mode 100644 index 0000000..c026394 --- /dev/null +++ b/contrib/wpa/src/crypto/sha512-internal.c @@ -0,0 +1,270 @@ +/* + * SHA-512 hash implementation and interface functions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512_i.h" +#include "crypto.h" + + +/** + * sha512_vector - SHA512 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha512_state ctx; + size_t i; + + sha512_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha512_process(&ctx, addr[i], len[i])) + return -1; + if (sha512_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA512 implementation ===== */ + +/* This is based on SHA512 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define CONST64(n) n ## ULL + +/* the K array */ +static const u64 K[80] = { + CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd), + CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc), + CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019), + CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118), + CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe), + CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2), + CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1), + CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694), + CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3), + CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65), + CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483), + CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5), + CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210), + CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4), + CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725), + CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70), + CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926), + CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df), + CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8), + CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b), + CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001), + CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30), + CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910), + CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8), + CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53), + CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8), + CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb), + CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3), + CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60), + CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec), + CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9), + CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b), + CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207), + CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178), + CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6), + CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b), + CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493), + CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c), + CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a), + CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817) +}; + +/* Various logical functions */ +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#define ROR64c(x, y) \ + ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \ + ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \ + CONST64(0xFFFFFFFFFFFFFFFF)) + +/* compress 1024-bits */ +static int sha512_compress(struct sha512_state *md, unsigned char *buf) +{ + u64 S[8], t0, t1; + u64 *W; + int i; + + W = os_malloc(80 * sizeof(u64)); + if (!W) + return -1; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE64(buf + (8 * i)); + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ + for (i = 0; i < 80; i++) { + t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i]; + t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]); + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3] + t0; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t0 + t1; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + os_free(W); + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +void sha512_init(struct sha512_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = CONST64(0x6a09e667f3bcc908); + md->state[1] = CONST64(0xbb67ae8584caa73b); + md->state[2] = CONST64(0x3c6ef372fe94f82b); + md->state[3] = CONST64(0xa54ff53a5f1d36f1); + md->state[4] = CONST64(0x510e527fade682d1); + md->state[5] = CONST64(0x9b05688c2b3e6c1f); + md->state[6] = CONST64(0x1f83d9abfb41bd6b); + md->state[7] = CONST64(0x5be0cd19137e2179); +} + + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +int sha512_process(struct sha512_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= SHA512_BLOCK_SIZE) { + if (sha512_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += SHA512_BLOCK_SIZE * 8; + in += SHA512_BLOCK_SIZE; + inlen -= SHA512_BLOCK_SIZE; + } else { + n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen)); + os_memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == SHA512_BLOCK_SIZE) { + if (sha512_compress(md, md->buf) < 0) + return -1; + md->length += 8 * SHA512_BLOCK_SIZE; + md->curlen = 0; + } + } + } + + return 0; +} + + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return CRYPT_OK if successful +*/ +int sha512_done(struct sha512_state *md, unsigned char *out) +{ + int i; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + /* increase the length of the message */ + md->length += md->curlen * CONST64(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad up to 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume + * that you won't hash > 2^64 bits of data... :-) + */ + while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 120, md->length); + sha512_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE64(out + (8 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA512 implementation ===== */ diff --git a/contrib/wpa/src/crypto/sha512-kdf.c b/contrib/wpa/src/crypto/sha512-kdf.c new file mode 100644 index 0000000..8b71f9b --- /dev/null +++ b/contrib/wpa/src/crypto/sha512-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" + + +/** + * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA512_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA512_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA512_MAC_LEN) + clen = SHA512_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA512_MAC_LEN); + return 0; +} diff --git a/contrib/wpa/src/crypto/sha512-prf.c b/contrib/wpa/src/crypto/sha512-prf.c new file mode 100644 index 0000000..3b2ad88 --- /dev/null +++ b/contrib/wpa/src/crypto/sha512-prf.c @@ -0,0 +1,108 @@ +/* + * SHA512-based KDF (IEEE 802.11ac) + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" +#include "crypto.h" + + +/** + * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2) + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + return sha512_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); +} + + +/** + * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA512_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA512_MAC_LEN) { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; + pos += SHA512_MAC_LEN; + } else { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; + os_memcpy(&buf[pos], hash, plen); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } + + os_memset(hash, 0, sizeof(hash)); + + return 0; +} diff --git a/contrib/wpa/src/crypto/sha512.c b/contrib/wpa/src/crypto/sha512.c new file mode 100644 index 0000000..66311c3 --- /dev/null +++ b/contrib/wpa/src/crypto/sha512.c @@ -0,0 +1,104 @@ +/* + * SHA-512 hash implementation and interface functions + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" +#include "crypto.h" + + +/** + * hmac_sha512_vector - HMAC-SHA512 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */ + unsigned char tk[64]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 128 bytes reset it to key = SHA512(key) */ + if (key_len > 128) { + if (sha512_vector(1, &key, &key_len, tk) < 0) + return -1; + key = tk; + key_len = 64; + } + + /* the HMAC_SHA512 transform looks like: + * + * SHA512(K XOR opad, SHA512(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 128 times + * opad is the byte 0x5c repeated 128 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha512_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + _addr[1] = mac; + _len[1] = SHA512_MAC_LEN; + return sha512_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha512 - HMAC-SHA512 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/contrib/wpa/src/crypto/sha512.h b/contrib/wpa/src/crypto/sha512.h new file mode 100644 index 0000000..8e64c8b --- /dev/null +++ b/contrib/wpa/src/crypto/sha512.h @@ -0,0 +1,27 @@ +/* + * SHA512 hash implementation and interface functions + * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA512_H +#define SHA512_H + +#define SHA512_MAC_LEN 64 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); + +#endif /* SHA512_H */ diff --git a/contrib/wpa/src/crypto/sha512_i.h b/contrib/wpa/src/crypto/sha512_i.h new file mode 100644 index 0000000..1089589 --- /dev/null +++ b/contrib/wpa/src/crypto/sha512_i.h @@ -0,0 +1,25 @@ +/* + * SHA-512 internal definitions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA512_I_H +#define SHA512_I_H + +#define SHA512_BLOCK_SIZE 128 + +struct sha512_state { + u64 length, state[8]; + u32 curlen; + u8 buf[SHA512_BLOCK_SIZE]; +}; + +void sha512_init(struct sha512_state *md); +int sha512_process(struct sha512_state *md, const unsigned char *in, + unsigned long inlen); +int sha512_done(struct sha512_state *md, unsigned char *out); + +#endif /* SHA512_I_H */ diff --git a/contrib/wpa/src/crypto/tls.h b/contrib/wpa/src/crypto/tls.h index 2e56233..8bdb91f 100644 --- a/contrib/wpa/src/crypto/tls.h +++ b/contrib/wpa/src/crypto/tls.h @@ -41,6 +41,8 @@ enum tls_fail_reason { TLS_FAIL_SERVER_CHAIN_PROBE = 8, TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, + TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, + TLS_FAIL_DN_MISMATCH = 12, }; @@ -63,6 +65,7 @@ union tls_event_data { size_t hash_len; const char *altsubject[TLS_MAX_ALT_SUBJECT]; int num_altsubject; + const char *serial_num; } peer_cert; struct { @@ -80,6 +83,8 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; + unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -95,6 +100,14 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) #define TLS_CONN_EAP_FAST BIT(7) #define TLS_CONN_DISABLE_TLSv1_0 BIT(8) +#define TLS_CONN_EXT_CERT_CHECK BIT(9) +#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10) +#define TLS_CONN_SUITEB BIT(11) +#define TLS_CONN_SUITEB_NO_ECDH BIT(12) +#define TLS_CONN_DISABLE_TLSv1_3 BIT(13) +#define TLS_CONN_ENABLE_TLSv1_0 BIT(14) +#define TLS_CONN_ENABLE_TLSv1_1 BIT(15) +#define TLS_CONN_ENABLE_TLSv1_2 BIT(16) /** * struct tls_connection_params - Parameters for TLS connection @@ -107,12 +120,19 @@ struct tls_config { * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects - * @suffix_match: String to suffix match in the dNSName or CN of the peer - * certificate or %NULL to allow all domain names. This may allow subdomains an - * wildcard certificates. Each domain name label must have a full match. + * @suffix_match: Semicolon deliminated string of values to suffix match against + * the dNSName or CN of the peer certificate or %NULL to allow all domain names. + * This may allow subdomains and wildcard certificates. Each domain name label + * must have a full case-insensitive match. * @domain_match: String to match in the dNSName or CN of the peer * certificate or %NULL to allow all domain names. This requires a full, * case-insensitive match. + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used @@ -136,9 +156,15 @@ struct tls_config { * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine * @openssl_ciphers: OpenSSL cipher configuration + * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if + * supported, empty string to disable, or a colon-separated curve list. * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled + * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling + * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if + * ocsp_multi is not enabled + * @check_cert_subject: Client certificate subject name matching string * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -176,9 +202,12 @@ struct tls_connection_params { const char *cert_id; const char *ca_cert_id; const char *openssl_ciphers; + const char *openssl_ecdh_curves; unsigned int flags; const char *ocsp_stapling_response; + const char *ocsp_stapling_response_multi; + const char *check_cert_subject; }; @@ -242,6 +271,18 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); int tls_connection_established(void *tls_ctx, struct tls_connection *conn); /** + * tls_connection_peer_serial_num - Fetch peer certificate serial number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Allocated string buffer containing the peer certificate serial + * number or %NULL on error. + * + * The caller is responsible for freeing the returned buffer with os_free(). + */ +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn); + +/** * tls_connection_shutdown - Shutdown TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() @@ -297,9 +338,11 @@ int __must_check tls_global_set_params( * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates + * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors * Returns: 0 on success, -1 on failure */ -int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, + int strict); /** * tls_connection_set_verify - Set certificate verification options @@ -330,29 +373,42 @@ int __must_check tls_connection_get_random(void *tls_ctx, struct tls_random *data); /** - * tls_connection_prf - Use TLS-PRF to derive keying material + * tls_connection_export_key - Derive keying material from a TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @label: Label (e.g., description of the key) for PRF - * @server_random_first: seed is 0 = client_random|server_random, - * 1 = server_random|client_random - * @skip_keyblock: Skip TLS key block from the beginning of PRF output + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + * + * Exports keying material using the mechanism described in RFC 5705. If + * context is %NULL, context is not provided; otherwise, context is provided + * (including the case of empty context with context_len == 0). + */ +int __must_check tls_connection_export_key(void *tls_ctx, + struct tls_connection *conn, + const char *label, + const u8 *context, + size_t context_len, + u8 *out, size_t out_len); + +/** + * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * tls_connection_prf() is required so that further keying material can be - * derived from the master secret. Example implementation of this function is in - * tls_prf_sha1_md5() when it is called with seed set to - * client_random|server_random (or server_random|client_random). For TLSv1.2 and - * newer, a different PRF is needed, though. + * Exports key material after the normal TLS key block for use with + * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST + * uses a different legacy mechanism. */ -int __must_check tls_connection_prf(void *tls_ctx, - struct tls_connection *conn, - const char *label, - int server_random_first, - int skip_keyblock, - u8 *out, size_t out_len); +int __must_check tls_connection_get_eap_fast_key(void *tls_ctx, + struct tls_connection *conn, + u8 *out, size_t out_len); /** * tls_connection_handshake - Process TLS handshake (client side) @@ -455,7 +511,9 @@ enum { TLS_CIPHER_RC4_SHA /* 0x0005 */, TLS_CIPHER_AES128_SHA /* 0x002f */, TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, - TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */, + TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */, + TLS_CIPHER_AES256_SHA /* 0x0035 */, }; /** diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c index f994379..daa01d9 100644 --- a/contrib/wpa/src/crypto/tls_gnutls.c +++ b/contrib/wpa/src/crypto/tls_gnutls.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for GnuTLS - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -37,6 +37,8 @@ struct tls_global { union tls_event_data *data); void *cb_ctx; int cert_in_cb; + + char *ocsp_stapling_response; }; struct tls_connection { @@ -133,6 +135,7 @@ void tls_deinit(void *ssl_ctx) if (global->params_set) gnutls_certificate_free_credentials(global->xcred); os_free(global->session_data); + os_free(global->ocsp_stapling_response); os_free(global); } @@ -292,6 +295,14 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { struct tls_global *global = ssl_ctx; @@ -343,10 +354,25 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { int ret; + const char *err; + char prio_buf[100]; + const char *prio = NULL; if (conn == NULL || params == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "GnuTLS: ocsp=3 not supported"); + return -1; + } + + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { + wpa_printf(MSG_INFO, + "GnuTLS: tls_ext_cert_check=1 not supported"); + return -1; + } + if (params->subject_match) { wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported"); return -1; @@ -382,12 +408,66 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, conn->flags = params->flags; + if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1 | + TLS_CONN_DISABLE_TLSv1_2)) { + os_snprintf(prio_buf, sizeof(prio_buf), + "NORMAL:-VERS-SSL3.0%s%s%s", + params->flags & TLS_CONN_DISABLE_TLSv1_0 ? + ":-VERS-TLS1.0" : "", + params->flags & TLS_CONN_DISABLE_TLSv1_1 ? + ":-VERS-TLS1.1" : "", + params->flags & TLS_CONN_DISABLE_TLSv1_2 ? + ":-VERS-TLS1.2" : ""); + prio = prio_buf; + } + if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) { + prio = "SUITEB128"; + } else if (os_strcmp(params->openssl_ciphers, + "SUITEB192") == 0) { + prio = "SUITEB192"; + } else if ((params->flags & TLS_CONN_SUITEB) && + os_strcmp(params->openssl_ciphers, + "ECDHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else if (os_strcmp(params->openssl_ciphers, + "ECDHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else if (os_strcmp(params->openssl_ciphers, + "DHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH"; + } else if (os_strcmp(params->openssl_ciphers, + "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else { + wpa_printf(MSG_INFO, + "GnuTLS: openssl_ciphers not supported"); + return -1; + } + } else if (params->flags & TLS_CONN_SUITEB) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH"; + } + + if (prio) { + wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio); + ret = gnutls_priority_set_direct(conn->session, prio, &err); + if (ret < 0) { + wpa_printf(MSG_ERROR, + "GnuTLS: Priority string failure at '%s'", + err); + return -1; + } + } + + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "GnuTLS: openssl_ecdh_curves not supported"); return -1; } - /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ if (params->ca_cert) { @@ -410,6 +490,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); return -1; } + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read CA cert '%s' in PEM format", + params->ca_cert); + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read CA cert '%s' in DER format", + params->ca_cert); } } else if (params->ca_cert_blob) { gnutls_datum_t ca; @@ -457,6 +544,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (params->client_cert && params->private_key) { + wpa_printf(MSG_DEBUG, + "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format", + params->client_cert, params->private_key); #if GNUTLS_VERSION_NUMBER >= 0x03010b ret = gnutls_certificate_set_x509_key_file2( conn->xcred, params->client_cert, params->private_key, @@ -468,8 +558,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, GNUTLS_X509_FMT_DER); #endif if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in DER format: %s", gnutls_strerror(ret)); + wpa_printf(MSG_DEBUG, + "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format", + gnutls_strerror(ret)); #if GNUTLS_VERSION_NUMBER >= 0x03010b ret = gnutls_certificate_set_x509_key_file2( conn->xcred, params->client_cert, @@ -486,11 +577,19 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); return ret; } + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read client cert/key in PEM format"); + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read client cert/key in DER format"); } } else if (params->private_key) { int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ + wpa_printf(MSG_DEBUG, + "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format", + params->private_key); ret = gnutls_certificate_set_x509_simple_pkcs12_file( conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); @@ -596,12 +695,53 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } +#if GNUTLS_VERSION_NUMBER >= 0x030103 +static int server_ocsp_status_req(gnutls_session_t session, void *ptr, + gnutls_datum_t *resp) +{ + struct tls_global *global = ptr; + char *cached; + size_t len; + + if (!global->ocsp_stapling_response) { + wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured"); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + cached = os_readfile(global->ocsp_stapling_response, &len); + if (!cached) { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - could not read response file (%s)", + global->ocsp_stapling_response); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - send cached response"); + resp->data = gnutls_malloc(len); + if (!resp->data) { + os_free(resp); + return GNUTLS_E_MEMORY_ERROR; + } + + os_memcpy(resp->data, cached, len); + resp->size = len; + os_free(cached); + + return GNUTLS_E_SUCCESS; +} +#endif /* 3.1.3 */ + + int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { struct tls_global *global = tls_ctx; int ret; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -690,6 +830,17 @@ int tls_global_set_params(void *tls_ctx, } } +#if GNUTLS_VERSION_NUMBER >= 0x030103 + os_free(global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + global->ocsp_stapling_response = NULL; + gnutls_certificate_set_ocsp_status_request_function( + global->xcred, server_ocsp_status_req, global); +#endif /* 3.1.3 */ + global->params_set = 1; return 0; @@ -700,7 +851,7 @@ fail: } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { /* TODO */ return 0; @@ -746,15 +897,31 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { - if (conn == NULL || conn->session == NULL || skip_keyblock) + if (conn == NULL || conn->session == NULL) return -1; +#if GNUTLS_VERSION_NUMBER >= 0x030404 + return gnutls_prf_rfc5705(conn->session, os_strlen(label), label, + context_len, (const char *) context, + out_len, (char *) out); +#else /* 3.4.4 */ + if (context) + return -1; return gnutls_prf(conn->session, os_strlen(label), label, - server_random_first, 0, NULL, out_len, (char *) out); + 0 /* client_random first */, 0, NULL, out_len, + (char *) out); +#endif /* 3.4.4 */ +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ + return -1; } @@ -919,6 +1086,52 @@ ocsp_error: } +static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match, + int full) +{ + int res = -1; + +#if GNUTLS_VERSION_NUMBER >= 0x030300 + if (full) + res = gnutls_x509_crt_check_hostname2( + cert, match, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); +#endif /* >= 3.3.0 */ + if (res == -1) + res = gnutls_x509_crt_check_hostname(cert, match); + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d", + full ? "": "suffix ", match, res); + return res; +} + + +static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match, + int full) +{ + char *values, *token, *context = NULL; + int ret = 0; + + if (!os_strchr(match, ';')) + return tls_match_suffix_helper(cert, match, full); + + values = os_strdup(match); + if (!values) + return 0; + + /* Process each match alternative separately until a match is found */ + while ((token = str_token(values, ";", &context))) { + if (tls_match_suffix_helper(cert, token, full)) { + ret = 1; + break; + } + } + + os_free(values); + return ret; +} + + static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; @@ -1114,8 +1327,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) if (i == 0) { if (conn->suffix_match && - !gnutls_x509_crt_check_hostname( - cert, conn->suffix_match)) { + !tls_match_suffix(cert, conn->suffix_match, 0)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); @@ -1131,9 +1343,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && - !gnutls_x509_crt_check_hostname2( - cert, conn->domain_match, - GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { + !tls_match_suffix(cert, conn->domain_match, 1)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); @@ -1262,6 +1472,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, ret = gnutls_handshake(conn->session); if (ret < 0) { gnutls_alert_description_t alert; + union tls_event_data ev; switch (ret) { case GNUTLS_E_AGAIN: @@ -1272,14 +1483,29 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, conn->push_buf = wpabuf_alloc(0); } break; + case GNUTLS_E_DH_PRIME_UNACCEPTABLE: + wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime"); + if (conn->global->event_cb) { + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 1; + ev.alert.type = "fatal"; + ev.alert.description = "insufficient security"; + conn->global->event_cb(conn->global->cb_ctx, + TLS_ALERT, &ev); + } + /* + * Could send a TLS Alert to the server, but for now, + * simply terminate handshake. + */ + conn->failed++; + conn->write_alerts++; + break; case GNUTLS_E_FATAL_ALERT_RECEIVED: alert = gnutls_alert_get(conn->session); wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", __func__, gnutls_alert_get_name(alert)); conn->read_alerts++; if (conn->global->event_cb != NULL) { - union tls_event_data ev; - os_memset(&ev, 0, sizeof(ev)); ev.alert.is_local = 0; ev.alert.type = gnutls_alert_get_name(alert); @@ -1430,16 +1656,53 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_version(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ - return -1; + gnutls_protocol_t ver; + + ver = gnutls_protocol_get_version(conn->session); + if (ver == GNUTLS_TLS1_0) + os_strlcpy(buf, "TLSv1", buflen); + else if (ver == GNUTLS_TLS1_1) + os_strlcpy(buf, "TLSv1.1", buflen); + else if (ver == GNUTLS_TLS1_2) + os_strlcpy(buf, "TLSv1.2", buflen); + else + return -1; + return 0; } int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ - buf[0] = '\0'; + gnutls_cipher_algorithm_t cipher; + gnutls_kx_algorithm_t kx; + gnutls_mac_algorithm_t mac; + const char *kx_str, *cipher_str, *mac_str; + int res; + + cipher = gnutls_cipher_get(conn->session); + cipher_str = gnutls_cipher_get_name(cipher); + if (!cipher_str) + cipher_str = ""; + + kx = gnutls_kx_get(conn->session); + kx_str = gnutls_kx_get_name(kx); + if (!kx_str) + kx_str = ""; + + mac = gnutls_mac_get(conn->session); + mac_str = gnutls_mac_get_name(mac); + if (!mac_str) + mac_str = ""; + + if (kx == GNUTLS_KX_RSA) + res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str); + else + res = os_snprintf(buf, buflen, "%s-%s-%s", + kx_str, cipher_str, mac_str); + if (os_snprintf_error(buflen, res)) + return -1; + return 0; } diff --git a/contrib/wpa/src/crypto/tls_internal.c b/contrib/wpa/src/crypto/tls_internal.c index 704751d..8095b43 100644 --- a/contrib/wpa/src/crypto/tls_internal.c +++ b/contrib/wpa/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,6 +23,11 @@ struct tls_global { int server; struct tlsv1_credentials *server_cred; int check_crl; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; }; struct tls_connection { @@ -51,6 +56,11 @@ void * tls_init(const struct tls_config *conf) global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; + if (conf) { + global->event_cb = conf->event_cb; + global->cb_ctx = conf->cb_ctx; + global->cert_in_cb = conf->cert_in_cb; + } return global; } @@ -64,10 +74,12 @@ void tls_deinit(void *ssl_ctx) tlsv1_client_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER - tlsv1_cred_free(global->server_cred); tlsv1_server_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_SERVER */ } +#ifdef CONFIG_TLS_INTERNAL_SERVER + tlsv1_cred_free(global->server_cred); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ os_free(global); } @@ -95,6 +107,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx) os_free(conn); return NULL; } + tlsv1_client_set_cb(conn->client, global->event_cb, + global->cb_ctx, global->cert_in_cb); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER @@ -163,6 +177,14 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT @@ -186,6 +208,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn->client == NULL) return -1; + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { + wpa_printf(MSG_INFO, + "TLS: tls_ext_cert_check=1 not supported"); + return -1; + } + cred = tlsv1_cred_alloc(); if (cred == NULL) return -1; @@ -220,6 +248,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported"); + tlsv1_cred_free(cred); + return -1; + } + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { @@ -259,8 +293,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - tlsv1_client_set_time_checks( - conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + tlsv1_client_set_flags(conn->client, params->flags); return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ @@ -276,6 +309,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; struct tlsv1_credentials *cred; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -312,6 +348,13 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->ocsp_stapling_response) + cred->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + if (params->ocsp_stapling_response_multi) + cred->ocsp_stapling_response_multi = + os_strdup(params->ocsp_stapling_response_multi); + return 0; #else /* CONFIG_TLS_INTERNAL_SERVER */ return -1; @@ -319,7 +362,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { struct tls_global *global = tls_ctx; global->check_crl = check_crl; @@ -368,9 +411,10 @@ static int tls_get_keyblock_size(struct tls_connection *conn) } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, const u8 *context, + size_t context_len, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len) { int ret = -1, skip = 0; u8 *tmp_out = NULL; @@ -388,16 +432,16 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - ret = tlsv1_client_prf(conn->client, label, - server_random_first, - _out, out_len); + ret = tlsv1_client_prf(conn->client, label, context, + context_len, server_random_first, + _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - ret = tlsv1_server_prf(conn->server, label, - server_random_first, - _out, out_len); + ret = tlsv1_server_prf(conn->server, label, context, + context_len, server_random_first, + _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ if (ret == 0 && skip_keyblock) @@ -408,6 +452,23 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) +{ + return tls_connection_prf(tls_ctx, conn, label, context, context_len, + 0, 0, out, out_len); +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ + return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0, + 1, 1, out, out_len); +} + + struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, @@ -621,7 +682,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_version(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ + if (conn == NULL) + return -1; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_version(conn->client, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; } @@ -666,12 +732,20 @@ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_failed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_read_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } @@ -679,6 +753,10 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_write_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } diff --git a/contrib/wpa/src/crypto/tls_none.c b/contrib/wpa/src/crypto/tls_none.c index ae392ad..6d6fb0c 100644 --- a/contrib/wpa/src/crypto/tls_none.c +++ b/contrib/wpa/src/crypto/tls_none.c @@ -45,6 +45,13 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + return NULL; +} + + int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { return -1; @@ -65,7 +72,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { return -1; } @@ -86,9 +93,16 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) +{ + return -1; +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) { return -1; } diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c index 8b7b47b..b0c23ae 100644 --- a/contrib/wpa/src/crypto/tls_openssl.c +++ b/contrib/wpa/src/crypto/tls_openssl.c @@ -18,6 +18,7 @@ #include <openssl/ssl.h> #include <openssl/err.h> +#include <openssl/opensslv.h> #include <openssl/pkcs12.h> #include <openssl/x509v3.h> #ifndef OPENSSL_NO_ENGINE @@ -35,12 +36,12 @@ #include "sha1.h" #include "sha256.h" #include "tls.h" +#include "tls_openssl.h" -#if OPENSSL_VERSION_NUMBER < 0x10000000L -/* ERR_remove_thread_state replaces ERR_remove_state and the latter is - * deprecated. However, OpenSSL 0.9.8 doesn't include - * ERR_remove_thread_state. */ -#define ERR_remove_thread_state(tid) ERR_remove_state(0) +#if !defined(CONFIG_FIPS) && \ + (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST)) +#define OPENSSL_NEED_EAP_FAST_PRF #endif #if defined(OPENSSL_IS_BORINGSSL) @@ -57,6 +58,69 @@ typedef int stack_index_t; #endif /* OPENSSL_NO_TLSEXT */ #endif /* SSL_set_tlsext_status_type */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \ + !defined(BORINGSSL_API_VERSION) +/* + * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL + * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for + * older versions. + */ + +static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +#ifdef OPENSSL_NEED_EAP_FAST_PRF +static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, + unsigned char *out, size_t outlen) +{ + if (!session || session->master_key_length < 0 || + (size_t) session->master_key_length > outlen) + return 0; + if ((size_t) session->master_key_length < outlen) + outlen = session->master_key_length; + os_memcpy(out, session->master_key, outlen); + return outlen; +} +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ + +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) +#ifdef CONFIG_SUITEB +static int RSA_bits(const RSA *r) +{ + return BN_num_bits(r->n); +} +#endif /* CONFIG_SUITEB */ + + +static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *) x); +} +#endif + #ifdef ANDROID #include <openssl/pem.h> #include <keystore/keystore_get.h> @@ -71,6 +135,66 @@ static BIO * BIO_from_keystore(const char *key) free(value); return bio; } + + +static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias) +{ + BIO *bio = BIO_from_keystore(key_alias); + STACK_OF(X509_INFO) *stack = NULL; + stack_index_t i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + + if (!stack) { + wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s", + key_alias); + return -1; + } + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + + if (info->x509) + X509_STORE_add_cert(ctx, info->x509); + if (info->crl) + X509_STORE_add_crl(ctx, info->crl); + } + + sk_X509_INFO_pop_free(stack, X509_INFO_free); + + return 0; +} + + +static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx, + const char *encoded_key_alias) +{ + int rc = -1; + int len = os_strlen(encoded_key_alias); + unsigned char *decoded_alias; + + if (len & 1) { + wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s", + encoded_key_alias); + return rc; + } + + decoded_alias = os_malloc(len / 2 + 1); + if (decoded_alias) { + if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) { + decoded_alias[len / 2] = '\0'; + rc = tls_add_ca_from_keystore( + ctx, (const char *) decoded_alias); + } + os_free(decoded_alias); + } + + return rc; +} + #endif /* ANDROID */ static int tls_openssl_ref_count = 0; @@ -90,18 +214,26 @@ static struct tls_context *tls_global = NULL; struct tls_data { SSL_CTX *ssl; unsigned int tls_session_lifetime; + int check_crl; + int check_crl_strict; + char *ca_cert; + unsigned int crl_reload_interval; + struct os_reltime crl_last_reload; + char *check_cert_subject; }; struct tls_connection { struct tls_context *context; + struct tls_data *data; SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ char *subject_match, *altsubject_match, *suffix_match, *domain_match; + char *check_cert_subject; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -116,6 +248,8 @@ struct tls_connection { unsigned int server_cert_only:1; unsigned int invalid_hb_used:1; unsigned int success_data:1; + unsigned int client_hello_generated:1; + unsigned int server:1; u8 srv_cert_hash[32]; @@ -125,10 +259,11 @@ struct tls_connection { X509 *peer_issuer; X509 *peer_issuer_issuer; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char server_random[SSL3_RANDOM_SIZE]; -#endif + + u16 cipher_suite; + int server_dh_prime_len; }; @@ -176,6 +311,36 @@ static void tls_show_errors(int level, const char *func, const char *txt) #endif /* CONFIG_NO_STDOUT_DEBUG */ +static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl) +{ + int flags; + X509_STORE *store; + + store = X509_STORE_new(); + if (!store) { + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - failed to allocate new certificate store", + __func__); + return NULL; + } + + if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + X509_STORE_free(store); + return NULL; + } + + flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + + X509_STORE_set_flags(store, flags); + + return store; +} + + #ifdef CONFIG_NATIVE_WINDOWS /* Windows CryptoAPI and access to certificate stores */ @@ -526,7 +691,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " "system certificate store: subject='%s'", buf); - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), + cert)) { tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert to OpenSSL " "certificate store"); @@ -624,10 +790,16 @@ static int tls_engine_load_dynamic_generic(const char *pre[], engine = ENGINE_by_id(id); if (engine) { - ENGINE_free(engine); wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " "available", id); - return 0; + /* + * If it was auto-loaded by ENGINE_by_id() we might still + * need to tell it which PKCS#11 module to use in legacy + * (non-p11-kit) environments. Do so now; even if it was + * properly initialised before, setting it again will be + * harmless. + */ + goto found; } ERR_clear_error(); @@ -664,7 +836,7 @@ static int tls_engine_load_dynamic_generic(const char *pre[], id, ERR_error_string(ERR_get_error(), NULL)); return -1; } - + found: while (post && post[0]) { wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { @@ -808,6 +980,9 @@ void * tls_init(const struct tls_config *conf) } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) SSL_load_error_strings(); SSL_library_init(); #ifndef OPENSSL_NO_SHA256 @@ -829,6 +1004,7 @@ void * tls_init(const struct tls_config *conf) #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ +#endif /* < 1.1.0 */ } else { context = tls_context_new(conf); if (context == NULL) @@ -849,15 +1025,26 @@ void * tls_init(const struct tls_config *conf) os_free(tls_global); tls_global = NULL; } + os_free(data); return NULL; } data->ssl = ssl; - if (conf) + if (conf) { data->tls_session_lifetime = conf->tls_session_lifetime; + data->crl_reload_interval = conf->crl_reload_interval; + } SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); +#ifdef SSL_MODE_NO_AUTO_CHAIN + /* Number of deployed use cases assume the default OpenSSL behavior of + * auto chaining the local certificate is in use. BoringSSL removed this + * functionality by default, so we need to restore it here to avoid + * breaking existing use cases. */ + SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN); +#endif /* SSL_MODE_NO_AUTO_CHAIN */ + SSL_CTX_set_info_callback(ssl, ssl_info_cb); SSL_CTX_set_app_data(ssl, context); if (data->tls_session_lifetime > 0) { @@ -885,8 +1072,10 @@ void * tls_init(const struct tls_config *conf) #ifndef OPENSSL_NO_ENGINE wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); +#if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_load_ENGINE_strings(); ENGINE_load_dynamic(); +#endif /* OPENSSL_VERSION_NUMBER */ if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || @@ -903,7 +1092,7 @@ void * tls_init(const struct tls_config *conf) if (conf && conf->openssl_ciphers) ciphers = conf->openssl_ciphers; else - ciphers = "DEFAULT:!EXP:!LOW"; + ciphers = TLS_DEFAULT_CIPHERS; if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: Failed to set cipher string '%s'", @@ -925,10 +1114,14 @@ void tls_deinit(void *ssl_ctx) os_free(context); if (data->tls_session_lifetime > 0) SSL_CTX_flush_sessions(ssl, 0); + os_free(data->ca_cert); SSL_CTX_free(ssl); tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ @@ -936,12 +1129,14 @@ void tls_deinit(void *ssl_ctx) ERR_remove_thread_state(NULL); ERR_free_strings(); EVP_cleanup(); +#endif /* < 1.1.0 */ os_free(tls_global->ocsp_stapling_response); tls_global->ocsp_stapling_response = NULL; os_free(tls_global); tls_global = NULL; } + os_free(data->check_cert_subject); os_free(data); } @@ -967,10 +1162,32 @@ static int tls_is_pin_error(unsigned int err) #endif /* OPENSSL_NO_ENGINE */ +#ifdef ANDROID +/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */ +EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id); +#endif /* ANDROID */ + static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, const char *cert_id, const char *ca_cert_id) { +#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL) +#if !defined(OPENSSL_NO_ENGINE) +#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL." +#endif + if (!key_id) + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + conn->engine = NULL; + conn->private_key = EVP_PKEY_from_keystore(key_id); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, + "ENGINE: cannot load private key with id '%s' [%s]", + key_id, + ERR_error_string(ERR_get_error(), NULL)); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } +#endif /* ANDROID && OPENSSL_IS_BORINGSSL */ + #ifndef OPENSSL_NO_ENGINE int ret = -1; if (engine_id == NULL) { @@ -1068,17 +1285,19 @@ err: static void tls_engine_deinit(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); if (conn->private_key) { EVP_PKEY_free(conn->private_key); conn->private_key = NULL; } if (conn->engine) { +#if !defined(OPENSSL_IS_BORINGSSL) ENGINE_finish(conn->engine); +#endif /* !OPENSSL_IS_BORINGSSL */ conn->engine = NULL; } -#endif /* OPENSSL_NO_ENGINE */ +#endif /* ANDROID || !OPENSSL_NO_ENGINE */ } @@ -1097,14 +1316,186 @@ int tls_get_errors(void *ssl_ctx) } +static const char * openssl_content_type(int content_type) +{ + switch (content_type) { + case 20: + return "change cipher spec"; + case 21: + return "alert"; + case 22: + return "handshake"; + case 23: + return "application data"; + case 24: + return "heartbeat"; + case 256: + return "TLS header info"; /* pseudo content type */ + default: + return "?"; + } +} + + +static const char * openssl_handshake_type(int content_type, const u8 *buf, + size_t len) +{ + if (content_type != 22 || !buf || len == 0) + return ""; + switch (buf[0]) { + case 0: + return "hello request"; + case 1: + return "client hello"; + case 2: + return "server hello"; + case 3: + return "hello verify request"; + case 4: + return "new session ticket"; + case 5: + return "end of early data"; + case 6: + return "hello retry request"; + case 8: + return "encrypted extensions"; + case 11: + return "certificate"; + case 12: + return "server key exchange"; + case 13: + return "certificate request"; + case 14: + return "server hello done"; + case 15: + return "certificate verify"; + case 16: + return "client key exchange"; + case 20: + return "finished"; + case 21: + return "certificate url"; + case 22: + return "certificate status"; + case 23: + return "supplemental data"; + case 24: + return "key update"; + case 254: + return "message hash"; + default: + return "?"; + } +} + + +#ifdef CONFIG_SUITEB + +static void check_server_hello(struct tls_connection *conn, + const u8 *pos, const u8 *end) +{ + size_t payload_len, id_len; + + /* + * Parse ServerHello to get the selected cipher suite since OpenSSL does + * not make it cleanly available during handshake and we need to know + * whether DHE was selected. + */ + + if (end - pos < 3) + return; + payload_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < payload_len) + return; + end = pos + payload_len; + + /* Skip Version and Random */ + if (end - pos < 2 + SSL3_RANDOM_SIZE) + return; + pos += 2 + SSL3_RANDOM_SIZE; + + /* Skip Session ID */ + if (end - pos < 1) + return; + id_len = *pos++; + if ((size_t) (end - pos) < id_len) + return; + pos += id_len; + + if (end - pos < 2) + return; + conn->cipher_suite = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x", + conn->cipher_suite); +} + + +static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn, + const u8 *pos, const u8 *end) +{ + size_t payload_len; + u16 dh_len; + BIGNUM *p; + int bits; + + if (!(conn->flags & TLS_CONN_SUITEB)) + return; + + /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */ + if (conn->cipher_suite != 0x9f) + return; + + if (end - pos < 3) + return; + payload_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < payload_len) + return; + end = pos + payload_len; + + if (end - pos < 2) + return; + dh_len = WPA_GET_BE16(pos); + pos += 2; + + if ((size_t) (end - pos) < dh_len) + return; + p = BN_bin2bn(pos, dh_len, NULL); + if (!p) + return; + + bits = BN_num_bits(p); + BN_free(p); + + conn->server_dh_prime_len = bits; + wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits", + conn->server_dh_prime_len); +} + +#endif /* CONFIG_SUITEB */ + + static void tls_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) { struct tls_connection *conn = arg; const u8 *pos = buf; - wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d", - write_p ? "TX" : "RX", version, content_type); + if (write_p == 2) { + wpa_printf(MSG_DEBUG, + "OpenSSL: session ver=0x%x content_type=%d", + version, content_type); + wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len); + return; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)", + write_p ? "TX" : "RX", version, content_type, + openssl_content_type(content_type), + openssl_handshake_type(content_type, buf, len)); wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len); if (content_type == 24 && len >= 3 && pos[0] == 1) { size_t payload_len = WPA_GET_BE16(pos + 1); @@ -1113,6 +1504,18 @@ static void tls_msg_cb(int write_p, int version, int content_type, conn->invalid_hb_used = 1; } } + +#ifdef CONFIG_SUITEB + /* + * Need to parse these handshake messages to be able to check DH prime + * length since OpenSSL does not expose the new cipher suite and DH + * parameters during handshake (e.g., for cert_cb() callback). + */ + if (content_type == 22 && pos && len > 0 && pos[0] == 2) + check_server_hello(conn, pos + 1, pos + len); + if (content_type == 22 && pos && len > 0 && pos[0] == 12) + check_server_key_exchange(ssl, conn, pos + 1, pos + len); +#endif /* CONFIG_SUITEB */ } @@ -1122,11 +1525,32 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; + X509_STORE *new_cert_store; + struct os_reltime now; struct tls_context *context = SSL_CTX_get_app_data(ssl); + /* Replace X509 store if it is time to update CRL. */ + if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 && + os_reltime_expired(&now, &data->crl_last_reload, + data->crl_reload_interval)) { + wpa_printf(MSG_INFO, + "OpenSSL: Flushing X509 store with ca_cert file"); + new_cert_store = tls_crl_cert_reload(data->ca_cert, + data->check_crl); + if (!new_cert_store) { + wpa_printf(MSG_ERROR, + "OpenSSL: Error replacing X509 store with ca_cert file"); + } else { + /* Replace old store */ + SSL_CTX_set_cert_store(ssl, new_cert_store); + data->crl_last_reload = now; + } + } + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->data = data; conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { @@ -1190,6 +1614,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) os_free(conn->altsubject_match); os_free(conn->suffix_match); os_free(conn->domain_match); + os_free(conn->check_cert_subject); os_free(conn->session_ticket); os_free(conn); } @@ -1201,6 +1626,31 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + ASN1_INTEGER *ser; + char *serial_num; + size_t len; + + if (!conn->peer_cert) + return NULL; + + ser = X509_get_serialNumber(conn->peer_cert); + if (!ser) + return NULL; + + len = ASN1_STRING_length(ser) * 2 + 1; + serial_num = os_malloc(len); + if (!serial_num) + return NULL; + wpa_snprintf_hex_uppercase(serial_num, len, + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + return serial_num; +} + + int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) @@ -1234,6 +1684,8 @@ static int tls_match_altsubject_component(X509 *cert, int type, found++; } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); + return found; } @@ -1283,9 +1735,9 @@ static int tls_match_altsubject(X509 *cert, const char *match) #ifndef CONFIG_NATIVE_WINDOWS static int domain_suffix_match(const u8 *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -1295,7 +1747,6 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -1315,12 +1766,223 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, #endif /* CONFIG_NATIVE_WINDOWS */ -static int tls_match_suffix(X509 *cert, const char *match, int full) +struct tls_dn_field_order_cnt { + u8 cn; + u8 c; + u8 l; + u8 st; + u8 o; + u8 ou; + u8 email; +}; + + +static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt, + int nid) +{ + switch (nid) { + case NID_commonName: + return dn_cnt->cn; + case NID_countryName: + return dn_cnt->c; + case NID_localityName: + return dn_cnt->l; + case NID_stateOrProvinceName: + return dn_cnt->st; + case NID_organizationName: + return dn_cnt->o; + case NID_organizationalUnitName: + return dn_cnt->ou; + case NID_pkcs9_emailAddress: + return dn_cnt->email; + default: + wpa_printf(MSG_ERROR, + "TLS: Unknown NID '%d' in check_cert_subject", + nid); + return -1; + } +} + + +/** + * match_dn_field - Match configuration DN field against Certificate DN field + * @cert: Certificate + * @nid: NID of DN field + * @field: Field name + * @value DN field value which is passed from configuration + * e.g., if configuration have C=US and this argument will point to US. + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int match_dn_field(const X509 *cert, int nid, const char *field, + const char *value, + const struct tls_dn_field_order_cnt *dn_cnt) +{ + int i, ret = 0, len, config_dn_field_index, match_index = 0; + X509_NAME *name; + + len = os_strlen(value); + name = X509_get_subject_name((X509 *) cert); + + /* Assign incremented cnt for every field of DN to check DN field in + * right order */ + config_dn_field_index = get_dn_field_index(dn_cnt, nid); + if (config_dn_field_index < 0) + return 0; + + /* Fetch value based on NID */ + for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + e = X509_NAME_get_entry(name, i); + if (!e) + continue; + + cn = X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + + match_index++; + + /* check for more than one DN field with same name */ + if (match_index != config_dn_field_index) + continue; + + /* Check wildcard at the right end side */ + /* E.g., if OU=develop* mentioned in configuration, allow 'OU' + * of the subject in the client certificate to start with + * 'develop' */ + if (len > 0 && value[len - 1] == '*') { + /* Compare actual certificate DN field value with + * configuration DN field value up to the specified + * length. */ + ret = ASN1_STRING_length(cn) >= len - 1 && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len - 1) == 0; + } else { + /* Compare actual certificate DN field value with + * configuration DN field value */ + ret = ASN1_STRING_length(cn) == len && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len) == 0; + } + if (!ret) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'", + field, value, ASN1_STRING_get0_data(cn)); + } + break; + } + + return ret; +} + + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g., + * C=US) + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int get_value_from_field(const X509 *cert, char *field_str, + struct tls_dn_field_order_cnt *dn_cnt) +{ + int nid; + char *context = NULL, *name, *value; + + if (os_strcmp(field_str, "*") == 0) + return 1; /* wildcard matches everything */ + + name = str_token(field_str, "=", &context); + if (!name) + return 0; + + /* Compare all configured DN fields and assign nid based on that to + * fetch correct value from certificate subject */ + if (os_strcmp(name, "CN") == 0) { + nid = NID_commonName; + dn_cnt->cn++; + } else if(os_strcmp(name, "C") == 0) { + nid = NID_countryName; + dn_cnt->c++; + } else if (os_strcmp(name, "L") == 0) { + nid = NID_localityName; + dn_cnt->l++; + } else if (os_strcmp(name, "ST") == 0) { + nid = NID_stateOrProvinceName; + dn_cnt->st++; + } else if (os_strcmp(name, "O") == 0) { + nid = NID_organizationName; + dn_cnt->o++; + } else if (os_strcmp(name, "OU") == 0) { + nid = NID_organizationalUnitName; + dn_cnt->ou++; + } else if (os_strcmp(name, "emailAddress") == 0) { + nid = NID_pkcs9_emailAddress; + dn_cnt->email++; + } else { + wpa_printf(MSG_ERROR, + "TLS: Unknown field '%s' in check_cert_subject", name); + return 0; + } + + value = str_token(field_str, "=", &context); + if (!value) { + wpa_printf(MSG_ERROR, + "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject", + name); + return 0; + } + + return match_dn_field(cert, nid, name, value, dn_cnt); +} + + +/** + * tls_match_dn_field - Match subject DN field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on failure +*/ +static int tls_match_dn_field(X509 *cert, const char *match) +{ + const char *token, *last = NULL; + char field[256]; + struct tls_dn_field_order_cnt dn_cnt; + + os_memset(&dn_cnt, 0, sizeof(dn_cnt)); + + /* Maximum length of each DN field is 255 characters */ + + /* Process each '/' delimited field */ + while ((token = cstr_token(match, "/", &last))) { + if (last - token >= (int) sizeof(field)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Too long DN matching field value in '%s'", + match); + return 0; + } + os_memcpy(field, token, last - token); + field[last - token] = '\0'; + + if (!get_value_from_field(cert, field, &dn_cnt)) { + wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'", + field); + return 0; + } + } + + return 1; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static int tls_match_suffix_helper(X509 *cert, const char *match, + size_t match_len, int full) { -#ifdef CONFIG_NATIVE_WINDOWS - /* wincrypt.h has conflicting X509_NAME definition */ - return -1; -#else /* CONFIG_NATIVE_WINDOWS */ GENERAL_NAME *gen; void *ext; int i; @@ -1342,13 +2004,15 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) gen->d.dNSName->data, gen->d.dNSName->length); if (domain_suffix_match(gen->d.dNSName->data, - gen->d.dNSName->length, match, full) == - 1) { + gen->d.dNSName->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); return 1; } } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); if (dns_name) { wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); @@ -1372,8 +2036,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -1383,6 +2047,25 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", full ? "": "suffix "); return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match, int full) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; #endif /* CONFIG_NATIVE_WINDOWS */ } @@ -1481,6 +2164,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, GENERAL_NAME *gen; void *ext; stack_index_t i; + ASN1_INTEGER *ser; + char serial_num[128]; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ @@ -1489,7 +2174,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe || context->cert_in_cb) { + if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) || + context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1508,6 +2194,14 @@ static void openssl_tls_cert_event(struct tls_connection *conn, ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; + ser = X509_get_serialNumber(err_cert); + if (ser) { + wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num), + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + ev.peer_cert.serial_num = serial_num; + } + ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { char *pos; @@ -1544,6 +2238,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn, pos += gen->d.ia5->length; *pos = '\0'; } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); for (alt = 0; alt < num_altsubject; alt++) ev.peer_cert.altsubject[alt] = altsubject[alt]; @@ -1565,6 +2260,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) struct tls_connection *conn; struct tls_context *context; char *match, *altmatch, *suffix_match, *domain_match; + const char *check_cert_subject; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -1605,6 +2301,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "time mismatch"); preverify_ok = 1; } + if (!preverify_ok && !conn->data->check_crl_strict && + (err == X509_V_ERR_CRL_HAS_EXPIRED || + err == X509_V_ERR_CRL_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Ignore certificate validity CRL time mismatch"); + preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); @@ -1658,6 +2361,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); + check_cert_subject = conn->check_cert_subject; + if (!check_cert_subject) + check_cert_subject = conn->data->check_cert_subject; + if (check_cert_subject) { + if (depth == 0 && + !tls_match_dn_field(err_cert, check_cert_subject)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } + } if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); @@ -1701,7 +2416,64 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } - if (preverify_ok && context->event_cb != NULL) +#ifdef CONFIG_SUITEB + if (conn->flags & TLS_CONN_SUITEB) { + EVP_PKEY *pk; + RSA *rsa; + int len = -1; + + pk = X509_get_pubkey(err_cert); + if (pk) { + rsa = EVP_PKEY_get1_RSA(pk); + if (rsa) { + len = RSA_bits(rsa); + RSA_free(rsa); + } + EVP_PKEY_free(pk); + } + + if (len >= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: RSA modulus size: %d bits", len); + if (len < 3072) { + preverify_ok = 0; + openssl_tls_fail_event( + conn, err_cert, err, + depth, buf, + "Insufficient RSA modulus size", + TLS_FAIL_INSUFFICIENT_KEY_LEN); + } + } + } +#endif /* CONFIG_SUITEB */ + +#ifdef OPENSSL_IS_BORINGSSL + if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && + preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, + conn->peer_issuer, + conn->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "certificate revoked", + TLS_FAIL_REVOKED); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && + (conn->flags & TLS_CONN_REQUIRE_OCSP)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } + } +#endif /* OPENSSL_IS_BORINGSSL */ + + if (depth == 0 && preverify_ok && context->event_cb != NULL) context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); @@ -1837,30 +2609,40 @@ static int tls_connection_ca_cert(struct tls_data *data, } #ifdef ANDROID + /* Single alias */ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { - BIO *bio = BIO_from_keystore(&ca_cert[11]); - STACK_OF(X509_INFO) *stack = NULL; - stack_index_t i; - - if (bio) { - stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); - BIO_free(bio); - } - if (!stack) + if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx), + &ca_cert[11]) < 0) return -1; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } - for (i = 0; i < sk_X509_INFO_num(stack); ++i) { - X509_INFO *info = sk_X509_INFO_value(stack, i); - if (info->x509) { - X509_STORE_add_cert(ssl_ctx->cert_store, - info->x509); - } - if (info->crl) { - X509_STORE_add_crl(ssl_ctx->cert_store, - info->crl); + /* Multiple aliases separated by space */ + if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) { + char *aliases = os_strdup(&ca_cert[12]); + const char *delim = " "; + int rc = 0; + char *savedptr; + char *alias; + + if (!aliases) + return -1; + alias = strtok_r(aliases, delim, &savedptr); + for (; alias; alias = strtok_r(NULL, delim, &savedptr)) { + if (tls_add_ca_from_keystore_encoded( + SSL_CTX_get_cert_store(ssl_ctx), alias)) { + wpa_printf(MSG_WARNING, + "OpenSSL: %s - Failed to add ca_cert %s from keystore", + __func__, alias); + rc = -1; + break; } } - sk_X509_INFO_pop_free(stack, X509_INFO_free); + os_free(aliases); + if (rc) + return rc; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); return 0; } @@ -1928,13 +2710,16 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_cert)); #endif /* OPENSSL_NO_STDIO */ + + os_free(data->ca_cert); + data->ca_cert = os_strdup(ca_cert); } return 0; } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { int flags; @@ -1951,6 +2736,10 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) if (check_crl == 2) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(cs, flags); + + data->check_crl = check_crl; + data->check_crl_strict = strict; + os_get_reltime(&data->crl_last_reload); } return 0; } @@ -1960,7 +2749,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *altsubject_match, const char *suffix_match, - const char *domain_match) + const char *domain_match, + const char *check_cert_subject) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -1994,19 +2784,60 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->check_cert_subject); + conn->check_cert_subject = NULL; + if (check_cert_subject) { + conn->check_cert_subject = os_strdup(check_cert_subject); + if (!conn->check_cert_subject) + return -1; + } + + return 0; +} + + +#ifdef CONFIG_SUITEB +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +static int suiteb_cert_cb(SSL *ssl, void *arg) +{ + struct tls_connection *conn = arg; + + /* + * This cert_cb() is not really the best location for doing a + * constraint check for the ServerKeyExchange message, but this seems to + * be the only place where the current OpenSSL sequence can be + * terminated cleanly with an TLS alert going out to the server. + */ + + if (!(conn->flags & TLS_CONN_SUITEB)) + return 1; + + /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */ + if (conn->cipher_suite != 0x9f) + return 1; + + if (conn->server_dh_prime_len >= 3072) + return 1; + + wpa_printf(MSG_DEBUG, + "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake", + conn->server_dh_prime_len); return 0; } +#endif /* OPENSSL_VERSION_NUMBER */ +#endif /* CONFIG_SUITEB */ -static void tls_set_conn_flags(SSL *ssl, unsigned int flags) +static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, + const char *openssl_ciphers) { + SSL *ssl = conn->ssl; + #ifdef SSL_OP_NO_TICKET if (flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_set_options(ssl, SSL_OP_NO_TICKET); -#ifdef SSL_clear_options else SSL_clear_options(ssl, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef SSL_OP_NO_TLSv1 @@ -2027,6 +2858,165 @@ static void tls_set_conn_flags(SSL *ssl, unsigned int flags) else SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2); #endif /* SSL_OP_NO_TLSv1_2 */ +#ifdef SSL_OP_NO_TLSv1_3 + if (flags & TLS_CONN_DISABLE_TLSv1_3) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_3); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3); +#endif /* SSL_OP_NO_TLSv1_3 */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (flags & (TLS_CONN_ENABLE_TLSv1_0 | + TLS_CONN_ENABLE_TLSv1_1 | + TLS_CONN_ENABLE_TLSv1_2)) { + int version = 0; + + /* Explicit request to enable TLS versions even if needing to + * override systemwide policies. */ + if (flags & TLS_CONN_ENABLE_TLSv1_0) { + version = TLS1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_1) { + if (!(flags & TLS_CONN_DISABLE_TLSv1_0)) + version = TLS1_1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_2) { + if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1))) + version = TLS1_2_VERSION; + } + if (!version) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid TLS version configuration"); + return -1; + } + + if (SSL_set_min_proto_version(ssl, version) != 1) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Failed to set minimum TLS version"); + return -1; + } + } +#endif /* >= 1.1.0 */ + +#ifdef CONFIG_SUITEB +#ifdef OPENSSL_IS_BORINGSSL + /* Start with defaults from BoringSSL */ + SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0); +#endif /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (flags & TLS_CONN_SUITEB_NO_ECDH) { + const char *ciphers = "DHE-RSA-AES256-GCM-SHA384"; + + if (openssl_ciphers) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Override ciphers for Suite B (no ECDH): %s", + openssl_ciphers); + ciphers = openssl_ciphers; + } + if (SSL_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B ciphers"); + return -1; + } + } else if (flags & TLS_CONN_SUITEB) { + EC_KEY *ecdh; + const char *ciphers = + "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"; + int nid[1] = { NID_secp384r1 }; + + if (openssl_ciphers) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Override ciphers for Suite B: %s", + openssl_ciphers); + ciphers = openssl_ciphers; + } + if (SSL_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B ciphers"); + return -1; + } + + if (SSL_set1_curves(ssl, nid, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B curves"); + return -1; + } + + ecdh = EC_KEY_new_by_curve_name(NID_secp384r1); + if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) { + EC_KEY_free(ecdh); + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH parameter"); + return -1; + } + EC_KEY_free(ecdh); + } + if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) { +#ifdef OPENSSL_IS_BORINGSSL + uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 }; + + if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs, + 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } +#else /* OPENSSL_IS_BORINGSSL */ + /* ECDSA+SHA384 if need to add EC support here */ + if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } +#endif /* OPENSSL_IS_BORINGSSL */ + + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + SSL_set_cert_cb(ssl, suiteb_cert_cb, conn); + } +#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */ + if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Suite B RSA case not supported with this OpenSSL version"); + return -1; + } +#endif /* OPENSSL_VERSION_NUMBER */ + +#ifdef OPENSSL_IS_BORINGSSL + if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) { + uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 }; + int nid[1] = { NID_secp384r1 }; + + if (SSL_set1_curves(ssl, nid, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B curves"); + return -1; + } + + if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs, + 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } + } +#else /* OPENSSL_IS_BORINGSSL */ + if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) && + openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } +#endif /* OPENSSL_IS_BORINGSSL */ +#else /* CONFIG_SUITEB */ + if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } +#endif /* CONFIG_SUITEB */ + + return 0; } @@ -2050,7 +3040,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } - tls_set_conn_flags(conn->ssl, flags); + if (tls_set_conn_flags(conn, flags, NULL) < 0) + return -1; conn->flags = flags; SSL_set_accept_state(conn->ssl); @@ -2082,6 +3073,17 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL && client_cert_blob == NULL) return 0; +#ifdef PKCS12_FUNCS +#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) + /* + * Clear previously set extra chain certificates, if any, from PKCS#12 + * processing in tls_parse_pkcs12() to allow OpenSSL to build a new + * chain properly. + */ + SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx); +#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */ +#endif /* PKCS12_FUNCS */ + if (client_cert_blob && SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, client_cert_blob_len) == 1) { @@ -2103,13 +3105,24 @@ static int tls_connection_client_cert(struct tls_connection *conn, int ret = -1; if (bio) { x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); - BIO_free(bio); } if (x509) { if (SSL_use_certificate(conn->ssl, x509) == 1) ret = 0; X509_free(x509); } + + /* Read additional certificates into the chain. */ + while (bio) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509) { + /* Takes ownership of x509 */ + SSL_add0_chain_cert(conn->ssl, x509); + } else { + BIO_free(bio); + bio = NULL; + } + } return ret; } #endif /* ANDROID */ @@ -2122,6 +3135,15 @@ static int tls_connection_client_cert(struct tls_connection *conn, return 0; } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) + if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) { + ERR_clear_error(); + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file" + " --> OK"); + return 0; + } +#else if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { ERR_clear_error(); @@ -2129,6 +3151,7 @@ static int tls_connection_client_cert(struct tls_connection *conn, " --> OK"); return 0; } +#endif tls_show_errors(MSG_DEBUG, __func__, "SSL_use_certificate_file failed"); @@ -2168,16 +3191,6 @@ static int tls_global_client_cert(struct tls_data *data, } -static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) -{ - if (password == NULL) { - return 0; - } - os_strlcpy(buf, (char *) password, size); - return os_strlen(buf); -} - - #ifdef PKCS12_FUNCS static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, const char *passwd) @@ -2229,28 +3242,42 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, } if (certs) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_clear_chain_certs(ssl); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) + if (ssl) + SSL_clear_chain_certs(ssl); + else + SSL_CTX_clear_chain_certs(data->ssl); while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "TLS: additional certificate" " from PKCS12: subject='%s'", buf); - if (SSL_add1_chain_cert(ssl, cert) != 1) { + if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) || + (!ssl && SSL_CTX_add1_chain_cert(data->ssl, + cert) != 1)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to add additional certificate"); res = -1; + X509_free(cert); break; } + X509_free(cert); } if (!res) { /* Try to continue anyway */ } - sk_X509_free(certs); + sk_X509_pop_free(certs, X509_free); #ifndef OPENSSL_IS_BORINGSSL - res = SSL_build_cert_chain(ssl, - SSL_BUILD_CHAIN_FLAG_CHECK | - SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + if (ssl) + res = SSL_build_cert_chain( + ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + else + res = SSL_CTX_build_cert_chain( + data->ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); if (!res) { tls_show_errors(MSG_DEBUG, __func__, "Failed to build certificate chain"); @@ -2265,9 +3292,7 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, */ res = 0; #else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ -#if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL_CTX_clear_extra_chain_certs(data->ssl); -#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */ while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); @@ -2279,11 +3304,12 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, */ if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1) { + X509_free(cert); res = -1; break; } } - sk_X509_free(certs); + sk_X509_pop_free(certs, X509_free); #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ } @@ -2463,7 +3489,7 @@ static int tls_connection_engine_ca_cert(struct tls_data *data, static int tls_connection_engine_private_key(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { tls_show_errors(MSG_ERROR, __func__, "ENGINE: cannot use private key for TLS"); @@ -2483,6 +3509,64 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) } +#ifndef OPENSSL_NO_STDIO +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (!password) + return 0; + os_strlcpy(buf, (const char *) password, size); + return os_strlen(buf); +} +#endif /* OPENSSL_NO_STDIO */ + + +static int tls_use_private_key_file(struct tls_data *data, SSL *ssl, + const char *private_key, + const char *private_key_passwd) +{ +#ifndef OPENSSL_NO_STDIO + BIO *bio; + EVP_PKEY *pkey; + int ret; + + /* First try ASN.1 (DER). */ + bio = BIO_new_file(private_key, "r"); + if (!bio) + return -1; + pkey = d2i_PrivateKey_bio(bio, NULL); + BIO_free(bio); + + if (pkey) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__); + } else { + /* Try PEM with the provided password. */ + bio = BIO_new_file(private_key, "r"); + if (!bio) + return -1; + pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb, + (void *) private_key_passwd); + BIO_free(bio); + if (!pkey) + return -1; + wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__); + /* Clear errors from the previous failed load. */ + ERR_clear_error(); + } + + if (ssl) + ret = SSL_use_PrivateKey(ssl, pkey); + else + ret = SSL_CTX_use_PrivateKey(data->ssl, pkey); + + EVP_PKEY_free(pkey); + return ret == 1 ? 0 : -1; +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ +} + + static int tls_connection_private_key(struct tls_data *data, struct tls_connection *conn, const char *private_key, @@ -2490,23 +3574,11 @@ static int tls_connection_private_key(struct tls_data *data, const u8 *private_key_blob, size_t private_key_blob_len) { - SSL_CTX *ssl_ctx = data->ssl; - char *passwd; int ok; if (private_key == NULL && private_key_blob == NULL) return 0; - if (private_key_passwd) { - passwd = os_strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - ok = 0; while (private_key_blob) { if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, @@ -2537,7 +3609,8 @@ static int tls_connection_private_key(struct tls_data *data, } if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob, - private_key_blob_len, passwd) == 0) { + private_key_blob_len, + private_key_passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); ok = 1; @@ -2548,29 +3621,14 @@ static int tls_connection_private_key(struct tls_data *data, } while (!ok && private_key) { -#ifndef OPENSSL_NO_STDIO - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (DER) --> OK"); - ok = 1; - break; - } - - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (PEM) --> OK"); + if (tls_use_private_key_file(data, conn->ssl, private_key, + private_key_passwd) == 0) { ok = 1; break; } -#else /* OPENSSL_NO_STDIO */ - wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", - __func__); -#endif /* OPENSSL_NO_STDIO */ - if (tls_read_pkcs12(data, conn->ssl, private_key, passwd) - == 0) { + if (tls_read_pkcs12(data, conn->ssl, private_key, + private_key_passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); ok = 1; @@ -2590,12 +3648,9 @@ static int tls_connection_private_key(struct tls_data *data, if (!ok) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); - os_free(passwd); return -1; } ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - os_free(passwd); if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " @@ -2613,37 +3668,19 @@ static int tls_global_private_key(struct tls_data *data, const char *private_key_passwd) { SSL_CTX *ssl_ctx = data->ssl; - char *passwd; if (private_key == NULL) return 0; - if (private_key_passwd) { - passwd = os_strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if ( -#ifndef OPENSSL_NO_STDIO - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_ASN1) != 1 && - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_PEM) != 1 && -#endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(data, NULL, private_key, passwd)) { + if (tls_use_private_key_file(data, NULL, private_key, + private_key_passwd) && + tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); - os_free(passwd); ERR_clear_error(); return -1; } - os_free(passwd); ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, @@ -2812,16 +3849,6 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL || keys == NULL) return -1; ssl = conn->ssl; -#if OPENSSL_VERSION_NUMBER < 0x10100000L - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) - return -1; - - os_memset(keys, 0, sizeof(*keys)); - keys->client_random = ssl->s3->client_random; - keys->client_random_len = SSL3_RANDOM_SIZE; - keys->server_random = ssl->s3->server_random; - keys->server_random_len = SSL3_RANDOM_SIZE; -#else if (ssl == NULL) return -1; @@ -2832,16 +3859,17 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, keys->server_random = conn->server_random; keys->server_random_len = SSL_get_server_random( ssl, conn->server_random, sizeof(conn->server_random)); -#endif return 0; } -#ifndef CONFIG_FIPS +#ifdef OPENSSL_NEED_EAP_FAST_PRF static int openssl_get_keyblock_size(SSL *ssl) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) const EVP_CIPHER *c; const EVP_MD *h; int md_size; @@ -2851,17 +3879,11 @@ static int openssl_get_keyblock_size(SSL *ssl) return -1; c = ssl->enc_read_ctx->cipher; -#if OPENSSL_VERSION_NUMBER >= 0x00909000L h = EVP_MD_CTX_md(ssl->read_hash); -#else - h = ssl->read_hash; -#endif if (h) md_size = EVP_MD_size(h); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L else if (ssl->s3) md_size = ssl->s3->tmp.new_mac_secret_size; -#endif else return -1; @@ -2899,86 +3921,26 @@ static int openssl_get_keyblock_size(SSL *ssl) EVP_CIPHER_iv_length(c)); #endif } -#endif /* CONFIG_FIPS */ +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ -static int openssl_tls_prf(struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { -#ifdef CONFIG_FIPS - wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " - "mode"); - return -1; -#else /* CONFIG_FIPS */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL *ssl; - u8 *rnd; - int ret = -1; - int skip = 0; - u8 *tmp_out = NULL; - u8 *_out = out; - const char *ver; - - /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. - */ - - if (conn == NULL) - return -1; - ssl = conn->ssl; - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL || - ssl->session->master_key_length <= 0) - return -1; - ver = SSL_get_version(ssl); - - if (skip_keyblock) { - skip = openssl_get_keyblock_size(ssl); - if (skip < 0) - return -1; - tmp_out = os_malloc(skip + out_len); - if (!tmp_out) - return -1; - _out = tmp_out; - } - - rnd = os_malloc(2 * SSL3_RANDOM_SIZE); - if (!rnd) { - os_free(tmp_out); + if (!conn || + SSL_export_keying_material(conn->ssl, out, out_len, label, + os_strlen(label), context, context_len, + context != NULL) != 1) return -1; - } - - if (server_random_first) { - os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random, - SSL3_RANDOM_SIZE); - } else { - os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random, - SSL3_RANDOM_SIZE); - } + return 0; +} - if (os_strcmp(ver, "TLSv1.2") == 0) { - tls_prf_sha256(ssl->session->master_key, - ssl->session->master_key_length, - label, rnd, 2 * SSL3_RANDOM_SIZE, - _out, skip + out_len); - ret = 0; - } else if (tls_prf_sha1_md5(ssl->session->master_key, - ssl->session->master_key_length, - label, rnd, 2 * SSL3_RANDOM_SIZE, - _out, skip + out_len) == 0) { - ret = 0; - } - os_free(rnd); - if (ret == 0 && skip_keyblock) - os_memcpy(out, _out + skip, out_len); - bin_clear_free(tmp_out, skip); - return ret; -#else +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ +#ifdef OPENSSL_NEED_EAP_FAST_PRF SSL *ssl; SSL_SESSION *sess; u8 *rnd; @@ -2993,9 +3955,9 @@ static int openssl_tls_prf(struct tls_connection *conn, const char *ver; /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. + * TLS library did not support EAP-FAST key generation, so get the + * needed TLS session parameters and use an internal implementation of + * TLS PRF to derive the key. */ if (conn == NULL) @@ -3008,15 +3970,13 @@ static int openssl_tls_prf(struct tls_connection *conn, if (!ver || !sess) return -1; - if (skip_keyblock) { - skip = openssl_get_keyblock_size(ssl); - if (skip < 0) - return -1; - tmp_out = os_malloc(skip + out_len); - if (!tmp_out) - return -1; - _out = tmp_out; - } + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; rnd = os_malloc(2 * SSL3_RANDOM_SIZE); if (!rnd) { @@ -3029,65 +3989,36 @@ static int openssl_tls_prf(struct tls_connection *conn, master_key_len = SSL_SESSION_get_master_key(sess, master_key, sizeof(master_key)); - if (server_random_first) { - os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, - SSL3_RANDOM_SIZE); - } else { - os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random, - SSL3_RANDOM_SIZE); - } + os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE); if (os_strcmp(ver, "TLSv1.2") == 0) { tls_prf_sha256(master_key, master_key_len, - label, rnd, 2 * SSL3_RANDOM_SIZE, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, _out, skip + out_len); ret = 0; } else if (tls_prf_sha1_md5(master_key, master_key_len, - label, rnd, 2 * SSL3_RANDOM_SIZE, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, _out, skip + out_len) == 0) { ret = 0; } os_memset(master_key, 0, sizeof(master_key)); os_free(rnd); - if (ret == 0 && skip_keyblock) + if (ret == 0) os_memcpy(out, _out + skip, out_len); bin_clear_free(tmp_out, skip); return ret; -#endif -#endif /* CONFIG_FIPS */ -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) -{ -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - SSL *ssl; - if (conn == NULL) - return -1; - if (server_random_first || skip_keyblock) - return openssl_tls_prf(conn, label, - server_random_first, skip_keyblock, - out, out_len); - ssl = conn->ssl; - if (SSL_export_keying_material(ssl, out, out_len, label, - os_strlen(label), NULL, 0, 0) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); - return 0; - } -#endif - return openssl_tls_prf(conn, label, server_random_first, - skip_keyblock, out, out_len); +#else /* OPENSSL_NEED_EAP_FAST_PRF */ + wpa_printf(MSG_ERROR, + "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode"); + return -1; +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ } static struct wpabuf * -openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, - int server) +openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data) { int res; struct wpabuf *out_data; @@ -3105,7 +4036,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, } /* Initiate TLS handshake or continue the existing handshake */ - if (server) + if (conn->server) res = SSL_accept(conn->ssl); else res = SSL_connect(conn->ssl); @@ -3120,9 +4051,58 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, else { tls_show_errors(MSG_INFO, __func__, "SSL_connect"); conn->failed++; + if (!conn->server && !conn->client_hello_generated) { + /* The server would not understand TLS Alert + * before ClientHello, so simply terminate + * handshake on this type of error case caused + * by a likely internal error like no ciphers + * available. */ + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not generate ClientHello"); + conn->write_alerts++; + return NULL; + } } } + if (!conn->server && !conn->failed) + conn->client_hello_generated = 1; + +#ifdef CONFIG_SUITEB + if ((conn->flags & TLS_CONN_SUITEB) && !conn->server && + os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 && + conn->server_dh_prime_len < 3072) { + struct tls_context *context = conn->context; + + /* + * This should not be reached since earlier cert_cb should have + * terminated the handshake. Keep this check here for extra + * protection if anything goes wrong with the more low-level + * checks based on having to parse the TLS handshake messages. + */ + wpa_printf(MSG_DEBUG, + "OpenSSL: Server DH prime length: %d bits", + conn->server_dh_prime_len); + + if (context->event_cb) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 1; + ev.alert.type = "fatal"; + ev.alert.description = "insufficient security"; + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); + } + /* + * Could send a TLS Alert to the server, but for now, simply + * terminate handshake. + */ + conn->failed++; + conn->write_alerts++; + return NULL; + } +#endif /* CONFIG_SUITEB */ + /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); @@ -3192,14 +4172,14 @@ openssl_get_appl_data(struct tls_connection *conn, size_t max_len) static struct wpabuf * openssl_connection_handshake(struct tls_connection *conn, const struct wpabuf *in_data, - struct wpabuf **appl_data, int server) + struct wpabuf **appl_data) { struct wpabuf *out_data; if (appl_data) *appl_data = NULL; - out_data = openssl_handshake(conn, in_data, server); + out_data = openssl_handshake(conn, in_data); if (out_data == NULL) return NULL; if (conn->invalid_hb_used) { @@ -3236,7 +4216,7 @@ tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { - return openssl_connection_handshake(conn, in_data, appl_data, 0); + return openssl_connection_handshake(conn, in_data, appl_data); } @@ -3245,7 +4225,8 @@ struct wpabuf * tls_connection_server_handshake(void *tls_ctx, const struct wpabuf *in_data, struct wpabuf **appl_data) { - return openssl_connection_handshake(conn, in_data, appl_data, 1); + conn->server = 1; + return openssl_connection_handshake(conn, in_data, appl_data); } @@ -3340,18 +4321,14 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - return conn ? SSL_cache_hit(conn->ssl) : 0; -#else - return conn ? conn->ssl->hit : 0; -#endif + return conn ? SSL_session_reused(conn->ssl) : 0; } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { - char buf[100], *pos, *end; + char buf[500], *pos, *end; u8 *c; int ret; @@ -3379,6 +4356,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, case TLS_CIPHER_ANON_DH_AES128_SHA: suite = "ADH-AES128-SHA"; break; + case TLS_CIPHER_RSA_DHE_AES256_SHA: + suite = "DHE-RSA-AES256-SHA"; + break; + case TLS_CIPHER_AES256_SHA: + suite = "AES256-SHA"; + break; default: wpa_printf(MSG_DEBUG, "TLS: Unsupported " "cipher selection: %d", *c); @@ -3394,7 +4377,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) if (os_strstr(buf, ":ADH-")) { /* @@ -3579,7 +4562,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) { struct tls_connection *conn = arg; const unsigned char *p; - int len, status, reason; + int len, status, reason, res; OCSP_RESPONSE *rsp; OCSP_BASICRESP *basic; OCSP_CERTID *id; @@ -3674,23 +4657,42 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer); if (!id) { - wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA256)"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } - if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, - &this_update, &next_update)) { + res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update); + if (!res) { + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA1)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + res = OCSP_resp_find_status(basic, id, &status, &reason, + &produced_at, &this_update, + &next_update); + } + + if (!res) { wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : " (OCSP not required)"); + OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; } + OCSP_CERTID_free(id); if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { tls_show_errors(MSG_INFO, __func__, @@ -3765,10 +4767,17 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const char *cert_id = params->cert_id; const char *ca_cert_id = params->ca_cert_id; const char *engine_id = params->engine ? params->engine_id : NULL; + const char *ciphers; if (conn == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "OpenSSL: ocsp=3 not supported"); + return -1; + } + /* * If the engine isn't explicitly configured, and any of the * cert/key fields are actually PKCS#11 URIs, then automatically @@ -3800,7 +4809,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, engine_id = "pkcs11"; #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) if (params->flags & TLS_CONN_EAP_FAST) { wpa_printf(MSG_DEBUG, "OpenSSL: Use TLSv1_method() for EAP-FAST"); @@ -3811,6 +4820,17 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } #endif +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +#ifdef SSL_OP_NO_TLSv1_3 + if (params->flags & TLS_CONN_EAP_FAST) { + /* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1 + * refuses to start the handshake with the modified ciphersuite + * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */ + wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST"); + SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3); + } +#endif /* SSL_OP_NO_TLSv1_3 */ +#endif #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ while ((err = ERR_get_error())) { @@ -3829,7 +4849,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->subject_match, params->altsubject_match, params->suffix_match, - params->domain_match)) + params->domain_match, + params->check_cert_subject)) return -1; if (engine_id && ca_cert_id) { @@ -3869,16 +4890,67 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - if (params->openssl_ciphers && - SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + ciphers = params->openssl_ciphers; +#ifdef CONFIG_SUITEB +#ifdef OPENSSL_IS_BORINGSSL + if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) { + /* BoringSSL removed support for SUITEB192, so need to handle + * this with hardcoded ciphersuite and additional checks for + * other parameters. */ + ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384"; + } +#endif /* OPENSSL_IS_BORINGSSL */ +#endif /* CONFIG_SUITEB */ + if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) { wpa_printf(MSG_INFO, "OpenSSL: Failed to set cipher string '%s'", - params->openssl_ciphers); + ciphers); return -1; } - tls_set_conn_flags(conn->ssl, params->flags); + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC + if (SSL_set1_curves_list(conn->ssl, + params->openssl_ecdh_curves) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + + if (tls_set_conn_flags(conn, params->flags, + params->openssl_ciphers) < 0) + return -1; +#ifdef OPENSSL_IS_BORINGSSL + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_enable_ocsp_stapling(conn->ssl); + } +#else /* OPENSSL_IS_BORINGSSL */ #ifdef HAVE_OCSP if (params->flags & TLS_CONN_REQUEST_OCSP) { SSL_CTX *ssl_ctx = data->ssl; @@ -3897,6 +4969,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, "OpenSSL: No OCSP support included - allow optional OCSP case to continue"); } #endif /* HAVE_OCSP */ +#endif /* OPENSSL_IS_BORINGSSL */ conn->flags = params->flags; @@ -3918,6 +4991,15 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } + os_free(data->check_cert_subject); + data->check_cert_subject = NULL; + if (params->check_cert_subject) { + data->check_cert_subject = + os_strdup(params->check_cert_subject); + if (!data->check_cert_subject) + return -1; + } + if (tls_global_ca_cert(data, params->ca_cert) || tls_global_client_cert(data, params->client_cert) || tls_global_private_key(data, params->private_key, @@ -3935,13 +5017,49 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_CTX_set_ecdh_auto(ssl_ctx, 1); +#endif + if (SSL_CTX_set1_curves_list(ssl_ctx, + params->openssl_ecdh_curves) != + 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); -#ifdef SSL_CTX_clear_options else SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef HAVE_OCSP @@ -3964,7 +5082,7 @@ int tls_global_set_params(void *tls_ctx, * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ -#ifdef OPENSSL_IS_BORINGSSL +#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, const SSL_CIPHER **cipher, void *arg) @@ -3977,7 +5095,9 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, struct tls_connection *conn = arg; int ret; -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -4030,11 +5150,10 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " "extension", data, len); - conn->session_ticket = os_malloc(len); + conn->session_ticket = os_memdup(data, len); if (conn->session_ticket == NULL) return 0; - os_memcpy(conn->session_ticket, data, len); conn->session_ticket_len = len; return 1; @@ -4072,9 +5191,15 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, int tls_get_library_version(char *buf, size_t buf_len) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", + OPENSSL_VERSION_TEXT, + OpenSSL_version(OPENSSL_VERSION)); +#else return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); +#endif } diff --git a/contrib/wpa/src/crypto/tls_openssl.h b/contrib/wpa/src/crypto/tls_openssl.h new file mode 100644 index 0000000..2a62d5c --- /dev/null +++ b/contrib/wpa/src/crypto/tls_openssl.h @@ -0,0 +1,19 @@ +/* + * SSL/TLS interface functions for OpenSSL + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLS_OPENSSL_H +#define TLS_OPENSSL_H + +enum ocsp_result { + OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID +}; + +enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert, + X509 *issuer, X509 *issuer_issuer); + +#endif /* TLS_OPENSSL_H */ diff --git a/contrib/wpa/src/crypto/tls_openssl_ocsp.c b/contrib/wpa/src/crypto/tls_openssl_ocsp.c new file mode 100644 index 0000000..8b37b34 --- /dev/null +++ b/contrib/wpa/src/crypto/tls_openssl_ocsp.c @@ -0,0 +1,846 @@ +/* + * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/x509v3.h> +#ifdef OPENSSL_IS_BORINGSSL +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#endif /* OPENSSL_IS_BORINGSSL */ + +#include "common.h" +#include "tls_openssl.h" + + +#ifdef OPENSSL_IS_BORINGSSL + +static void tls_show_errors(int level, const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(level, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + + +/* + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuer's public key + * serialNumber CertificateSerialNumber } + */ +typedef struct { + X509_ALGOR *hashAlgorithm; + ASN1_OCTET_STRING *issuerNameHash; + ASN1_OCTET_STRING *issuerKeyHash; + ASN1_INTEGER *serialNumber; +} CertID; + +/* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ +typedef struct { + ASN1_OBJECT *responseType; + ASN1_OCTET_STRING *response; +} ResponseBytes; + +/* + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ +typedef struct { + ASN1_ENUMERATED *responseStatus; + ResponseBytes *responseBytes; +} OCSPResponse; + +ASN1_SEQUENCE(ResponseBytes) = { + ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT), + ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(ResponseBytes); + +ASN1_SEQUENCE(OCSPResponse) = { + ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED), + ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0) +} ASN1_SEQUENCE_END(OCSPResponse); + +IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse); + +/* + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ +typedef struct { + int type; + union { + X509_NAME *byName; + ASN1_OCTET_STRING *byKey; + } value; +} ResponderID; + +/* + * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + */ +typedef struct { + ASN1_GENERALIZEDTIME *revocationTime; + ASN1_ENUMERATED *revocationReason; +} RevokedInfo; + +/* + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + */ +typedef struct { + int type; + union { + ASN1_NULL *good; + RevokedInfo *revoked; + ASN1_NULL *unknown; + } value; +} CertStatus; + +/* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct { + CertID *certID; + CertStatus *certStatus; + ASN1_GENERALIZEDTIME *thisUpdate; + ASN1_GENERALIZEDTIME *nextUpdate; + STACK_OF(X509_EXTENSION) *singleExtensions; +} SingleResponse; + +/* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct { + ASN1_INTEGER *version; + ResponderID *responderID; + ASN1_GENERALIZEDTIME *producedAt; + STACK_OF(SingleResponse) *responses; + STACK_OF(X509_EXTENSION) *responseExtensions; +} ResponseData; + +/* + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ +typedef struct { + ResponseData *tbsResponseData; + X509_ALGOR *signatureAlgorithm; + ASN1_BIT_STRING *signature; + STACK_OF(X509) *certs; +} BasicOCSPResponse; + +ASN1_SEQUENCE(CertID) = { + ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR), + ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING), + ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING), + ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER) +} ASN1_SEQUENCE_END(CertID); + +ASN1_CHOICE(ResponderID) = { + ASN1_EXP(ResponderID, value.byName, X509_NAME, 1), + ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2) +} ASN1_CHOICE_END(ResponderID); + +ASN1_SEQUENCE(RevokedInfo) = { + ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME), + ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0) +} ASN1_SEQUENCE_END(RevokedInfo); + +ASN1_CHOICE(CertStatus) = { + ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0), + ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1), + ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2) +} ASN1_CHOICE_END(CertStatus); + +ASN1_SEQUENCE(SingleResponse) = { + ASN1_SIMPLE(SingleResponse, certID, CertID), + ASN1_SIMPLE(SingleResponse, certStatus, CertStatus), + ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME), + ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0), + ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions, + X509_EXTENSION, 1) +} ASN1_SEQUENCE_END(SingleResponse); + +ASN1_SEQUENCE(ResponseData) = { + ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0), + ASN1_SIMPLE(ResponseData, responderID, ResponderID), + ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME), + ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse), + ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions, + X509_EXTENSION, 1) +} ASN1_SEQUENCE_END(ResponseData); + +ASN1_SEQUENCE(BasicOCSPResponse) = { + ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData), + ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR), + ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING), + ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0) +} ASN1_SEQUENCE_END(BasicOCSPResponse); + +IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse); + +#define sk_SingleResponse_num(sk) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk)) + +#define sk_SingleResponse_value(sk, i) \ + ((SingleResponse *) \ + sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i))) + + +static char * mem_bio_to_str(BIO *out) +{ + char *txt; + size_t rlen; + int res; + + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return NULL; + } + + res = BIO_read(out, txt, rlen); + BIO_free(out); + if (res < 0) { + os_free(txt); + return NULL; + } + + txt[res] = '\0'; + return txt; +} + + +static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + if (!ASN1_GENERALIZEDTIME_print(out, t)) { + BIO_free(out); + return NULL; + } + + return mem_bio_to_str(out); +} + + +static char * responderid_str(ResponderID *rid) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + switch (rid->type) { + case 0: + X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE); + break; + case 1: + i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING); + break; + default: + BIO_free(out); + return NULL; + } + + return mem_bio_to_str(out); +} + + +static char * octet_string_str(ASN1_OCTET_STRING *o) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING); + return mem_bio_to_str(out); +} + + +static char * integer_str(ASN1_INTEGER *i) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_INTEGER(out, i); + return mem_bio_to_str(out); +} + + +static char * algor_str(X509_ALGOR *alg) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_OBJECT(out, alg->algorithm); + return mem_bio_to_str(out); +} + + +static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext) +{ + BIO *out; + + if (!ext) + return NULL; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + if (!X509V3_extensions_print(out, title, ext, 0, 0)) { + BIO_free(out); + return NULL; + } + return mem_bio_to_str(out); +} + + +static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd, + ASN1_GENERALIZEDTIME *nextupd) +{ + time_t now, tmp; + + if (!ASN1_GENERALIZEDTIME_check(thisupd)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid OCSP response thisUpdate"); + return 0; + } + + time(&now); + tmp = now + 5 * 60; /* allow five minute clock difference */ + if (X509_cmp_time(thisupd, &tmp) > 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid"); + return 0; + } + + if (!nextupd) + return 1; /* OK - no limit on response age */ + + if (!ASN1_GENERALIZEDTIME_check(nextupd)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid OCSP response nextUpdate"); + return 0; + } + + tmp = now - 5 * 60; /* allow five minute clock difference */ + if (X509_cmp_time(nextupd, &tmp) < 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired"); + return 0; + } + + if (ASN1_STRING_cmp(nextupd, thisupd) < 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response nextUpdate before thisUpdate"); + return 0; + } + + /* Both thisUpdate and nextUpdate are valid */ + return -1; +} + + +static int issuer_match(X509 *cert, X509 *issuer, CertID *certid) +{ + X509_NAME *iname; + ASN1_BIT_STRING *ikey; + const EVP_MD *dgst; + unsigned int len; + unsigned char md[EVP_MAX_MD_SIZE]; + ASN1_OCTET_STRING *hash; + char *txt; + + dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm); + if (!dgst) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find matching hash algorithm for OCSP"); + return -1; + } + + iname = X509_get_issuer_name(cert); + if (!X509_NAME_digest(iname, dgst, md, &len)) + return -1; + hash = ASN1_OCTET_STRING_new(); + if (!hash) + return -1; + if (!ASN1_OCTET_STRING_set(hash, md, len)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + txt = octet_string_str(hash); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s", + txt); + os_free(txt); + } + + if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + ikey = X509_get0_pubkey_bitstr(issuer); + if (!ikey || + !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) || + !ASN1_OCTET_STRING_set(hash, md, len)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + txt = octet_string_str(hash); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s", + txt); + os_free(txt); + } + + if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + ASN1_OCTET_STRING_free(hash); + return 0; +} + + +static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid) +{ + unsigned int i; + unsigned char hash[SHA_DIGEST_LENGTH]; + + if (rid->type == 0) { + /* byName */ + return X509_find_by_subject(certs, rid->value.byName); + } + + /* byKey */ + if (rid->value.byKey->length != SHA_DIGEST_LENGTH) + return NULL; + for (i = 0; i < sk_X509_num(certs); i++) { + X509 *x = sk_X509_value(certs, i); + + X509_pubkey_digest(x, EVP_sha1(), hash, NULL); + if (os_memcmp(rid->value.byKey->data, hash, + SHA_DIGEST_LENGTH) == 0) + return x; + } + + return NULL; +} + + +enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert, + X509 *issuer, X509 *issuer_issuer) +{ + const uint8_t *resp_data; + size_t resp_len; + OCSPResponse *resp; + int status; + ResponseBytes *bytes; + const u8 *basic_data; + size_t basic_len; + BasicOCSPResponse *basic; + ResponseData *rd; + char *txt; + int i, num; + unsigned int j, num_resp; + SingleResponse *matching_resp = NULL, *cmp_sresp; + enum ocsp_result result = OCSP_INVALID; + X509_STORE *store; + STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL; + X509_STORE_CTX ctx; + X509 *signer, *tmp_cert; + int signer_trusted = 0; + EVP_PKEY *skey; + int ret; + char buf[256]; + + txt = integer_str(X509_get_serialNumber(cert)); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt); + os_free(txt); + } + + SSL_get0_ocsp_response(ssl, &resp_data, &resp_len); + if (resp_data == NULL || resp_len == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return OCSP_NO_RESPONSE; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len); + + resp = d2i_OCSPResponse(NULL, &resp_data, resp_len); + if (!resp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse"); + return OCSP_INVALID; + } + + status = ASN1_ENUMERATED_get(resp->responseStatus); + if (status != 0) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d", + status); + return OCSP_INVALID; + } + + bytes = resp->responseBytes; + + if (!bytes || + OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) { + wpa_printf(MSG_INFO, + "OpenSSL: Could not find BasicOCSPResponse"); + return OCSP_INVALID; + } + + basic_data = ASN1_STRING_data(bytes->response); + basic_len = ASN1_STRING_length(bytes->response); + wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse", + basic_data, basic_len); + + basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len); + if (!basic) { + wpa_printf(MSG_INFO, + "OpenSSL: Could not parse BasicOCSPResponse"); + OCSPResponse_free(resp); + return OCSP_INVALID; + } + + rd = basic->tbsResponseData; + + if (basic->certs) { + untrusted = sk_X509_dup(basic->certs); + if (!untrusted) + goto fail; + + num = sk_X509_num(basic->certs); + for (i = 0; i < num; i++) { + X509 *extra_cert; + + extra_cert = sk_X509_value(basic->certs, i); + X509_NAME_oneline(X509_get_subject_name(extra_cert), + buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, + "OpenSSL: BasicOCSPResponse cert %s", buf); + + if (!sk_X509_push(untrusted, extra_cert)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not add certificate to the untrusted stack"); + } + } + } + + store = SSL_CTX_get_cert_store(ssl_ctx); + if (issuer) { + if (X509_STORE_add_cert(store, issuer) != 1) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: Could not add issuer to certificate store"); + } + certs = sk_X509_new_null(); + if (certs) { + tmp_cert = X509_dup(issuer); + if (tmp_cert && !sk_X509_push(certs, tmp_cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store"); + X509_free(tmp_cert); + sk_X509_free(certs); + certs = NULL; + } + if (certs && issuer_issuer) { + tmp_cert = X509_dup(issuer_issuer); + if (tmp_cert && + !sk_X509_push(certs, tmp_cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); + X509_free(tmp_cert); + } + } + } + } + + signer = ocsp_find_signer(certs, rd->responderID); + if (!signer) + signer = ocsp_find_signer(untrusted, rd->responderID); + else + signer_trusted = 1; + if (!signer) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find OCSP signer certificate"); + goto fail; + } + + skey = X509_get_pubkey(signer); + if (!skey) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not get OCSP signer public key"); + goto fail; + } + if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData), + basic->signatureAlgorithm, basic->signature, + basic->tbsResponseData, skey) <= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: BasicOCSPResponse signature is invalid"); + goto fail; + } + + X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, + "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature", + buf); + + if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted)) + goto fail; + X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER); + ret = X509_verify_cert(&ctx); + chain = X509_STORE_CTX_get1_chain(&ctx); + X509_STORE_CTX_cleanup(&ctx); + if (ret <= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not validate OCSP signer certificate"); + goto fail; + } + + if (!chain || sk_X509_num(chain) <= 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found"); + goto fail; + } + + if (!signer_trusted) { + X509_check_purpose(signer, -1, 0); + if ((signer->ex_flags & EXFLAG_XKUSAGE) && + (signer->ex_xkusage & XKU_OCSP_SIGN)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP signer certificate delegation OK"); + } else { + tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1); + if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) != + X509_TRUST_TRUSTED) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP signer certificate not trusted"); + result = OCSP_NO_RESPONSE; + goto fail; + } + } + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu", + ASN1_INTEGER_get(rd->version)); + + txt = responderid_str(rd->responderID); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s", + txt); + os_free(txt); + } + + txt = generalizedtime_str(rd->producedAt); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s", + txt); + os_free(txt); + } + + num_resp = sk_SingleResponse_num(rd->responses); + if (num_resp == 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse"); + result = OCSP_NO_RESPONSE; + goto fail; + } + cmp_sresp = sk_SingleResponse_value(rd->responses, 0); + for (j = 0; j < num_resp; j++) { + SingleResponse *sresp; + CertID *cid1, *cid2; + + sresp = sk_SingleResponse_value(rd->responses, j); + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u", + j + 1, num_resp); + + txt = algor_str(sresp->certID->hashAlgorithm); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID hashAlgorithm: %s", txt); + os_free(txt); + } + + txt = octet_string_str(sresp->certID->issuerNameHash); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID issuerNameHash: %s", txt); + os_free(txt); + } + + txt = octet_string_str(sresp->certID->issuerKeyHash); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID issuerKeyHash: %s", txt); + os_free(txt); + } + + txt = integer_str(sresp->certID->serialNumber); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID serialNumber: %s", txt); + os_free(txt); + } + + switch (sresp->certStatus->type) { + case 0: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good"); + break; + case 1: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked"); + break; + default: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown"); + break; + } + + txt = generalizedtime_str(sresp->thisUpdate); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt); + os_free(txt); + } + + if (sresp->nextUpdate) { + txt = generalizedtime_str(sresp->nextUpdate); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s", + txt); + os_free(txt); + } + } + + txt = extensions_str("singleExtensions", + sresp->singleExtensions); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); + os_free(txt); + } + + cid1 = cmp_sresp->certID; + cid2 = sresp->certID; + if (j > 0 && + (OBJ_cmp(cid1->hashAlgorithm->algorithm, + cid2->hashAlgorithm->algorithm) != 0 || + ASN1_OCTET_STRING_cmp(cid1->issuerNameHash, + cid2->issuerNameHash) != 0 || + ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash, + cid2->issuerKeyHash) != 0)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse"); + goto fail; + } + + if (!matching_resp && issuer && + ASN1_INTEGER_cmp(sresp->certID->serialNumber, + X509_get_serialNumber(cert)) == 0 && + issuer_match(cert, issuer, sresp->certID) == 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: This response matches peer certificate"); + matching_resp = sresp; + } + } + + txt = extensions_str("responseExtensions", rd->responseExtensions); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); + os_free(txt); + } + + if (!matching_resp) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find OCSP response that matches the peer certificate"); + result = OCSP_NO_RESPONSE; + goto fail; + } + + if (!ocsp_resp_valid(matching_resp->thisUpdate, + matching_resp->nextUpdate)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response not valid at this time"); + goto fail; + } + + if (matching_resp->certStatus->type == 1) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response indicated that the peer certificate has been revoked"); + result = OCSP_REVOKED; + goto fail; + } + + if (matching_resp->certStatus->type != 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response did not indicate good status"); + result = OCSP_NO_RESPONSE; + goto fail; + } + + /* OCSP response indicated the certificate is good. */ + result = OCSP_GOOD; +fail: + sk_X509_pop_free(chain, X509_free); + sk_X509_free(untrusted); + sk_X509_pop_free(certs, X509_free); + BasicOCSPResponse_free(basic); + OCSPResponse_free(resp); + + return result; +} + +#endif /* OPENSSL_IS_BORINGSSL */ diff --git a/contrib/wpa/src/crypto/tls_wolfssl.c b/contrib/wpa/src/crypto/tls_wolfssl.c new file mode 100644 index 0000000..e9cb425 --- /dev/null +++ b/contrib/wpa/src/crypto/tls_wolfssl.c @@ -0,0 +1,2201 @@ +/* + * SSL/TLS interface functions for wolfSSL TLS case + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "tls.h" + +/* wolfSSL includes */ +#include <wolfssl/options.h> +#include <wolfssl/ssl.h> +#include <wolfssl/error-ssl.h> +#include <wolfssl/wolfcrypt/asn.h> + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#define HAVE_AESGCM +#include <wolfssl/wolfcrypt/aes.h> +#endif + +#if !defined(CONFIG_FIPS) && \ + (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST)) +#define WOLFSSL_NEED_EAP_FAST_PRF +#endif + +#define SECRET_LEN 48 +#define RAN_LEN 32 +#define SESSION_TICKET_LEN 256 + +static int tls_ref_count = 0; + +static int tls_ex_idx_session = 0; + + +/* tls input data for wolfSSL Read Callback */ +struct tls_in_data { + const struct wpabuf *in_data; + size_t consumed; /* how many bytes have we used already */ +}; + +/* tls output data for wolfSSL Write Callback */ +struct tls_out_data { + struct wpabuf *out_data; +}; + +struct tls_context { + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; + char *ocsp_stapling_response; +}; + +static struct tls_context *tls_global = NULL; + +/* wolfssl tls_connection */ +struct tls_connection { + struct tls_context *context; + WOLFSSL *ssl; + int read_alerts; + int write_alerts; + int failed; + struct tls_in_data input; + struct tls_out_data output; + char *subject_match; + char *alt_subject_match; + char *suffix_match; + char *domain_match; + + u8 srv_cert_hash[32]; + + unsigned char client_random[RAN_LEN]; + unsigned char server_random[RAN_LEN]; + unsigned int flags; +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + tls_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + byte session_ticket[SESSION_TICKET_LEN]; +#endif + unsigned int ca_cert_verify:1; + unsigned int cert_probe:1; + unsigned int server_cert_only:1; + unsigned int success_data:1; + + WOLFSSL_X509 *peer_cert; + WOLFSSL_X509 *peer_issuer; + WOLFSSL_X509 *peer_issuer_issuer; +}; + + +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + + if (!context) + return NULL; + + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + + return context; +} + + +static void wolfssl_reset_in_data(struct tls_in_data *in, + const struct wpabuf *buf) +{ + /* old one not owned by us so don't free */ + in->in_data = buf; + in->consumed = 0; +} + + +static void wolfssl_reset_out_data(struct tls_out_data *out) +{ + /* old one not owned by us so don't free */ + out->out_data = wpabuf_alloc_copy("", 0); +} + + +/* wolfSSL I/O Receive CallBack */ +static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + size_t get = sz; + struct tls_in_data *data = ctx; + + if (!data) + return -1; + + if (get > (wpabuf_len(data->in_data) - data->consumed)) + get = wpabuf_len(data->in_data) - data->consumed; + + os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get); + data->consumed += get; + + if (get == 0) + return -2; /* WANT_READ */ + + return (int) get; +} + + +/* wolfSSL I/O Send CallBack */ +static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + struct wpabuf *tmp; + struct tls_out_data *data = ctx; + + if (!data) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz); + + tmp = wpabuf_alloc_copy(buf, sz); + if (!tmp) + return -1; + data->out_data = wpabuf_concat(data->out_data, tmp); + if (!data->out_data) + return -1; + + return sz; +} + + +static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess) +{ + struct wpabuf *buf; + + buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (!buf) + return; + wpa_printf(MSG_DEBUG, + "wolfSSL: Free application session data %p (sess %p)", + buf, sess); + wpabuf_free(buf); + + wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); +} + + +void * tls_init(const struct tls_config *conf) +{ + WOLFSSL_CTX *ssl_ctx; + struct tls_context *context; + const char *ciphers; + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif /* DEBUG_WOLFSSL */ + + context = tls_context_new(conf); + if (!context) + return NULL; + + if (tls_ref_count == 0) { + tls_global = context; + + if (wolfSSL_Init() < 0) + return NULL; + /* wolfSSL_Debugging_ON(); */ + } + + tls_ref_count++; + + /* start as client */ + ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + if (!ssl_ctx) { + tls_ref_count--; + if (context != tls_global) + os_free(context); + if (tls_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } + } + wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb); + wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb); + wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context); + + if (conf->tls_session_lifetime > 0) { + wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1); + wolfSSL_CTX_set_session_cache_mode(ssl_ctx, + SSL_SESS_CACHE_SERVER); + wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime); + wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb); + } else { + wolfSSL_CTX_set_session_cache_mode(ssl_ctx, + SSL_SESS_CACHE_CLIENT); + } + + if (conf && conf->openssl_ciphers) + ciphers = conf->openssl_ciphers; + else + ciphers = "ALL"; + if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set cipher string '%s'", + ciphers); + tls_deinit(ssl_ctx); + return NULL; + } + + return ssl_ctx; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + + if (context != tls_global) + os_free(context); + + wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx); + + tls_ref_count--; + if (tls_ref_count == 0) { + wolfSSL_Cleanup(); + os_free(tls_global); + tls_global = NULL; + } +} + + +int tls_get_errors(void *tls_ctx) +{ +#ifdef DEBUG_WOLFSSL +#if 0 + unsigned long err; + + err = wolfSSL_ERR_peek_last_error_line(NULL, NULL); + if (err != 0) { + wpa_printf(MSG_INFO, "TLS - SSL error: %s", + wolfSSL_ERR_error_string(err, NULL)); + return 1; + } +#endif +#endif /* DEBUG_WOLFSSL */ + return 0; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + WOLFSSL_CTX *ssl_ctx = tls_ctx; + struct tls_connection *conn; + + wpa_printf(MSG_DEBUG, "SSL: connection init"); + + conn = os_zalloc(sizeof(*conn)); + if (!conn) + return NULL; + conn->ssl = wolfSSL_new(ssl_ctx); + if (!conn->ssl) { + os_free(conn); + return NULL; + } + + wolfSSL_SetIOReadCtx(conn->ssl, &conn->input); + wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output); + wolfSSL_set_ex_data(conn->ssl, 0, conn); + conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + + /* Need randoms post-hanshake for EAP-FAST, export key and deriving + * session ID in EAP methods. */ + wolfSSL_KeepArrays(conn->ssl); + wolfSSL_KeepHandshakeResources(conn->ssl); + wolfSSL_UseClientSuites(conn->ssl); + + return conn; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return; + + wpa_printf(MSG_DEBUG, "SSL: connection deinit"); + + /* parts */ + wolfSSL_free(conn->ssl); + os_free(conn->subject_match); + os_free(conn->alt_subject_match); + os_free(conn->suffix_match); + os_free(conn->domain_match); + + /* self */ + os_free(conn); +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ + return conn ? wolfSSL_is_init_finished(conn->ssl) : 0; +} + + +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ + WOLFSSL_SESSION *session; + + if (!conn) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: connection shutdown"); + + /* Set quiet as OpenSSL does */ + wolfSSL_set_quiet_shutdown(conn->ssl, 1); + wolfSSL_shutdown(conn->ssl); + + session = wolfSSL_get_session(conn->ssl); + if (wolfSSL_clear(conn->ssl) != 1) + return -1; + wolfSSL_set_session(conn->ssl, session); + + return 0; +} + + +static int tls_connection_set_subject_match(struct tls_connection *conn, + const char *subject_match, + const char *alt_subject_match, + const char *suffix_match, + const char *domain_match) +{ + os_free(conn->subject_match); + conn->subject_match = NULL; + if (subject_match) { + conn->subject_match = os_strdup(subject_match); + if (!conn->subject_match) + return -1; + } + + os_free(conn->alt_subject_match); + conn->alt_subject_match = NULL; + if (alt_subject_match) { + conn->alt_subject_match = os_strdup(alt_subject_match); + if (!conn->alt_subject_match) + return -1; + } + + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (!conn->suffix_match) + return -1; + } + + os_free(conn->domain_match); + conn->domain_match = NULL; + if (domain_match) { + conn->domain_match = os_strdup(domain_match); + if (!conn->domain_match) + return -1; + } + + return 0; +} + + +static int tls_connection_dh(struct tls_connection *conn, const char *dh_file, + const u8 *dh_blob, size_t blob_len) +{ + if (!dh_file && !dh_blob) + return 0; + + wolfSSL_set_accept_state(conn->ssl); + + if (dh_blob) { + if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, "SSL: use DH DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use DH blob OK"); + return 0; + } + + if (dh_file) { + wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file); + if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file, + SSL_FILETYPE_PEM) < 0) { + wpa_printf(MSG_INFO, "SSL: use DH PEM file failed"); + if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use DH DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: use DH file OK"); + return 0; + } + + return 0; +} + + +static int tls_connection_client_cert(struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t blob_len) +{ + if (!client_cert && !client_cert_blob) + return 0; + + if (client_cert_blob) { + if (wolfSSL_use_certificate_chain_buffer_format( + conn->ssl, client_cert_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK"); + return 0; + } + + if (client_cert) { + if (wolfSSL_use_certificate_chain_file(conn->ssl, + client_cert) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert PEM file failed"); + if (wolfSSL_use_certificate_chain_file_format( + conn->ssl, client_cert, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: use client cert file OK"); + return 0; + } + + return 0; +} + + +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (!password) + return 0; + os_strlcpy(buf, (char *) password, size); + return os_strlen(buf); +} + + +static int tls_connection_private_key(void *tls_ctx, + struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t blob_len) +{ + WOLFSSL_CTX *ctx = tls_ctx; + char *passwd = NULL; + int ok = 0; + + if (!private_key && !private_key_blob) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (!passwd) + return -1; + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb); + wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd); + + if (private_key_blob) { + if (wolfSSL_use_PrivateKey_buffer(conn->ssl, + private_key_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use private DER blob failed"); + } else { + wpa_printf(MSG_DEBUG, "SSL: use private key blob OK"); + ok = 1; + } + } + + if (!ok && private_key) { + if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_PEM) < 0) { + wpa_printf(MSG_INFO, + "SSL: use private key PEM file failed"); + if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) < 0) + { + wpa_printf(MSG_INFO, + "SSL: use private key DER file failed"); + } else { + ok = 1; + } + } else { + ok = 1; + } + + if (ok) + wpa_printf(MSG_DEBUG, "SSL: use private key file OK"); + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, NULL); + os_free(passwd); + + if (!ok) + return -1; + + return 0; +} + + +static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type, + const char *value, size_t len) +{ + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int found = 0; + int i; + + ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL); + + for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) { + gen = wolfSSL_sk_value(ext, i); + if (gen->type != type) + continue; + if (os_strlen((char *) gen->obj) == len && + os_memcmp(value, gen->obj, len) == 0) + found++; + } + + wolfSSL_sk_ASN1_OBJECT_free(ext); + + return found; +} + + +static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match) +{ + int type; + const char *pos, *end; + size_t len; + + pos = match; + do { + if (os_strncmp(pos, "EMAIL:", 6) == 0) { + type = GEN_EMAIL; + pos += 6; + } else if (os_strncmp(pos, "DNS:", 4) == 0) { + type = GEN_DNS; + pos += 4; + } else if (os_strncmp(pos, "URI:", 4) == 0) { + type = GEN_URI; + pos += 4; + } else { + wpa_printf(MSG_INFO, + "TLS: Invalid altSubjectName match '%s'", + pos); + return 0; + } + end = os_strchr(pos, ';'); + while (end) { + if (os_strncmp(end + 1, "EMAIL:", 6) == 0 || + os_strncmp(end + 1, "DNS:", 4) == 0 || + os_strncmp(end + 1, "URI:", 4) == 0) + break; + end = os_strchr(end + 1, ';'); + } + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (tls_match_alt_subject_component(cert, type, pos, len) > 0) + return 1; + pos = end + 1; + } while (end); + + return 0; +} + + +static int domain_suffix_match(const char *val, size_t len, const char *match, + size_t match_len, int full) +{ + size_t i; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, + "TLS: Embedded null in a string - reject"); + return 0; + } + } + + if (match_len > len || (full && match_len != len)) + return 0; + + if (os_strncasecmp(val + len - match_len, match, match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} + + +static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match, + size_t match_len, int full) +{ + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int i; + int j; + int dns_name = 0; + WOLFSSL_X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s", + full ? "" : "suffix ", match); + + ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL); + + for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) { + gen = wolfSSL_sk_value(ext, j); + if (gen->type != ASN_DNS_TYPE) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->obj, os_strlen((char *)gen->obj)); + if (domain_suffix_match((const char *) gen->obj, + os_strlen((char *) gen->obj), match, + match_len, full) == 1) { + wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", + full ? "Match" : "Suffix match"); + wolfSSL_sk_ASN1_OBJECT_free(ext); + return 1; + } + } + wolfSSL_sk_ASN1_OBJECT_free(ext); + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = wolfSSL_X509_get_subject_name(cert); + i = -1; + for (;;) { + WOLFSSL_X509_NAME_ENTRY *e; + WOLFSSL_ASN1_STRING *cn; + + i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME, + i); + if (i == -1) + break; + e = wolfSSL_X509_NAME_get_entry(name, i); + if (!e) + continue; + cn = wolfSSL_X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { + wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", + full ? "Match" : "Suffix match"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", + full ? "" : "suffix "); + return 0; +} + + +static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +{ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; +} + + +static enum tls_fail_reason wolfssl_tls_fail_reason(int err) +{ + switch (err) { + case X509_V_ERR_CERT_REVOKED: + return TLS_FAIL_REVOKED; + case ASN_BEFORE_DATE_E: + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CRL_NOT_YET_VALID: + return TLS_FAIL_NOT_YET_VALID; + case ASN_AFTER_DATE_E: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_HAS_EXPIRED: + return TLS_FAIL_EXPIRED; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + case X509_V_ERR_INVALID_CA: + return TLS_FAIL_UNTRUSTED; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_CERT_REJECTED: + return TLS_FAIL_BAD_CERTIFICATE; + default: + return TLS_FAIL_UNSPECIFIED; + } +} + + +static const char * wolfssl_tls_err_string(int err, const char *err_str) +{ + switch (err) { + case ASN_BEFORE_DATE_E: + return "certificate is not yet valid"; + case ASN_AFTER_DATE_E: + return "certificate has expired"; + default: + return err_str; + } +} + + +static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert) +{ + struct wpabuf *buf = NULL; + const u8 *data; + int cert_len; + + data = wolfSSL_X509_get_der(cert, &cert_len); + if (!data) + buf = wpabuf_alloc_copy(data, cert_len); + + return buf; +} + + +static void wolfssl_tls_fail_event(struct tls_connection *conn, + WOLFSSL_X509 *err_cert, int err, int depth, + const char *subject, const char *err_str, + enum tls_fail_reason reason) +{ + union tls_event_data ev; + struct wpabuf *cert = NULL; + struct tls_context *context = conn->context; + + if (!context->event_cb) + return; + + cert = get_x509_cert(err_cert); + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ? + reason : wolfssl_tls_fail_reason(err); + ev.cert_fail.depth = depth; + ev.cert_fail.subject = subject; + ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str); + ev.cert_fail.cert = cert; + context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert); +} + + +static void wolfssl_tls_cert_event(struct tls_connection *conn, + WOLFSSL_X509 *err_cert, int depth, + const char *subject) +{ + struct wpabuf *cert = NULL; + union tls_event_data ev; + struct tls_context *context = conn->context; + char *alt_subject[TLS_MAX_ALT_SUBJECT]; + int alt, num_alt_subject = 0; + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int i; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + + if (!context->event_cb) + return; + + os_memset(&ev, 0, sizeof(ev)); + if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) || + context->cert_in_cb) { + cert = get_x509_cert(err_cert); + ev.peer_cert.cert = cert; + } + +#ifdef CONFIG_SHA256 + if (cert) { + const u8 *addr[1]; + size_t len[1]; + + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + + ev.peer_cert.depth = depth; + ev.peer_cert.subject = subject; + + ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL); + for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) { + char *pos; + + if (num_alt_subject == TLS_MAX_ALT_SUBJECT) + break; + gen = wolfSSL_sk_value((void *) ext, i); + if (gen->type != GEN_EMAIL && + gen->type != GEN_DNS && + gen->type != GEN_URI) + continue; + + pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1); + if (!pos) + break; + alt_subject[num_alt_subject++] = pos; + + switch (gen->type) { + case GEN_EMAIL: + os_memcpy(pos, "EMAIL:", 6); + pos += 6; + break; + case GEN_DNS: + os_memcpy(pos, "DNS:", 4); + pos += 4; + break; + case GEN_URI: + os_memcpy(pos, "URI:", 4); + pos += 4; + break; + } + + os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj)); + pos += os_strlen((char *)gen->obj); + *pos = '\0'; + } + wolfSSL_sk_ASN1_OBJECT_free(ext); + + for (alt = 0; alt < num_alt_subject; alt++) + ev.peer_cert.altsubject[alt] = alt_subject[alt]; + ev.peer_cert.num_altsubject = num_alt_subject; + + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert); + for (alt = 0; alt < num_alt_subject; alt++) + os_free(alt_subject[alt]); +} + + +static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + WOLFSSL_X509 *err_cert; + int err, depth; + WOLFSSL *ssl; + struct tls_connection *conn; + struct tls_context *context; + char *match, *altmatch, *suffix_match, *domain_match; + const char *err_str; + + err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx); + if (!err_cert) { + wpa_printf(MSG_DEBUG, "wolfSSL: No Cert"); + return 0; + } + + err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx); + depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = wolfSSL_X509_STORE_CTX_get_ex_data( + x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx()); + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf, + sizeof(buf)); + + conn = wolfSSL_get_ex_data(ssl, 0); + if (!conn) { + wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data"); + return 0; + } + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + else if (depth == 2) + conn->peer_issuer_issuer = err_cert; + + context = conn->context; + match = conn->subject_match; + altmatch = conn->alt_subject_match; + suffix_match = conn->suffix_match; + domain_match = conn->domain_match; + + if (!preverify_ok && !conn->ca_cert_verify) + preverify_ok = 1; + if (!preverify_ok && depth > 0 && conn->server_cert_only) + preverify_ok = 1; + if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) && + (err == X509_V_ERR_CERT_HAS_EXPIRED || + err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E || + err == X509_V_ERR_CERT_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Ignore certificate validity time mismatch"); + preverify_ok = 1; + } + + err_str = wolfSSL_X509_verify_cert_error_string(err); + +#ifdef CONFIG_SHA256 + /* + * Do not require preverify_ok so we can explicity allow otherwise + * invalid pinned server certificates. + */ + if (depth == 0 && conn->server_cert_only) { + struct wpabuf *cert; + + cert = get_x509_cert(err_cert); + if (!cert) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Could not fetch server certificate data"); + preverify_ok = 0; + } else { + u8 hash[32]; + const u8 *addr[1]; + size_t len[1]; + + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) < 0 || + os_memcmp(conn->srv_cert_hash, hash, 32) != 0) { + err_str = "Server certificate mismatch"; + err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + preverify_ok = 0; + } else if (!preverify_ok) { + /* + * Certificate matches pinned certificate, allow + * regardless of other problems. + */ + wpa_printf(MSG_DEBUG, + "wolfSSL: Ignore validation issues for a pinned server certificate"); + preverify_ok = 1; + } + wpabuf_free(cert); + } + } +#endif /* CONFIG_SHA256 */ + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, + "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'", + err, err_str, depth, buf); + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + err_str, TLS_FAIL_UNSPECIFIED); + return preverify_ok; + } + + wpa_printf(MSG_DEBUG, + "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", + __func__, preverify_ok, err, err_str, + conn->ca_cert_verify, depth, buf); + if (depth == 0 && match && os_strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, + "TLS: Subject '%s' did not match with '%s'", + buf, match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Subject mismatch", + TLS_FAIL_SUBJECT_MISMATCH); + } else if (depth == 0 && altmatch && + !tls_match_alt_subject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, + "TLS: altSubjectName match '%s' not found", + altmatch); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "AltSubject mismatch", + TLS_FAIL_ALTSUBJECT_MISMATCH); + } else if (depth == 0 && suffix_match && + !tls_match_suffix(err_cert, suffix_match, 0)) { + wpa_printf(MSG_WARNING, + "TLS: Domain suffix match '%s' not found", + suffix_match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); + } else if (depth == 0 && domain_match && + !tls_match_suffix(err_cert, domain_match, 1)) { + wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", + domain_match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain mismatch", + TLS_FAIL_DOMAIN_MISMATCH); + } else { + wolfssl_tls_cert_event(conn, err_cert, depth, buf); + } + + if (conn->cert_probe && preverify_ok && depth == 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Reject server certificate on probe-only run"); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Server certificate chain probe", + TLS_FAIL_SERVER_CHAIN_PROBE); + } + +#ifdef HAVE_OCSP_WOLFSSL + if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && + preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, + conn->peer_issuer, + conn->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "certificate revoked", + TLS_FAIL_REVOKED); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && + (conn->flags & TLS_CONN_REQUIRE_OCSP)) { + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } + } +#endif /* HAVE_OCSP_WOLFSSL */ + if (depth == 0 && preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + + return preverify_ok; +} + + +static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, + const char *ca_cert, + const u8 *ca_cert_blob, size_t blob_len, + const char *ca_path) +{ + WOLFSSL_CTX *ctx = tls_ctx; + + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + + if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Probe for server certificate chain"); + conn->cert_probe = 1; + conn->ca_cert_verify = 0; + return 0; + } + + if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) { +#ifdef CONFIG_SHA256 + const char *pos = ca_cert + 7; + + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Unsupported ca_cert hash value '%s'", + ca_cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'", + ca_cert); + return -1; + } + if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'", + ca_cert); + return -1; + } + conn->server_cert_only = 1; + wpa_printf(MSG_DEBUG, + "wolfSSL: Checking only server certificate match"); + return 0; +#else /* CONFIG_SHA256 */ + wpa_printf(MSG_INFO, + "No SHA256 included in the build - cannot validate server certificate hash"); + return -1; +#endif /* CONFIG_SHA256 */ + } + + if (ca_cert_blob) { + if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len, + SSL_FILETYPE_ASN1) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, "SSL: failed to load CA blob"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK"); + return 0; + } + + if (ca_cert || ca_path) { + WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new(); + + if (!cm) { + wpa_printf(MSG_INFO, + "SSL: failed to create certificate store"); + return -1; + } + wolfSSL_CTX_set_cert_store(ctx, cm); + + if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, + "SSL: failed to load ca_cert as PEM"); + + if (!ca_cert) + return -1; + + if (wolfSSL_CTX_der_load_verify_locations( + ctx, ca_cert, SSL_FILETYPE_ASN1) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, + "SSL: failed to load ca_cert as DER"); + return -1; + } + } + return 0; + } + + conn->ca_cert_verify = 0; + return 0; +} + + +static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags) +{ +#ifdef HAVE_SESSION_TICKET +#if 0 + if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET)) + wolfSSL_UseSessionTicket(ssl); +#endif +#endif /* HAVE_SESSION_TICKET */ + + if (flags & TLS_CONN_DISABLE_TLSv1_0) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1); + if (flags & TLS_CONN_DISABLE_TLSv1_1) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + if (flags & TLS_CONN_DISABLE_TLSv1_2) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2); +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + wpa_printf(MSG_DEBUG, "SSL: set params"); + + if (tls_connection_set_subject_match(conn, params->subject_match, + params->altsubject_match, + params->suffix_match, + params->domain_match) < 0) { + wpa_printf(MSG_INFO, "Error setting subject match"); + return -1; + } + + if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path) < 0) { + wpa_printf(MSG_INFO, "Error setting CA cert"); + return -1; + } + + if (tls_connection_client_cert(conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting client cert"); + return -1; + } + + if (tls_connection_private_key(tls_ctx, conn, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting private key"); + return -1; + } + + if (tls_connection_dh(conn, params->dh_file, params->dh_blob, + params->dh_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting DH"); + return -1; + } + + if (params->openssl_ciphers && + wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "wolfSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + + tls_set_conn_flags(conn->ssl, params->flags); + +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST + if (params->flags & TLS_CONN_REQUEST_OCSP) { + if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP, + WOLFSSL_CSR_OCSP_USE_NONCE) != + SSL_SUCCESS) + return -1; + wolfSSL_CTX_EnableOCSP(tls_ctx, 0); + } +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */ +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 + if (params->flags & TLS_CONN_REQUEST_OCSP) { + if (wolfSSL_UseOCSPStaplingV2(conn->ssl, + WOLFSSL_CSR2_OCSP_MULTI, 0) != + SSL_SUCCESS) + return -1; + wolfSSL_CTX_EnableOCSP(tls_ctx, 0); + } +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ +#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ + !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) + wolfSSL_CTX_EnableOCSP(ctx, 0); +#else /* HAVE_OCSP */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "wolfSSL: No OCSP support included - reject configuration"); + return -1; + } + if (params->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "wolfSSL: No OCSP support included - allow optional OCSP case to continue"); + } +#endif /* HAVE_OCSP */ +#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST && + * !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ + + conn->flags = params->flags; + + return 0; +} + + +static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (ca_cert) { + if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1) + { + wpa_printf(MSG_WARNING, + "Failed to load root certificates"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "TLS: Trusted root certificate(s) loaded"); + } + + return 0; +} + + +static int tls_global_client_cert(void *ssl_ctx, const char *client_cert) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (!client_cert) + return 0; + + if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert, + SSL_FILETYPE_ASN1) != + SSL_SUCCESS && + wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, "Failed to load client certificate"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s", + client_cert); + + return 0; +} + + +static int tls_global_private_key(void *ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + char *passwd = NULL; + int ret = 0; + + if (!private_key) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (!passwd) + return -1; + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb); + wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd); + + if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key, + SSL_FILETYPE_ASN1) != 1 && + wolfSSL_CTX_use_PrivateKey_file(ctx, private_key, + SSL_FILETYPE_PEM) != 1) { + wpa_printf(MSG_INFO, "Failed to load private key"); + ret = -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Loaded global private key"); + + os_free(passwd); + wolfSSL_CTX_set_default_passwd_cb(ctx, NULL); + + return ret; +} + + +static int tls_global_dh(void *ssl_ctx, const char *dh_file, + const u8 *dh_blob, size_t blob_len) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (!dh_file && !dh_blob) + return 0; + + if (dh_blob) { + if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK"); + return 0; + } + + if (dh_file) { + if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) < + 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH PEM file failed"); + if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: global use DH file OK"); + return 0; + } + + return 0; +} + + +#ifdef HAVE_OCSP + +int ocsp_status_cb(void *unused, const char *url, int url_sz, + unsigned char *request, int request_sz, + unsigned char **response) +{ + size_t len; + + (void) unused; + + if (!url) { + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - no response configured"); + *response = NULL; + return 0; + } + + *response = (unsigned char *) os_readfile(url, &len); + if (!*response) { + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - could not read response file"); + return -1; + } + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - send cached response"); + return len; +} + + +void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response) +{ + os_free(response); +} + +#endif /* HAVE_OCSP */ + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + wpa_printf(MSG_DEBUG, "SSL: global set params"); + + if (params->check_cert_subject) + return -1; /* not yet supported */ + + if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", + params->ca_cert); + return -1; + } + + if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) { + wpa_printf(MSG_INFO, + "SSL: Failed to load client cert file '%s'", + params->client_cert); + return -1; + } + + if (tls_global_private_key(tls_ctx, params->private_key, + params->private_key_passwd) < 0) { + wpa_printf(MSG_INFO, + "SSL: Failed to load private key file '%s'", + params->private_key); + return -1; + } + + if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob, + params->dh_blob_len) < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'", + params->dh_file); + return -1; + } + + if (params->openssl_ciphers && + wolfSSL_CTX_set_cipher_list(tls_ctx, + params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "wolfSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "wolfSSL: openssl_ecdh_curves not supported"); + return -1; + } + +#ifdef HAVE_SESSION_TICKET + /* Session ticket is off by default - can't disable once on. */ + if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) + wolfSSL_CTX_UseSessionTicket(tls_ctx); +#endif /* HAVE_SESSION_TICKET */ + +#ifdef HAVE_OCSP + if (params->ocsp_stapling_response) { + wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx, + params->ocsp_stapling_response); + wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb, + ocsp_resp_free_cb, NULL); + } +#endif /* HAVE_OCSP */ + + return 0; +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) +{ + wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl); + + if (check_crl) { + /* Hack to Enable CRLs. */ + wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM); + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) +{ + if (!conn) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer); + + if (verify_peer) { + conn->ca_cert_verify = 1; + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + tls_verify_cb); + } else { + conn->ca_cert_verify = 0; + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + wolfSSL_set_accept_state(conn->ssl); + + /* TODO: do we need to fake a session like OpenSSL does here? */ + + return 0; +} + + +static struct wpabuf * wolfssl_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + int server) +{ + int res; + + wolfssl_reset_out_data(&conn->output); + + /* Initiate TLS handshake or continue the existing handshake */ + if (server) { + wolfSSL_set_accept_state(conn->ssl); + res = wolfSSL_accept(conn->ssl); + wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res); + } else { + wolfSSL_set_connect_state(conn->ssl); + res = wolfSSL_connect(conn->ssl); + wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res); + } + + if (res != 1) { + int err = wolfSSL_get_error(conn->ssl, res); + + if (err == SSL_ERROR_WANT_READ) { + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - want more data"); + } else if (err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - want to write"); + } else { + char msg[80]; + + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - failed %s", + wolfSSL_ERR_error_string(err, msg)); + conn->failed++; + } + } + + return conn->output.out_data; +} + + +static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn, + size_t max_len) +{ + int res; + struct wpabuf *appl_data = wpabuf_alloc(max_len + 100); + + if (!appl_data) + return NULL; + + res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data), + wpabuf_size(appl_data)); + if (res < 0) { + int err = wolfSSL_get_error(conn->ssl, res); + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, + "SSL: No Application Data included"); + } else { + char msg[80]; + + wpa_printf(MSG_DEBUG, + "Failed to read possible Application Data %s", + wolfSSL_ERR_error_string(err, msg)); + } + + wpabuf_free(appl_data); + return NULL; + } + + wpabuf_put(appl_data, res); + wpa_hexdump_buf_key(MSG_MSGDUMP, + "SSL: Application Data in Finished message", + appl_data); + return appl_data; +} + + +static struct wpabuf * +wolfssl_connection_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, int server) +{ + struct wpabuf *out_data; + + wolfssl_reset_in_data(&conn->input, in_data); + + if (appl_data) + *appl_data = NULL; + + out_data = wolfssl_handshake(conn, in_data, server); + if (!out_data) + return NULL; + + if (wolfSSL_is_init_finished(conn->ssl)) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Handshake finished - resumed=%d", + tls_connection_resumed(NULL, conn)); + if (appl_data && in_data) + *appl_data = wolfssl_get_appl_data(conn, + wpabuf_len(in_data)); + } + + return out_data; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return wolfssl_connection_handshake(conn, in_data, appl_data, 0); +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return wolfssl_connection_handshake(conn, in_data, appl_data, 1); +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + + if (!conn) + return NULL; + + wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data)); + + wolfssl_reset_out_data(&conn->output); + + res = wolfSSL_write(conn->ssl, wpabuf_head(in_data), + wpabuf_len(in_data)); + if (res < 0) { + int err = wolfSSL_get_error(conn->ssl, res); + char msg[80]; + + wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s", + wolfSSL_ERR_error_string(err, msg)); + return NULL; + } + + return conn->output.out_data; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + struct wpabuf *buf; + + if (!conn) + return NULL; + + wpa_printf(MSG_DEBUG, "SSL: decrypt"); + + wolfssl_reset_in_data(&conn->input, in_data); + + /* Read decrypted data for further processing */ + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (!buf) + return NULL; + res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf)); + if (res < 0) { + wpa_printf(MSG_INFO, "Decryption failed - SSL_read"); + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + + wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf)); + + return buf; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ + return conn ? wolfSSL_session_reused(conn->ssl) : 0; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + char buf[128], *pos, *end; + u8 *c; + int ret; + + if (!conn || !conn->ssl || !ciphers) + return -1; + + buf[0] = '\0'; + pos = buf; + end = pos + sizeof(buf); + + c = ciphers; + while (*c != TLS_CIPHER_NONE) { + const char *suite; + + switch (*c) { + case TLS_CIPHER_RC4_SHA: + suite = "RC4-SHA"; + break; + case TLS_CIPHER_AES128_SHA: + suite = "AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES128_SHA: + suite = "DHE-RSA-AES128-SHA"; + break; + case TLS_CIPHER_ANON_DH_AES128_SHA: + suite = "ADH-AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES256_SHA: + suite = "DHE-RSA-AES256-SHA"; + break; + case TLS_CIPHER_AES256_SHA: + suite = "AES256-SHA"; + break; + default: + wpa_printf(MSG_DEBUG, + "TLS: Unsupported cipher selection: %d", *c); + return -1; + } + ret = os_snprintf(pos, end - pos, ":%s", suite); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + + c++; + } + + wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1); + + if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) { + wpa_printf(MSG_DEBUG, "Cipher suite configuration failed"); + return -1; + } + + return 0; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + WOLFSSL_CIPHER *cipher; + const char *name; + + if (!conn || !conn->ssl) + return -1; + + cipher = wolfSSL_get_current_cipher(conn->ssl); + if (!cipher) + return -1; + + name = wolfSSL_CIPHER_get_name(cipher); + if (!name) + return -1; + + if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0) + os_strlcpy(buf, "RC4-SHA", buflen); + else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "ADH-AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0) + os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen); + else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0) + os_strlcpy(buf, "AES256-SHA", buflen); + else + os_strlcpy(buf, name, buflen); + + return 0; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + /* no empty fragments in wolfSSL for now */ + return 0; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return -1; + + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return -1; + + /* TODO: this is not incremented anywhere */ + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + if (!conn) + return -1; + + /* TODO: this is not incremented anywhere */ + return conn->write_alerts; +} + + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s", + WOLFSSL_VERSION, wolfSSL_lib_version()); +} + +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + + if (!conn || !conn->ssl) + return -1; + + name = wolfSSL_get_version(conn->ssl); + if (!name) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) +{ + WOLFSSL *ssl; + + if (!conn || !keys) + return -1; + ssl = conn->ssl; + if (!ssl) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + keys->client_random = conn->client_random; + keys->client_random_len = wolfSSL_get_client_random( + ssl, conn->client_random, sizeof(conn->client_random)); + keys->server_random = conn->server_random; + keys->server_random_len = wolfSSL_get_server_random( + ssl, conn->server_random, sizeof(conn->server_random)); + + return 0; +} + + +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) +{ + if (context) + return -1; + if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0) + return -1; + return 0; +} + + +#define SEED_LEN (RAN_LEN + RAN_LEN) + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ + byte seed[SEED_LEN]; + int ret = -1; + WOLFSSL *ssl; + byte *tmp_out; + byte *_out; + int skip = 0; + byte *master_key; + unsigned int master_key_len; + byte *server_random; + unsigned int server_len; + byte *client_random; + unsigned int client_len; + + if (!conn || !conn->ssl) + return -1; + ssl = conn->ssl; + + skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) + + wolfSSL_GetIVSize(ssl)); + + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + + wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random, + &server_len, &client_random, &client_len); + os_memcpy(seed, server_random, RAN_LEN); + os_memcpy(seed + RAN_LEN, client_random, RAN_LEN); + + if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) { + tls_prf_sha256(master_key, master_key_len, + "key expansion", seed, sizeof(seed), + _out, skip + out_len); + ret = 0; + } else { + ret = tls_prf_sha1_md5(master_key, master_key_len, + "key expansion", seed, sizeof(seed), + _out, skip + out_len); + } + + os_memset(master_key, 0, master_key_len); + if (ret == 0) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip + out_len); + + return ret; +} + + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + (void) ssl_ctx; + + if (!conn || !conn->ssl || ext_type != 35) + return -1; + + if (wolfSSL_set_SessionTicket(conn->ssl, data, + (unsigned int) data_len) != 1) + return -1; + + return 0; +} + + +static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg) +{ + struct tls_connection *conn = arg; + int ret; + unsigned char client_random[RAN_LEN]; + unsigned char server_random[RAN_LEN]; + word32 ticket_len = sizeof(conn->session_ticket); + + if (!conn || !conn->session_ticket_cb) + return 1; + + if (wolfSSL_get_client_random(s, client_random, + sizeof(client_random)) == 0 || + wolfSSL_get_server_random(s, server_random, + sizeof(server_random)) == 0 || + wolfSSL_get_SessionTicket(s, conn->session_ticket, + &ticket_len) != 1) + return 1; + + if (ticket_len == 0) + return 0; + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, ticket_len, + client_random, server_random, secret); + if (ret <= 0) + return 1; + + *secret_len = SECRET_LEN; + return 0; +} + +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; + + if (cb) { + if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; + } else { + if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; + } + + return 0; +#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + return -1; +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ + wpa_printf(MSG_DEBUG, + "wolfSSL: Success data accepted for resumed session"); +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ + WOLFSSL_SESSION *sess; + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) + return; + + wolfSSL_SSL_SESSION_set_timeout(sess, 0); + wpa_printf(MSG_DEBUG, + "wolfSSL: Removed cached session to disable session resumption"); +} + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ + WOLFSSL_SESSION *sess; + struct wpabuf *old; + + wpa_printf(MSG_DEBUG, "wolfSSL: Set success data"); + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) { + wpa_printf(MSG_DEBUG, + "wolfSSL: No session found for success data"); + goto fail; + } + + old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (old) { + wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p", + old); + wpabuf_free(old); + } + if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + goto fail; + + wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data); + conn->success_data = 1; + return; + +fail: + wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data"); + wpabuf_free(data); +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + WOLFSSL_SESSION *sess; + + wpa_printf(MSG_DEBUG, "wolfSSL: Get success data"); + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) + return NULL; + return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); +} |