Skip to content

Commit

Permalink
Bug 1529813 - Expose TLS HKDF functions for QUIC, r=ekr
Browse files Browse the repository at this point in the history
Summary:
This just does the two functions that QUIC needs.

I reused the tests for HKDF for testing that the exposed function works identically, at least for those cases where DeriveSecret can be used.

Reviewers: ekr

Tags: #secure-revision

Bug #: 1529813

Differential Revision: https://phabricator.services.mozilla.com/D20762

--HG--
extra : rebase_source : 1a8a32dbc568fed69bf757623932a8144eef74e3
extra : amend_source : 87390c7b6068363f3494e407cfba7208f79a8797
  • Loading branch information
martinthomson committed Feb 22, 2019
1 parent ae30248 commit 7dcf0c8
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 10 deletions.
137 changes: 137 additions & 0 deletions gtests/ssl_gtest/tls_hkdf_unittest.cc
Expand Up @@ -7,6 +7,9 @@
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sslproto.h"
#include "sslexp.h"
#include "tls13hkdf.h"

#include "databuffer.h"
Expand Down Expand Up @@ -65,6 +68,18 @@ size_t GetHashLength(SSLHashType hash) {
return 0;
}

PRUint16 GetSomeCipherSuiteForHash(SSLHashType hash) {
switch (hash) {
case ssl_hash_sha256:
return TLS_AES_128_GCM_SHA256;
case ssl_hash_sha384:
return TLS_AES_256_GCM_SHA384;
default:
ADD_FAILURE() << "Unknown hash: " << hash;
}
return 0;
}

const std::string kHashName[] = {"None", "MD5", "SHA-1", "SHA-224",
"SHA-256", "SHA-384", "SHA-512"};

Expand Down Expand Up @@ -143,6 +158,14 @@ class TlsHkdfTest : public ::testing::Test,

DumpKey("Output", prkk);
VerifyKey(prkk, expected);

// Now test the public wrapper.
PRUint16 cs = GetSomeCipherSuiteForHash(base_hash);
rv = SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3, cs, ikmk1.get(),
ikmk2.get(), &prk);
ASSERT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
VerifyKey(ScopedPK11SymKey(prk), expected);
}

void HkdfExpandLabel(ScopedPK11SymKey* prk, SSLHashType base_hash,
Expand All @@ -159,6 +182,19 @@ class TlsHkdfTest : public ::testing::Test,
ASSERT_EQ(SECSuccess, rv);
DumpData("Output", &output[0], output.size());
EXPECT_EQ(0, memcmp(expected.data(), &output[0], expected.len()));

if (session_hash_len > 0) {
return;
}

// Verify that the public API produces the same result.
PRUint16 cs = GetSomeCipherSuiteForHash(base_hash);
PK11SymKey* secret;
rv = SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_3, cs, prk->get(),
label, label_len, &secret);
EXPECT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
VerifyKey(ScopedPK11SymKey(secret), expected);
}

protected:
Expand Down Expand Up @@ -262,6 +298,107 @@ TEST_P(TlsHkdfTest, HkdfExpandLabel) {
expected_data);
}

TEST_P(TlsHkdfTest, HkdfExpandLabelNoHash) {
const uint8_t tv[][48] = {
{/* ssl_hash_none */},
{/* ssl_hash_md5 */},
{/* ssl_hash_sha1 */},
{/* ssl_hash_sha224 */},
{0xb7, 0x08, 0x00, 0xe3, 0x8e, 0x48, 0x68, 0x91, 0xb1, 0x0f, 0x5e,
0x6f, 0x22, 0x53, 0x6b, 0x84, 0x69, 0x75, 0xaa, 0xa3, 0x2a, 0xe7,
0xde, 0xaa, 0xc3, 0xd1, 0xb4, 0x05, 0x22, 0x5c, 0x68, 0xf5},
{0x13, 0xd3, 0x36, 0x9f, 0x3c, 0x78, 0xa0, 0x32, 0x40, 0xee, 0x16, 0xe9,
0x11, 0x12, 0x66, 0xc7, 0x51, 0xad, 0xd8, 0x3c, 0xa1, 0xa3, 0x97, 0x74,
0xd7, 0x45, 0xff, 0xa7, 0x88, 0x9e, 0x52, 0x17, 0x2e, 0xaa, 0x3a, 0xd2,
0x35, 0xd8, 0xd5, 0x35, 0xfd, 0x65, 0x70, 0x9f, 0xa9, 0xf9, 0xfa, 0x23}};

const DataBuffer expected_data(tv[hash_type_], GetHashLength(hash_type_));
HkdfExpandLabel(&k1_, hash_type_, nullptr, 0, kLabelMasterSecret,
strlen(kLabelMasterSecret), expected_data);
}

