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.c392
1 files changed, 300 insertions, 92 deletions
diff --git a/security/nss/lib/ssl/sslprimitive.c b/security/nss/lib/ssl/sslprimitive.c
index 540c17840..2afecfb16 100644
--- a/security/nss/lib/ssl/sslprimitive.c
+++ b/security/nss/lib/ssl/sslprimitive.c
@@ -6,6 +6,7 @@
* 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 "blapit.h"
#include "keyhi.h"
#include "pk11pub.h"
#include "sechash.h"
@@ -19,47 +20,28 @@
#include "tls13hkdf.h"
struct SSLAeadContextStr {
- CK_MECHANISM_TYPE mech;
- ssl3KeyMaterial keys;
+ /* sigh, the API creates a single context, but then uses either encrypt
+ * and decrypt on that context. We should take an encrypt/decrypt
+ * variable here, but for now create two contexts. */
+ PK11Context *encryptContext;
+ PK11Context *decryptContext;
+ int tagLen;
+ int ivLen;
+ unsigned char iv[MAX_IV_LENGTH];
};
-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)
+SSLExp_MakeVariantAead(PRUint16 version, PRUint16 cipherSuite, SSLProtocolVariant variant,
+ 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";
+ CK_MECHANISM_TYPE mech;
+ SECItem nullParams = { siBuffer, NULL, 0 };
+ PK11SymKey *key = NULL;
PORT_Assert(strlen(keySuffix) >= strlen(ivSuffix));
if (secret == NULL || ctx == NULL ||
@@ -81,7 +63,9 @@ SSLExp_MakeAead(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *secret,
if (out == NULL) {
goto loser;
}
- out->mech = ssl3_Alg2Mech(cipher->calg);
+ mech = ssl3_Alg2Mech(cipher->calg);
+ out->ivLen = cipher->iv_size + cipher->explicit_nonce_size;
+ out->tagLen = cipher->tag_size;
memcpy(label, labelPrefix, labelPrefixLen);
memcpy(label + labelPrefixLen, ivSuffix, strlen(ivSuffix));
@@ -89,8 +73,8 @@ SSLExp_MakeAead(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *secret,
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);
+ label, labelLen, variant,
+ out->iv, ivLen);
if (rv != SECSuccess) {
goto loser;
}
@@ -99,91 +83,96 @@ SSLExp_MakeAead(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *secret,
labelLen = labelPrefixLen + strlen(keySuffix);
rv = tls13_HkdfExpandLabel(secret, hash,
NULL, 0, // Handshake hash.
- label, labelLen,
- out->mech, cipher->key_size, &out->keys.key);
+ label, labelLen, mech, cipher->key_size,
+ variant, &key);
if (rv != SECSuccess) {
goto loser;
}
+ /* We really need to change the API to Create a context for each
+ * encrypt and decrypt rather than a single call that does both. it's
+ * almost certain that the underlying application tries to use the same
+ * context for both. */
+ out->encryptContext = PK11_CreateContextBySymKey(mech,
+ CKA_NSS_MESSAGE | CKA_ENCRYPT,
+ key, &nullParams);
+ if (out->encryptContext == NULL) {
+ goto loser;
+ }
+
+ out->decryptContext = PK11_CreateContextBySymKey(mech,
+ CKA_NSS_MESSAGE | CKA_DECRYPT,
+ key, &nullParams);
+ if (out->decryptContext == NULL) {
+ goto loser;
+ }
+
+ PK11_FreeSymKey(key);
*ctx = out;
return SECSuccess;
loser:
+ PK11_FreeSymKey(key);
SSLExp_DestroyAead(out);
return SECFailure;
}
SECStatus
+SSLExp_MakeAead(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *secret,
+ const char *labelPrefix, unsigned int labelPrefixLen, SSLAeadContext **ctx)
+{
+ return SSLExp_MakeVariantAead(version, cipherSuite, ssl_variant_stream, secret,
+ labelPrefix, labelPrefixLen, ctx);
+}
+
+SECStatus
SSLExp_DestroyAead(SSLAeadContext *ctx)
{
if (!ctx) {
return SECSuccess;
}
+ if (ctx->encryptContext) {
+ PK11_DestroyContext(ctx->encryptContext, PR_TRUE);
+ }
+ if (ctx->decryptContext) {
+ PK11_DestroyContext(ctx->decryptContext, PR_TRUE);
+ }
- 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,
+ssl_AeadInner(const SSLAeadContext *ctx, PK11Context *context,
+ PRBool decrypt, PRUint64 counter,
const PRUint8 *aad, unsigned int aadLen,
- const PRUint8 *plaintext, unsigned int plaintextLen,
+ const PRUint8 *in, unsigned int inLen,
PRUint8 *out, unsigned int *outLen, unsigned int maxOut)
{
- if (ctx == NULL || (aad == NULL && aadLen > 0) || plaintext == NULL ||
+ if (ctx == NULL || (aad == NULL && aadLen > 0) || in == 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));
+ PRUint8 nonce[sizeof(counter)] = { 0 };
+ sslBuffer nonceBuf = SSL_BUFFER_FIXED(nonce, 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);
+ /* at least on encrypt, we should not be using CKG_NO_GENERATE, but
+ * the current experimental API has the application tracking the counter
+ * rather than token. We should look at the QUIC code and see if the
+ * counter can be moved internally where it belongs. That would
+ * also get rid of the formatting code above and have the API
+ * call tls13_AEAD directly in SSLExp_Aead* */
+ return tls13_AEAD(context, decrypt, CKG_NO_GENERATE, 0, ctx->iv, NULL,
+ ctx->ivLen, nonce, sizeof(counter), aad, aadLen,
+ out, outLen, maxOut, ctx->tagLen, in, inLen);
}
SECStatus
@@ -193,19 +182,21 @@ SSLExp_AeadEncrypt(const SSLAeadContext *ctx, PRUint64 counter,
PRUint8 *out, unsigned int *outLen, unsigned int maxOut)
{
// false == encrypt
- return ssl_AeadInner(ctx, PR_FALSE, counter, aad, aadLen,
- plaintext, plaintextLen, out, outLen, maxOut);
+ return ssl_AeadInner(ctx, ctx->encryptContext, 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,
+ const PRUint8 *ciphertext, unsigned int ciphertextLen,
PRUint8 *out, unsigned int *outLen, unsigned int maxOut)
{
// true == decrypt
- return ssl_AeadInner(ctx, PR_TRUE, counter, aad, aadLen,
- plaintext, plaintextLen, out, outLen, maxOut);
+ return ssl_AeadInner(ctx, ctx->decryptContext, PR_TRUE, counter,
+ aad, aadLen, ciphertext, ciphertextLen,
+ out, outLen, maxOut);
}
SECStatus
@@ -229,8 +220,17 @@ SSLExp_HkdfExtract(PRUint16 version, PRUint16 cipherSuite,
SECStatus
SSLExp_HkdfExpandLabel(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
const PRUint8 *hsHash, unsigned int hsHashLen,
- const char *label, unsigned int labelLen,
- PK11SymKey **keyp)
+ const char *label, unsigned int labelLen, PK11SymKey **keyp)
+{
+ return SSLExp_HkdfVariantExpandLabel(version, cipherSuite, prk, hsHash, hsHashLen,
+ label, labelLen, ssl_variant_stream, keyp);
+}
+
+SECStatus
+SSLExp_HkdfVariantExpandLabel(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
+ const PRUint8 *hsHash, unsigned int hsHashLen,
+ const char *label, unsigned int labelLen,
+ SSLProtocolVariant variant, PK11SymKey **keyp)
{
if (prk == NULL || keyp == NULL ||
label == NULL || labelLen == 0) {
@@ -245,8 +245,8 @@ SSLExp_HkdfExpandLabel(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
return SECFailure; /* Code already set. */
}
return tls13_HkdfExpandLabel(prk, hash, hsHash, hsHashLen, label, labelLen,
- tls13_GetHkdfMechanismForHash(hash),
- tls13_GetHashSizeForHash(hash), keyp);
+ CKM_HKDF_DERIVE,
+ tls13_GetHashSizeForHash(hash), variant, keyp);
}
SECStatus
@@ -256,6 +256,18 @@ SSLExp_HkdfExpandLabelWithMech(PRUint16 version, PRUint16 cipherSuite, PK11SymKe
CK_MECHANISM_TYPE mech, unsigned int keySize,
PK11SymKey **keyp)
{
+ return SSLExp_HkdfVariantExpandLabelWithMech(version, cipherSuite, prk, hsHash, hsHashLen,
+ label, labelLen, mech, keySize,
+ ssl_variant_stream, keyp);
+}
+
+SECStatus
+SSLExp_HkdfVariantExpandLabelWithMech(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,
+ SSLProtocolVariant variant, PK11SymKey **keyp)
+{
if (prk == NULL || keyp == NULL ||
label == NULL || labelLen == 0 ||
mech == CKM_INVALID_MECHANISM || keySize == 0) {
@@ -270,5 +282,201 @@ SSLExp_HkdfExpandLabelWithMech(PRUint16 version, PRUint16 cipherSuite, PK11SymKe
return SECFailure; /* Code already set. */
}
return tls13_HkdfExpandLabel(prk, hash, hsHash, hsHashLen, label, labelLen,
- mech, keySize, keyp);
+ mech, keySize, variant, keyp);
+}
+
+SECStatus
+ssl_CreateMaskingContextInner(PRUint16 version, PRUint16 cipherSuite,
+ SSLProtocolVariant variant,
+ PK11SymKey *secret,
+ const char *label,
+ unsigned int labelLen,
+ SSLMaskingContext **ctx)
+{
+ if (!secret || !ctx || (!label && labelLen)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ SSLMaskingContext *out = PORT_ZNew(SSLMaskingContext);
+ if (out == NULL) {
+ goto loser;
+ }
+
+ SSLHashType hash;
+ const ssl3BulkCipherDef *cipher;
+ SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
+ &hash, &cipher);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser; /* Code already set. */
+ }
+
+ out->mech = tls13_SequenceNumberEncryptionMechanism(cipher->calg);
+ if (out->mech == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+
+ // Derive the masking key
+ rv = tls13_HkdfExpandLabel(secret, hash,
+ NULL, 0, // Handshake hash.
+ label, labelLen,
+ out->mech,
+ cipher->key_size, variant,
+ &out->secret);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ out->version = version;
+ out->cipherSuite = cipherSuite;
+
+ *ctx = out;
+ return SECSuccess;
+loser:
+ SSLExp_DestroyMaskingContext(out);
+ return SECFailure;
+}
+
+SECStatus
+ssl_CreateMaskInner(SSLMaskingContext *ctx, const PRUint8 *sample,
+ unsigned int sampleLen, PRUint8 *outMask,
+ unsigned int maskLen)
+{
+ if (!ctx || !sample || !sampleLen || !outMask || !maskLen) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (ctx->secret == NULL) {
+ PORT_SetError(SEC_ERROR_NO_KEY);
+ return SECFailure;
+ }
+
+ SECStatus rv = SECFailure;
+ unsigned int outMaskLen = 0;
+ int paramLen = 0;
+
+ /* Internal output len/buf, for use if the caller allocated and requested
+ * less than one block of output. |oneBlock| should have size equal to the
+ * largest block size supported below. */
+ PRUint8 oneBlock[AES_BLOCK_SIZE];
+ PRUint8 *outMask_ = outMask;
+ unsigned int maskLen_ = maskLen;
+
+ switch (ctx->mech) {
+ case CKM_AES_ECB:
+ if (sampleLen < AES_BLOCK_SIZE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (maskLen_ < AES_BLOCK_SIZE) {
+ outMask_ = oneBlock;
+ maskLen_ = sizeof(oneBlock);
+ }
+ rv = PK11_Encrypt(ctx->secret,
+ ctx->mech,
+ NULL,
+ outMask_, &outMaskLen, maskLen_,
+ sample, AES_BLOCK_SIZE);
+ if (rv == SECSuccess &&
+ maskLen < AES_BLOCK_SIZE) {
+ memcpy(outMask, outMask_, maskLen);
+ }
+ break;
+ case CKM_NSS_CHACHA20_CTR:
+ paramLen = 16;
+ /* fall through */
+ case CKM_CHACHA20:
+ paramLen = (paramLen) ? paramLen : sizeof(CK_CHACHA20_PARAMS);
+ if (sampleLen < paramLen) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ SECItem param;
+ param.type = siBuffer;
+ param.len = paramLen;
+ param.data = (PRUint8 *)sample; // const-cast :(
+ unsigned char zeros[128] = { 0 };
+
+ if (maskLen > sizeof(zeros)) {
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ rv = PK11_Encrypt(ctx->secret,
+ ctx->mech,
+ &param,
+ outMask, &outMaskLen,
+ maskLen,
+ zeros, maskLen);
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_PKCS11_FUNCTION_FAILED);
+ return SECFailure;
+ }
+
+ // Ensure we produced at least as much material as requested.
+ if (outMaskLen < maskLen) {
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+ssl_DestroyMaskingContextInner(SSLMaskingContext *ctx)
+{
+ if (!ctx) {
+ return SECSuccess;
+ }
+
+ PK11_FreeSymKey(ctx->secret);
+ PORT_ZFree(ctx, sizeof(*ctx));
+ return SECSuccess;
+}
+
+SECStatus
+SSLExp_CreateMask(SSLMaskingContext *ctx, const PRUint8 *sample,
+ unsigned int sampleLen, PRUint8 *outMask,
+ unsigned int maskLen)
+{
+ return ssl_CreateMaskInner(ctx, sample, sampleLen, outMask, maskLen);
+}
+
+SECStatus
+SSLExp_CreateMaskingContext(PRUint16 version, PRUint16 cipherSuite,
+ PK11SymKey *secret,
+ const char *label,
+ unsigned int labelLen,
+ SSLMaskingContext **ctx)
+{
+ return ssl_CreateMaskingContextInner(version, cipherSuite, ssl_variant_stream, secret,
+ label, labelLen, ctx);
+}
+
+SECStatus
+SSLExp_CreateVariantMaskingContext(PRUint16 version, PRUint16 cipherSuite,
+ SSLProtocolVariant variant,
+ PK11SymKey *secret,
+ const char *label,
+ unsigned int labelLen,
+ SSLMaskingContext **ctx)
+{
+ return ssl_CreateMaskingContextInner(version, cipherSuite, variant, secret,
+ label, labelLen, ctx);
+}
+
+SECStatus
+SSLExp_DestroyMaskingContext(SSLMaskingContext *ctx)
+{
+ return ssl_DestroyMaskingContextInner(ctx);
}