diff options
Diffstat (limited to 'security/nss/lib/ssl/sslprimitive.c')
-rw-r--r-- | security/nss/lib/ssl/sslprimitive.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/sslprimitive.c b/security/nss/lib/ssl/sslprimitive.c new file mode 100644 index 000000000..540c17840 --- /dev/null +++ b/security/nss/lib/ssl/sslprimitive.c @@ -0,0 +1,274 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * SSL Primitives: Public HKDF and AEAD Functions + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "keyhi.h" +#include "pk11pub.h" +#include "sechash.h" +#include "ssl.h" +#include "sslexp.h" +#include "sslerr.h" +#include "sslproto.h" + +#include "sslimpl.h" +#include "tls13con.h" +#include "tls13hkdf.h" + +struct SSLAeadContextStr { + CK_MECHANISM_TYPE mech; + ssl3KeyMaterial keys; +}; + +static SECStatus +tls13_GetHashAndCipher(PRUint16 version, PRUint16 cipherSuite, + SSLHashType *hash, const ssl3BulkCipherDef **cipher) +{ + if (version < SSL_LIBRARY_VERSION_TLS_1_3) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + // Lookup and check the suite. + SSLVersionRange vrange = { version, version }; + if (!ssl3_CipherSuiteAllowedForVersionRange(cipherSuite, &vrange)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + const ssl3CipherSuiteDef *suiteDef = ssl_LookupCipherSuiteDef(cipherSuite); + const ssl3BulkCipherDef *cipherDef = ssl_GetBulkCipherDef(suiteDef); + if (cipherDef->type != type_aead) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + *hash = suiteDef->prf_hash; + if (cipher != NULL) { + *cipher = cipherDef; + } + return SECSuccess; +} + +SECStatus +SSLExp_MakeAead(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *secret, + const char *labelPrefix, unsigned int labelPrefixLen, + SSLAeadContext **ctx) +{ + SSLAeadContext *out = NULL; + char label[255]; // Maximum length label. + static const char *const keySuffix = "key"; + static const char *const ivSuffix = "iv"; + + PORT_Assert(strlen(keySuffix) >= strlen(ivSuffix)); + if (secret == NULL || ctx == NULL || + (labelPrefix == NULL && labelPrefixLen > 0) || + labelPrefixLen + strlen(keySuffix) > sizeof(label)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + + SSLHashType hash; + const ssl3BulkCipherDef *cipher; + SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite, + &hash, &cipher); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + + out = PORT_ZNew(SSLAeadContext); + if (out == NULL) { + goto loser; + } + out->mech = ssl3_Alg2Mech(cipher->calg); + + memcpy(label, labelPrefix, labelPrefixLen); + memcpy(label + labelPrefixLen, ivSuffix, strlen(ivSuffix)); + unsigned int labelLen = labelPrefixLen + strlen(ivSuffix); + unsigned int ivLen = cipher->iv_size + cipher->explicit_nonce_size; + rv = tls13_HkdfExpandLabelRaw(secret, hash, + NULL, 0, // Handshake hash. + label, labelLen, + out->keys.iv, ivLen); + if (rv != SECSuccess) { + goto loser; + } + + memcpy(label + labelPrefixLen, keySuffix, strlen(keySuffix)); + labelLen = labelPrefixLen + strlen(keySuffix); + rv = tls13_HkdfExpandLabel(secret, hash, + NULL, 0, // Handshake hash. + label, labelLen, + out->mech, cipher->key_size, &out->keys.key); + if (rv != SECSuccess) { + goto loser; + } + + *ctx = out; + return SECSuccess; + +loser: + SSLExp_DestroyAead(out); + return SECFailure; +} + +SECStatus +SSLExp_DestroyAead(SSLAeadContext *ctx) +{ + if (!ctx) { + return SECSuccess; + } + + PK11_FreeSymKey(ctx->keys.key); + PORT_ZFree(ctx, sizeof(*ctx)); + return SECSuccess; +} + +/* Bug 1529440 exists to refactor this and the other AEAD uses. */ +static SECStatus +ssl_AeadInner(const SSLAeadContext *ctx, PRBool decrypt, PRUint64 counter, + const PRUint8 *aad, unsigned int aadLen, + const PRUint8 *plaintext, unsigned int plaintextLen, + PRUint8 *out, unsigned int *outLen, unsigned int maxOut) +{ + if (ctx == NULL || (aad == NULL && aadLen > 0) || plaintext == NULL || + out == NULL || outLen == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + // Setup the nonce. + PRUint8 nonce[12] = { 0 }; + sslBuffer nonceBuf = SSL_BUFFER_FIXED(nonce + sizeof(nonce) - sizeof(counter), + sizeof(counter)); + SECStatus rv = sslBuffer_AppendNumber(&nonceBuf, counter, sizeof(counter)); + if (rv != SECSuccess) { + PORT_Assert(0); + return SECFailure; + } + for (int i = 0; i < sizeof(nonce); ++i) { + nonce[i] ^= ctx->keys.iv[i]; + } + + // Build AEAD parameters. + CK_GCM_PARAMS gcmParams = { 0 }; + CK_NSS_AEAD_PARAMS aeadParams = { 0 }; + unsigned char *params; + unsigned int paramsLen; + switch (ctx->mech) { + case CKM_AES_GCM: + gcmParams.pIv = nonce; + gcmParams.ulIvLen = sizeof(nonce); + gcmParams.pAAD = (unsigned char *)aad; // const cast :( + gcmParams.ulAADLen = aadLen; + gcmParams.ulTagBits = 128; // GCM measures in bits. + params = (unsigned char *)&gcmParams; + paramsLen = sizeof(gcmParams); + break; + + case CKM_NSS_CHACHA20_POLY1305: + aeadParams.pNonce = nonce; + aeadParams.ulNonceLen = sizeof(nonce); + aeadParams.pAAD = (unsigned char *)aad; // const cast :( + aeadParams.ulAADLen = aadLen; + aeadParams.ulTagLen = 16; // AEAD measures in octets. + params = (unsigned char *)&aeadParams; + paramsLen = sizeof(aeadParams); + break; + + default: + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + return tls13_AEAD(&ctx->keys, decrypt, out, outLen, maxOut, + plaintext, plaintextLen, ctx->mech, params, paramsLen); +} + +SECStatus +SSLExp_AeadEncrypt(const SSLAeadContext *ctx, PRUint64 counter, + const PRUint8 *aad, unsigned int aadLen, + const PRUint8 *plaintext, unsigned int plaintextLen, + PRUint8 *out, unsigned int *outLen, unsigned int maxOut) +{ + // false == encrypt + return ssl_AeadInner(ctx, PR_FALSE, counter, aad, aadLen, + plaintext, plaintextLen, out, outLen, maxOut); +} + +SECStatus +SSLExp_AeadDecrypt(const SSLAeadContext *ctx, PRUint64 counter, + const PRUint8 *aad, unsigned int aadLen, + const PRUint8 *plaintext, unsigned int plaintextLen, + PRUint8 *out, unsigned int *outLen, unsigned int maxOut) +{ + // true == decrypt + return ssl_AeadInner(ctx, PR_TRUE, counter, aad, aadLen, + plaintext, plaintextLen, out, outLen, maxOut); +} + +SECStatus +SSLExp_HkdfExtract(PRUint16 version, PRUint16 cipherSuite, + PK11SymKey *salt, PK11SymKey *ikm, PK11SymKey **keyp) +{ + if (keyp == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + SSLHashType hash; + SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite, + &hash, NULL); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + return tls13_HkdfExtract(salt, ikm, hash, keyp); +} + +SECStatus +SSLExp_HkdfExpandLabel(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk, + const PRUint8 *hsHash, unsigned int hsHashLen, + const char *label, unsigned int labelLen, + PK11SymKey **keyp) +{ + if (prk == NULL || keyp == NULL || + label == NULL || labelLen == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + SSLHashType hash; + SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite, + &hash, NULL); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + return tls13_HkdfExpandLabel(prk, hash, hsHash, hsHashLen, label, labelLen, + tls13_GetHkdfMechanismForHash(hash), + tls13_GetHashSizeForHash(hash), keyp); +} + +SECStatus +SSLExp_HkdfExpandLabelWithMech(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk, + const PRUint8 *hsHash, unsigned int hsHashLen, + const char *label, unsigned int labelLen, + CK_MECHANISM_TYPE mech, unsigned int keySize, + PK11SymKey **keyp) +{ + if (prk == NULL || keyp == NULL || + label == NULL || labelLen == 0 || + mech == CKM_INVALID_MECHANISM || keySize == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + SSLHashType hash; + SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite, + &hash, NULL); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + return tls13_HkdfExpandLabel(prk, hash, hsHash, hsHashLen, label, labelLen, + mech, keySize, keyp); +} |