TEST_P(TlsHkdfTest, BadExtractWrapperInput) {
PK11SymKey* key = nullptr;

// Bad version.
EXPECT_EQ(SECFailure, SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_2,
TLS_AES_128_GCM_SHA256, k1_.get(),
k2_.get(), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Bad ciphersuite.
EXPECT_EQ(SECFailure, SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_NULL_SHA, k1_.get(),
k2_.get(), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Old ciphersuite.
EXPECT_EQ(SECFailure,
SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_AES_128_CBC_SHA, k1_.get(),
k2_.get(), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// NULL outparam..
EXPECT_EQ(SECFailure,
SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_AES_128_CBC_SHA, k1_.get(),
k2_.get(), nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

EXPECT_EQ(nullptr, key);
}

TEST_P(TlsHkdfTest, BadDeriveSecretWrapperInput) {
PK11SymKey* key = nullptr;
static const char* kLabel = "label";

// Bad version.
EXPECT_EQ(SECFailure, SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_2,
TLS_AES_128_GCM_SHA256, k1_.get(),
kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Bad ciphersuite.
EXPECT_EQ(SECFailure, SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_NULL_MD5, k1_.get(),
kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Old ciphersuite.
EXPECT_EQ(SECFailure,
SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_AES_128_CBC_SHA, k1_.get(),
kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Null PRK.
EXPECT_EQ(SECFailure, SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_2,
TLS_AES_128_GCM_SHA256, nullptr,
kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Null, non-zero-length label.
EXPECT_EQ(SECFailure, SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_AES_128_GCM_SHA256, k1_.get(),
nullptr, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Null, empty label.
EXPECT_EQ(SECFailure, SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_AES_128_GCM_SHA256, k1_.get(),
nullptr, 0, &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

// Null key pointer..
EXPECT_EQ(SECFailure, SSL_HkdfDeriveSecret(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_AES_128_GCM_SHA256, k1_.get(),
kLabel, strlen(kLabel), nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());

EXPECT_EQ(nullptr, key);
}

static const SSLHashType kHashTypes[] = {ssl_hash_sha256, ssl_hash_sha384};
INSTANTIATE_TEST_CASE_P(AllHashFuncs, TlsHkdfTest,
::testing::ValuesIn(kHashTypes));
Expand Down
24 changes: 22 additions & 2 deletions lib/ssl/sslexp.h
Expand Up @@ -629,8 +629,8 @@ typedef SECStatus(PR_CALLBACK *SSLRecordWriteCallback)(
/*
* The following AEAD functions expose an AEAD primitive that uses a ciphersuite
* to set parameters. The ciphersuite determines the Hash function used by
* HKDF, the AEAD function, and the size of key and IV. Only TLS 1.3
* ciphersuites can be used.
* HKDF, the AEAD function, and the size of key and IV. This is only supported
* for TLS 1.3.
*
* The key and IV are generated using the TLS KDF with a custom label. That is
* HKDF-Expand-Label(secret, labelPrefix + " key" or " iv", "", L).
Expand Down Expand Up @@ -680,6 +680,26 @@ typedef struct SSLAeadContextStr SSLAeadContext;
(SSLAeadContext * _ctx), \
(ctx))

/* SSL_HkdfExtract and SSL_HkdfExpandLabel implement the functions from TLS,
* using the version and ciphersuite to set parameters. This allows callers to
* use these TLS functions as a KDF. This is only supported for TLS 1.3. */
#define SSL_HkdfExtract(version, cipherSuite, salt, ikm, keyp) \
SSL_EXPERIMENTAL_API("SSL_HkdfExtract", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _salt, PK11SymKey * _ikm, \
PK11SymKey * *_keyp), \
(version, cipherSuite, salt, ikm, keyp))

#define SSL_HkdfDeriveSecret(version, cipherSuite, prk, \
label, labelLen, keyp) \
SSL_EXPERIMENTAL_API("SSL_HkdfDeriveSecret", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _prk, \
const char *_label, unsigned int _labelLen, \
PK11SymKey **_keyp), \
(version, cipherSuite, prk, \
label, labelLen, keyp))

/* Deprecated experimental APIs */
#define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API

Expand Down
6 changes: 6 additions & 0 deletions lib/ssl/sslimpl.h
Expand Up @@ -1773,6 +1773,12 @@ SECStatus SSLExp_AeadDecrypt(const SSLAeadContext *ctx, PRUint64 counter,
const PRUint8 *plaintext, unsigned int plaintextLen,
PRUint8 *out, unsigned int *outLen, unsigned int maxOut);

SECStatus SSLExp_HkdfExtract(PRUint16 version, PRUint16 cipherSuite,
PK11SymKey *salt, PK11SymKey *ikm, PK11SymKey **keyp);
SECStatus SSLExp_HkdfDeriveSecret(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
const char *label, unsigned int labelLen,
PK11SymKey **key);

SEC_END_PROTOS

#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
Expand Down
42 changes: 42 additions & 0 deletions lib/ssl/sslprimitive.c
Expand Up @@ -205,3 +205,45 @@ SSLExp_AeadDecrypt(const SSLAeadContext *ctx, PRUint64 counter,
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;
const ssl3BulkCipherDef *cipher; /* Unused here. */
SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
&hash, &cipher);
if (rv != SECSuccess) {
return SECFailure; /* Code already set. */
}
return tls13_HkdfExtract(salt, ikm, hash, keyp);
}

SECStatus
SSLExp_HkdfDeriveSecret(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
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;
const ssl3BulkCipherDef *cipher; /* Unused here. */
SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
&hash, &cipher);
if (rv != SECSuccess) {
return SECFailure; /* Code already set. */
}
return tls13_HkdfExpandLabel(prk, hash, NULL, 0, label, labelLen,
tls13_GetHkdfMechanismForHash(hash),
tls13_GetHashSizeForHash(hash), keyp);
}
2 changes: 2 additions & 0 deletions lib/ssl/sslsock.c
Expand Up @@ -4052,6 +4052,8 @@ struct {
EXP(GetResumptionTokenInfo),
EXP(HelloRetryRequestCallback),
EXP(InstallExtensionHooks),
EXP(HkdfExtract),
EXP(HkdfDeriveSecret),
EXP(KeyUpdate),
EXP(MakeAead),
EXP(RecordLayerData),
Expand Down
15 changes: 7 additions & 8 deletions lib/ssl/tls13hkdf.c
Expand Up @@ -140,14 +140,13 @@ tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash,
const char *kLabelPrefix = "tls13 ";
const unsigned int kLabelPrefixLen = strlen(kLabelPrefix);

if (handshakeHash) {
if (handshakeHashLen > 255) {
PORT_Assert(0);
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
} else {
PORT_Assert(!handshakeHashLen);
PORT_Assert(prk);
PORT_Assert(keyp);
if ((handshakeHashLen > 255) ||
(handshakeHash == NULL && handshakeHashLen > 0) ||
(labelLen + kLabelPrefixLen > 255)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}

/*
Expand Down

0 comments on commit 7dcf0c8

Please sign in to comment.