diff options
Diffstat (limited to 'security/nss/lib/ssl/tls13hkdf.c')
-rw-r--r-- | security/nss/lib/ssl/tls13hkdf.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/tls13hkdf.c b/security/nss/lib/ssl/tls13hkdf.c new file mode 100644 index 0000000000..ed6cdd559f --- /dev/null +++ b/security/nss/lib/ssl/tls13hkdf.c @@ -0,0 +1,305 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * TLS 1.3 Protocol + * + * 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 "pk11func.h" +#include "secitem.h" +#include "ssl.h" +#include "sslt.h" +#include "sslerr.h" +#include "sslimpl.h" + +/* This table contains the mapping between TLS hash identifiers and the + * PKCS#11 identifiers */ +static const struct { + SSLHashType hash; + CK_MECHANISM_TYPE pkcs11Mech; + unsigned int hashSize; +} kTlsHkdfInfo[] = { + { ssl_hash_none, 0, 0 }, + { ssl_hash_md5, 0, 0 }, + { ssl_hash_sha1, 0, 0 }, + { ssl_hash_sha224, 0 }, + { ssl_hash_sha256, CKM_SHA256, 32 }, + { ssl_hash_sha384, CKM_SHA384, 48 }, + { ssl_hash_sha512, CKM_SHA512, 64 } +}; + +SECStatus +tls13_HkdfExtract(PK11SymKey *ikm1, PK11SymKey *ikm2, SSLHashType baseHash, + PK11SymKey **prkp) +{ + CK_HKDF_PARAMS params; + SECItem paramsi; + PK11SymKey *prk; + static const PRUint8 zeroKeyBuf[HASH_LENGTH_MAX]; + SECItem zeroKeyItem = { siBuffer, CONST_CAST(PRUint8, zeroKeyBuf), kTlsHkdfInfo[baseHash].hashSize }; + PK11SlotInfo *slot = NULL; + PK11SymKey *newIkm2 = NULL; + PK11SymKey *newIkm1 = NULL; + SECStatus rv; + + params.bExtract = CK_TRUE; + params.bExpand = CK_FALSE; + params.prfHashMechanism = kTlsHkdfInfo[baseHash].pkcs11Mech; + params.pInfo = NULL; + params.ulInfoLen = 0UL; + params.pSalt = NULL; + params.ulSaltLen = 0UL; + params.hSaltKey = CK_INVALID_HANDLE; + + if (!ikm1) { + /* PKCS #11 v3.0 has and explict NULL value, which equates to + * a sequence of zeros equal in length to the HMAC. */ + params.ulSaltType = CKF_HKDF_SALT_NULL; + } else { + /* PKCS #11 v3.0 can take the salt as a key handle */ + params.hSaltKey = PK11_GetSymKeyHandle(ikm1); + params.ulSaltType = CKF_HKDF_SALT_KEY; + + /* if we have both keys, make sure they are in the same slot */ + if (ikm2) { + rv = PK11_SymKeysToSameSlot(CKM_HKDF_DERIVE, + CKA_DERIVE, CKA_DERIVE, + ikm2, ikm1, &newIkm2, &newIkm1); + if (rv != SECSuccess) { + SECItem *salt; + /* couldn't move the keys, try extracting the salt */ + rv = PK11_ExtractKeyValue(ikm1); + if (rv != SECSuccess) + return rv; + salt = PK11_GetKeyData(ikm1); + if (!salt) + return SECFailure; + PORT_Assert(salt->len > 0); + /* Set up for Salt as Data instead of Salt as key */ + params.pSalt = salt->data; + params.ulSaltLen = salt->len; + params.ulSaltType = CKF_HKDF_SALT_DATA; + } + /* use the new keys */ + if (newIkm1) { + /* we've moved the key, get the handle for the new key */ + params.hSaltKey = PK11_GetSymKeyHandle(newIkm1); + /* we don't use ikm1 after this, so don't bother setting it */ + } + if (newIkm2) { + /* new ikm2 key, use the new key */ + ikm2 = newIkm2; + } + } + } + paramsi.data = (unsigned char *)¶ms; + paramsi.len = sizeof(params); + + PORT_Assert(kTlsHkdfInfo[baseHash].pkcs11Mech); + PORT_Assert(kTlsHkdfInfo[baseHash].hashSize); + PORT_Assert(kTlsHkdfInfo[baseHash].hash == baseHash); + + /* A zero ikm2 is a key of hash-length 0s. */ + if (!ikm2) { + /* if we have ikm1, put the zero key in the same slot */ + slot = ikm1 ? PK11_GetSlotFromKey(ikm1) : PK11_GetBestSlot(CKM_HKDF_DERIVE, NULL); + if (!slot) { + return SECFailure; + } + + newIkm2 = PK11_ImportDataKey(slot, CKM_HKDF_DERIVE, PK11_OriginUnwrap, + CKA_DERIVE, &zeroKeyItem, NULL); + if (!newIkm2) { + return SECFailure; + } + ikm2 = newIkm2; + } + PORT_Assert(ikm2); + + PRINT_BUF(50, (NULL, "HKDF Extract: IKM1/Salt", params.pSalt, params.ulSaltLen)); + PRINT_KEY(50, (NULL, "HKDF Extract: IKM2", ikm2)); + + prk = PK11_Derive(ikm2, CKM_HKDF_DERIVE, ¶msi, CKM_HKDF_DERIVE, + CKA_DERIVE, 0); + PK11_FreeSymKey(newIkm2); + PK11_FreeSymKey(newIkm1); + if (slot) + PK11_FreeSlot(slot); + if (!prk) { + return SECFailure; + } + + PRINT_KEY(50, (NULL, "HKDF Extract", prk)); + *prkp = prk; + + return SECSuccess; +} + +SECStatus +tls13_HkdfExpandLabelGeneral(CK_MECHANISM_TYPE deriveMech, PK11SymKey *prk, + SSLHashType baseHash, + const PRUint8 *handshakeHash, unsigned int handshakeHashLen, + const char *label, unsigned int labelLen, + CK_MECHANISM_TYPE algorithm, unsigned int keySize, + SSLProtocolVariant variant, PK11SymKey **keyp) +{ + CK_HKDF_PARAMS params; + SECItem paramsi = { siBuffer, NULL, 0 }; + /* Size of info array needs to be big enough to hold the maximum Prefix, + * Label, plus HandshakeHash. If it's ever to small, the code will abort. + */ + PRUint8 info[256]; + sslBuffer infoBuf = SSL_BUFFER(info); + PK11SymKey *derived; + SECStatus rv; + const char *kLabelPrefixTls = "tls13 "; + const char *kLabelPrefixDtls = "dtls13"; + const unsigned int kLabelPrefixLen = + (variant == ssl_variant_stream) ? strlen(kLabelPrefixTls) : strlen(kLabelPrefixDtls); + const char *kLabelPrefix = + (variant == ssl_variant_stream) ? kLabelPrefixTls : kLabelPrefixDtls; + + PORT_Assert(prk); + PORT_Assert(keyp); + if ((handshakeHashLen > 255) || + (handshakeHash == NULL && handshakeHashLen > 0) || + (labelLen + kLabelPrefixLen > 255)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* + * [draft-ietf-tls-tls13-11] Section 7.1: + * + * HKDF-Expand-Label(Secret, Label, HashValue, Length) = + * HKDF-Expand(Secret, HkdfLabel, Length) + * + * Where HkdfLabel is specified as: + * + * struct HkdfLabel { + * uint16 length; + * opaque label<9..255>; + * opaque hash_value<0..255>; + * }; + * + * Where: + * - HkdfLabel.length is Length + * - HkdfLabel.hash_value is HashValue. + * - HkdfLabel.label is "TLS 1.3, " + Label + * + */ + rv = sslBuffer_AppendNumber(&infoBuf, keySize, 2); + if (rv != SECSuccess) { + return SECFailure; + } + rv = sslBuffer_AppendNumber(&infoBuf, labelLen + kLabelPrefixLen, 1); + if (rv != SECSuccess) { + return SECFailure; + } + rv = sslBuffer_Append(&infoBuf, kLabelPrefix, kLabelPrefixLen); + if (rv != SECSuccess) { + return SECFailure; + } + rv = sslBuffer_Append(&infoBuf, label, labelLen); + if (rv != SECSuccess) { + return SECFailure; + } + rv = sslBuffer_AppendVariable(&infoBuf, handshakeHash, handshakeHashLen, 1); + if (rv != SECSuccess) { + return SECFailure; + } + + params.bExtract = CK_FALSE; + params.bExpand = CK_TRUE; + params.prfHashMechanism = kTlsHkdfInfo[baseHash].pkcs11Mech; + params.pInfo = SSL_BUFFER_BASE(&infoBuf); + params.ulInfoLen = SSL_BUFFER_LEN(&infoBuf); + paramsi.data = (unsigned char *)¶ms; + paramsi.len = sizeof(params); + derived = PK11_DeriveWithFlags(prk, deriveMech, + ¶msi, algorithm, + CKA_DERIVE, keySize, + CKF_SIGN | CKF_VERIFY); + if (!derived) { + return SECFailure; + } + + *keyp = derived; + +#ifdef TRACE + if (ssl_trace >= 50) { + /* Make sure the label is null terminated. */ + char labelStr[100]; + PORT_Memcpy(labelStr, label, labelLen); + labelStr[labelLen] = 0; + SSL_TRC(50, ("HKDF Expand: label='tls13 %s',requested length=%d", + labelStr, keySize)); + } + PRINT_KEY(50, (NULL, "PRK", prk)); + PRINT_BUF(50, (NULL, "Hash", handshakeHash, handshakeHashLen)); + PRINT_BUF(50, (NULL, "Info", SSL_BUFFER_BASE(&infoBuf), + SSL_BUFFER_LEN(&infoBuf))); + PRINT_KEY(50, (NULL, "Derived key", derived)); +#endif + + return SECSuccess; +} + +SECStatus +tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash, + const PRUint8 *handshakeHash, unsigned int handshakeHashLen, + const char *label, unsigned int labelLen, + CK_MECHANISM_TYPE algorithm, unsigned int keySize, + SSLProtocolVariant variant, PK11SymKey **keyp) +{ + return tls13_HkdfExpandLabelGeneral(CKM_HKDF_DERIVE, prk, baseHash, + handshakeHash, handshakeHashLen, + label, labelLen, algorithm, keySize, + variant, keyp); +} + +SECStatus +tls13_HkdfExpandLabelRaw(PK11SymKey *prk, SSLHashType baseHash, + const PRUint8 *handshakeHash, unsigned int handshakeHashLen, + const char *label, unsigned int labelLen, + SSLProtocolVariant variant, unsigned char *output, + unsigned int outputLen) +{ + PK11SymKey *derived = NULL; + SECItem *rawkey; + SECStatus rv; + + /* the result is not really a key, it's a data object */ + rv = tls13_HkdfExpandLabelGeneral(CKM_HKDF_DATA, prk, baseHash, + handshakeHash, handshakeHashLen, + label, labelLen, CKM_HKDF_DERIVE, outputLen, + variant, &derived); + if (rv != SECSuccess || !derived) { + goto abort; + } + + rv = PK11_ExtractKeyValue(derived); + if (rv != SECSuccess) { + goto abort; + } + + rawkey = PK11_GetKeyData(derived); + if (!rawkey) { + goto abort; + } + + PORT_Assert(rawkey->len == outputLen); + memcpy(output, rawkey->data, outputLen); + PK11_FreeSymKey(derived); + + return SECSuccess; + +abort: + if (derived) { + PK11_FreeSymKey(derived); + } + PORT_SetError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE); + return SECFailure; +} |