michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Diffie-Hellman parameter generation, key generation, and secret derivation. michael@0: * KEA secret generation and verification. michael@0: */ michael@0: #ifdef FREEBL_NO_DEPEND michael@0: #include "stubs.h" michael@0: #endif michael@0: michael@0: #include "prerr.h" michael@0: #include "secerr.h" michael@0: michael@0: #include "blapi.h" michael@0: #include "secitem.h" michael@0: #include "mpi.h" michael@0: #include "mpprime.h" michael@0: #include "secmpi.h" michael@0: michael@0: #define KEA_DERIVED_SECRET_LEN 128 michael@0: michael@0: /* Lengths are in bytes. */ michael@0: static unsigned int michael@0: dh_GetSecretKeyLen(unsigned int primeLen) michael@0: { michael@0: /* Based on Table 2 in NIST SP 800-57. */ michael@0: if (primeLen >= 1920) { /* 15360 bits */ michael@0: return 64; /* 512 bits */ michael@0: } michael@0: if (primeLen >= 960) { /* 7680 bits */ michael@0: return 48; /* 384 bits */ michael@0: } michael@0: if (primeLen >= 384) { /* 3072 bits */ michael@0: return 32; /* 256 bits */ michael@0: } michael@0: if (primeLen >= 256) { /* 2048 bits */ michael@0: return 28; /* 224 bits */ michael@0: } michael@0: return 20; /* 160 bits */ michael@0: } michael@0: michael@0: SECStatus michael@0: DH_GenParam(int primeLen, DHParams **params) michael@0: { michael@0: PLArenaPool *arena; michael@0: DHParams *dhparams; michael@0: unsigned char *pb = NULL; michael@0: unsigned char *ab = NULL; michael@0: unsigned long counter = 0; michael@0: mp_int p, q, a, h, psub1, test; michael@0: mp_err err = MP_OKAY; michael@0: SECStatus rv = SECSuccess; michael@0: if (!params || primeLen < 0) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE); michael@0: if (!arena) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: dhparams = (DHParams *)PORT_ArenaZAlloc(arena, sizeof(DHParams)); michael@0: if (!dhparams) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: return SECFailure; michael@0: } michael@0: dhparams->arena = arena; michael@0: MP_DIGITS(&p) = 0; michael@0: MP_DIGITS(&q) = 0; michael@0: MP_DIGITS(&a) = 0; michael@0: MP_DIGITS(&h) = 0; michael@0: MP_DIGITS(&psub1) = 0; michael@0: MP_DIGITS(&test) = 0; michael@0: CHECK_MPI_OK( mp_init(&p) ); michael@0: CHECK_MPI_OK( mp_init(&q) ); michael@0: CHECK_MPI_OK( mp_init(&a) ); michael@0: CHECK_MPI_OK( mp_init(&h) ); michael@0: CHECK_MPI_OK( mp_init(&psub1) ); michael@0: CHECK_MPI_OK( mp_init(&test) ); michael@0: /* generate prime with MPI, uses Miller-Rabin to generate strong prime. */ michael@0: pb = PORT_Alloc(primeLen); michael@0: CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(pb, primeLen) ); michael@0: pb[0] |= 0x80; /* set high-order bit */ michael@0: pb[primeLen-1] |= 0x01; /* set low-order bit */ michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&p, pb, primeLen) ); michael@0: CHECK_MPI_OK( mpp_make_prime(&p, primeLen * 8, PR_TRUE, &counter) ); michael@0: /* construct Sophie-Germain prime q = (p-1)/2. */ michael@0: CHECK_MPI_OK( mp_sub_d(&p, 1, &psub1) ); michael@0: CHECK_MPI_OK( mp_div_2(&psub1, &q) ); michael@0: /* construct a generator from the prime. */ michael@0: ab = PORT_Alloc(primeLen); michael@0: /* generate a candidate number a in p's field */ michael@0: CHECK_SEC_OK( RNG_GenerateGlobalRandomBytes(ab, primeLen) ); michael@0: CHECK_MPI_OK( mp_read_unsigned_octets(&a, ab, primeLen) ); michael@0: /* force a < p (note that quot(a/p) <= 1) */ michael@0: if ( mp_cmp(&a, &p) > 0 ) michael@0: CHECK_MPI_OK( mp_sub(&a, &p, &a) ); michael@0: do { michael@0: /* check that a is in the range [2..p-1] */ michael@0: if ( mp_cmp_d(&a, 2) < 0 || mp_cmp(&a, &psub1) >= 0) { michael@0: /* a is outside of the allowed range. Set a=3 and keep going. */ michael@0: mp_set(&a, 3); michael@0: } michael@0: /* if a**q mod p != 1 then a is a generator */ michael@0: CHECK_MPI_OK( mp_exptmod(&a, &q, &p, &test) ); michael@0: if ( mp_cmp_d(&test, 1) != 0 ) michael@0: break; michael@0: /* increment the candidate and try again. */ michael@0: CHECK_MPI_OK( mp_add_d(&a, 1, &a) ); michael@0: } while (PR_TRUE); michael@0: MPINT_TO_SECITEM(&p, &dhparams->prime, arena); michael@0: MPINT_TO_SECITEM(&a, &dhparams->base, arena); michael@0: *params = dhparams; michael@0: cleanup: michael@0: mp_clear(&p); michael@0: mp_clear(&q); michael@0: mp_clear(&a); michael@0: mp_clear(&h); michael@0: mp_clear(&psub1); michael@0: mp_clear(&test); michael@0: if (pb) PORT_ZFree(pb, primeLen); michael@0: if (ab) PORT_ZFree(ab, primeLen); michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: rv = SECFailure; michael@0: } michael@0: if (rv) michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: DH_NewKey(DHParams *params, DHPrivateKey **privKey) michael@0: { michael@0: PLArenaPool *arena; michael@0: DHPrivateKey *key; michael@0: mp_int g, xa, p, Ya; michael@0: mp_err err = MP_OKAY; michael@0: SECStatus rv = SECSuccess; michael@0: if (!params || !privKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE); michael@0: if (!arena) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: key = (DHPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(DHPrivateKey)); michael@0: if (!key) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: return SECFailure; michael@0: } michael@0: key->arena = arena; michael@0: MP_DIGITS(&g) = 0; michael@0: MP_DIGITS(&xa) = 0; michael@0: MP_DIGITS(&p) = 0; michael@0: MP_DIGITS(&Ya) = 0; michael@0: CHECK_MPI_OK( mp_init(&g) ); michael@0: CHECK_MPI_OK( mp_init(&xa) ); michael@0: CHECK_MPI_OK( mp_init(&p) ); michael@0: CHECK_MPI_OK( mp_init(&Ya) ); michael@0: /* Set private key's p */ michael@0: CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->prime, ¶ms->prime) ); michael@0: SECITEM_TO_MPINT(key->prime, &p); michael@0: /* Set private key's g */ michael@0: CHECK_SEC_OK( SECITEM_CopyItem(arena, &key->base, ¶ms->base) ); michael@0: SECITEM_TO_MPINT(key->base, &g); michael@0: /* Generate private key xa */ michael@0: SECITEM_AllocItem(arena, &key->privateValue, michael@0: dh_GetSecretKeyLen(params->prime.len)); michael@0: RNG_GenerateGlobalRandomBytes(key->privateValue.data, michael@0: key->privateValue.len); michael@0: SECITEM_TO_MPINT( key->privateValue, &xa ); michael@0: /* xa < p */ michael@0: CHECK_MPI_OK( mp_mod(&xa, &p, &xa) ); michael@0: /* Compute public key Ya = g ** xa mod p */ michael@0: CHECK_MPI_OK( mp_exptmod(&g, &xa, &p, &Ya) ); michael@0: MPINT_TO_SECITEM(&Ya, &key->publicValue, key->arena); michael@0: *privKey = key; michael@0: cleanup: michael@0: mp_clear(&g); michael@0: mp_clear(&xa); michael@0: mp_clear(&p); michael@0: mp_clear(&Ya); michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: rv = SECFailure; michael@0: } michael@0: if (rv) michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: DH_Derive(SECItem *publicValue, michael@0: SECItem *prime, michael@0: SECItem *privateValue, michael@0: SECItem *derivedSecret, michael@0: unsigned int outBytes) michael@0: { michael@0: mp_int p, Xa, Yb, ZZ, psub1; michael@0: mp_err err = MP_OKAY; michael@0: int len = 0; michael@0: unsigned int nb; michael@0: unsigned char *secret = NULL; michael@0: if (!publicValue || !prime || !privateValue || !derivedSecret) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: memset(derivedSecret, 0, sizeof *derivedSecret); michael@0: MP_DIGITS(&p) = 0; michael@0: MP_DIGITS(&Xa) = 0; michael@0: MP_DIGITS(&Yb) = 0; michael@0: MP_DIGITS(&ZZ) = 0; michael@0: MP_DIGITS(&psub1) = 0; michael@0: CHECK_MPI_OK( mp_init(&p) ); michael@0: CHECK_MPI_OK( mp_init(&Xa) ); michael@0: CHECK_MPI_OK( mp_init(&Yb) ); michael@0: CHECK_MPI_OK( mp_init(&ZZ) ); michael@0: CHECK_MPI_OK( mp_init(&psub1) ); michael@0: SECITEM_TO_MPINT(*publicValue, &Yb); michael@0: SECITEM_TO_MPINT(*privateValue, &Xa); michael@0: SECITEM_TO_MPINT(*prime, &p); michael@0: CHECK_MPI_OK( mp_sub_d(&p, 1, &psub1) ); michael@0: michael@0: /* We assume that the modulus, p, is a safe prime. That is, p = 2q+1 where michael@0: * q is also a prime. Thus the orders of the subgroups are factors of 2q: michael@0: * namely 1, 2, q and 2q. michael@0: * michael@0: * We check that the peer's public value isn't zero (which isn't in the michael@0: * group), one (subgroup of order one) or p-1 (subgroup of order 2). We michael@0: * also check that the public value is less than p, to avoid being fooled michael@0: * by values like p+1 or 2*p-1. michael@0: * michael@0: * Thus we must be operating in the subgroup of size q or 2q. */ michael@0: if (mp_cmp_d(&Yb, 1) <= 0 || michael@0: mp_cmp(&Yb, &psub1) >= 0) { michael@0: err = MP_BADARG; michael@0: goto cleanup; michael@0: } michael@0: michael@0: /* ZZ = (Yb)**Xa mod p */ michael@0: CHECK_MPI_OK( mp_exptmod(&Yb, &Xa, &p, &ZZ) ); michael@0: /* number of bytes in the derived secret */ michael@0: len = mp_unsigned_octet_size(&ZZ); michael@0: if (len <= 0) { michael@0: err = MP_BADARG; michael@0: goto cleanup; michael@0: } michael@0: /* allocate a buffer which can hold the entire derived secret. */ michael@0: secret = PORT_Alloc(len); michael@0: /* grab the derived secret */ michael@0: err = mp_to_unsigned_octets(&ZZ, secret, len); michael@0: if (err >= 0) err = MP_OKAY; michael@0: /* michael@0: ** if outBytes is 0 take all of the bytes from the derived secret. michael@0: ** if outBytes is not 0 take exactly outBytes from the derived secret, zero michael@0: ** pad at the beginning if necessary, and truncate beginning bytes michael@0: ** if necessary. michael@0: */ michael@0: if (outBytes > 0) michael@0: nb = outBytes; michael@0: else michael@0: nb = len; michael@0: SECITEM_AllocItem(NULL, derivedSecret, nb); michael@0: if (len < nb) { michael@0: unsigned int offset = nb - len; michael@0: memset(derivedSecret->data, 0, offset); michael@0: memcpy(derivedSecret->data + offset, secret, len); michael@0: } else { michael@0: memcpy(derivedSecret->data, secret + len - nb, nb); michael@0: } michael@0: cleanup: michael@0: mp_clear(&p); michael@0: mp_clear(&Xa); michael@0: mp_clear(&Yb); michael@0: mp_clear(&ZZ); michael@0: mp_clear(&psub1); michael@0: if (secret) { michael@0: /* free the buffer allocated for the full secret. */ michael@0: PORT_ZFree(secret, len); michael@0: } michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: if (derivedSecret->data) michael@0: PORT_ZFree(derivedSecret->data, derivedSecret->len); michael@0: return SECFailure; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: KEA_Derive(SECItem *prime, michael@0: SECItem *public1, michael@0: SECItem *public2, michael@0: SECItem *private1, michael@0: SECItem *private2, michael@0: SECItem *derivedSecret) michael@0: { michael@0: mp_int p, Y, R, r, x, t, u, w; michael@0: mp_err err; michael@0: unsigned char *secret = NULL; michael@0: unsigned int len = 0, offset; michael@0: if (!prime || !public1 || !public2 || !private1 || !private2 || michael@0: !derivedSecret) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: memset(derivedSecret, 0, sizeof *derivedSecret); michael@0: MP_DIGITS(&p) = 0; michael@0: MP_DIGITS(&Y) = 0; michael@0: MP_DIGITS(&R) = 0; michael@0: MP_DIGITS(&r) = 0; michael@0: MP_DIGITS(&x) = 0; michael@0: MP_DIGITS(&t) = 0; michael@0: MP_DIGITS(&u) = 0; michael@0: MP_DIGITS(&w) = 0; michael@0: CHECK_MPI_OK( mp_init(&p) ); michael@0: CHECK_MPI_OK( mp_init(&Y) ); michael@0: CHECK_MPI_OK( mp_init(&R) ); michael@0: CHECK_MPI_OK( mp_init(&r) ); michael@0: CHECK_MPI_OK( mp_init(&x) ); michael@0: CHECK_MPI_OK( mp_init(&t) ); michael@0: CHECK_MPI_OK( mp_init(&u) ); michael@0: CHECK_MPI_OK( mp_init(&w) ); michael@0: SECITEM_TO_MPINT(*prime, &p); michael@0: SECITEM_TO_MPINT(*public1, &Y); michael@0: SECITEM_TO_MPINT(*public2, &R); michael@0: SECITEM_TO_MPINT(*private1, &r); michael@0: SECITEM_TO_MPINT(*private2, &x); michael@0: /* t = DH(Y, r, p) = Y ** r mod p */ michael@0: CHECK_MPI_OK( mp_exptmod(&Y, &r, &p, &t) ); michael@0: /* u = DH(R, x, p) = R ** x mod p */ michael@0: CHECK_MPI_OK( mp_exptmod(&R, &x, &p, &u) ); michael@0: /* w = (t + u) mod p */ michael@0: CHECK_MPI_OK( mp_addmod(&t, &u, &p, &w) ); michael@0: /* allocate a buffer for the full derived secret */ michael@0: len = mp_unsigned_octet_size(&w); michael@0: secret = PORT_Alloc(len); michael@0: /* grab the secret */ michael@0: err = mp_to_unsigned_octets(&w, secret, len); michael@0: if (err > 0) err = MP_OKAY; michael@0: /* allocate output buffer */ michael@0: SECITEM_AllocItem(NULL, derivedSecret, KEA_DERIVED_SECRET_LEN); michael@0: memset(derivedSecret->data, 0, derivedSecret->len); michael@0: /* copy in the 128 lsb of the secret */ michael@0: if (len >= KEA_DERIVED_SECRET_LEN) { michael@0: memcpy(derivedSecret->data, secret + (len - KEA_DERIVED_SECRET_LEN), michael@0: KEA_DERIVED_SECRET_LEN); michael@0: } else { michael@0: offset = KEA_DERIVED_SECRET_LEN - len; michael@0: memcpy(derivedSecret->data + offset, secret, len); michael@0: } michael@0: cleanup: michael@0: mp_clear(&p); michael@0: mp_clear(&Y); michael@0: mp_clear(&R); michael@0: mp_clear(&r); michael@0: mp_clear(&x); michael@0: mp_clear(&t); michael@0: mp_clear(&u); michael@0: mp_clear(&w); michael@0: if (secret) michael@0: PORT_ZFree(secret, len); michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: return SECFailure; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: PRBool michael@0: KEA_Verify(SECItem *Y, SECItem *prime, SECItem *subPrime) michael@0: { michael@0: mp_int p, q, y, r; michael@0: mp_err err; michael@0: int cmp = 1; /* default is false */ michael@0: if (!Y || !prime || !subPrime) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: MP_DIGITS(&p) = 0; michael@0: MP_DIGITS(&q) = 0; michael@0: MP_DIGITS(&y) = 0; michael@0: MP_DIGITS(&r) = 0; michael@0: CHECK_MPI_OK( mp_init(&p) ); michael@0: CHECK_MPI_OK( mp_init(&q) ); michael@0: CHECK_MPI_OK( mp_init(&y) ); michael@0: CHECK_MPI_OK( mp_init(&r) ); michael@0: SECITEM_TO_MPINT(*prime, &p); michael@0: SECITEM_TO_MPINT(*subPrime, &q); michael@0: SECITEM_TO_MPINT(*Y, &y); michael@0: /* compute r = y**q mod p */ michael@0: CHECK_MPI_OK( mp_exptmod(&y, &q, &p, &r) ); michael@0: /* compare to 1 */ michael@0: cmp = mp_cmp_d(&r, 1); michael@0: cleanup: michael@0: mp_clear(&p); michael@0: mp_clear(&q); michael@0: mp_clear(&y); michael@0: mp_clear(&r); michael@0: if (err) { michael@0: MP_TO_SEC_ERROR(err); michael@0: return PR_FALSE; michael@0: } michael@0: return (cmp == 0) ? PR_TRUE : PR_FALSE; michael@0: }