summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/sslprimitive.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/sslprimitive.c')
-rw-r--r--security/nss/lib/ssl/sslprimitive.c274
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);
+}