/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* 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 #include "gtest/gtest.h" #include "nss_scoped_ptrs.h" #include "cryptohi.h" #include "secitem.h" #include "secerr.h" namespace nss_test { class SignParamsTestF : public ::testing::Test { protected: ScopedPLArenaPool arena_; ScopedSECKEYPrivateKey privk_; ScopedSECKEYPublicKey pubk_; ScopedSECKEYPrivateKey ecPrivk_; ScopedSECKEYPublicKey ecPubk_; void SetUp() { arena_.reset(PORT_NewArena(2048)); SECKEYPublicKey *pubk; SECKEYPrivateKey *privk = SECKEY_CreateRSAPrivateKey(1024, &pubk, NULL); ASSERT_NE(nullptr, pubk); pubk_.reset(pubk); ASSERT_NE(nullptr, privk); privk_.reset(privk); SECKEYECParams ecParams = {siBuffer, NULL, 0}; SECOidData *oidData; oidData = SECOID_FindOIDByTag(SEC_OID_CURVE25519); ASSERT_NE(nullptr, oidData); ASSERT_NE(nullptr, SECITEM_AllocItem(NULL, &ecParams, (2 + oidData->oid.len))) << "Couldn't allocate memory for OID."; ecParams.data[0] = SEC_ASN1_OBJECT_ID; /* we have to prepend 0x06 */ ecParams.data[1] = oidData->oid.len; memcpy(ecParams.data + 2, oidData->oid.data, oidData->oid.len); SECKEYPublicKey *ecPubk; SECKEYPrivateKey *ecPrivk = SECKEY_CreateECPrivateKey(&ecParams, &ecPubk, NULL); ASSERT_NE(nullptr, ecPubk); ecPubk_.reset(ecPubk); ASSERT_NE(nullptr, ecPrivk); ecPrivk_.reset(ecPrivk); } void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) { PORT_Memset(params, 0, sizeof(SECKEYRSAPSSParams)); params->hashAlg = (SECAlgorithmID *)PORT_ArenaZAlloc( arena_.get(), sizeof(SECAlgorithmID)); ASSERT_NE(nullptr, params->hashAlg); SECStatus rv = SECOID_SetAlgorithmID(arena_.get(), params->hashAlg, hashAlgTag, NULL); ASSERT_EQ(SECSuccess, rv); } void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag, SECOidTag maskHashAlgTag) { CreatePssParams(params, hashAlgTag); SECAlgorithmID maskHashAlg; PORT_Memset(&maskHashAlg, 0, sizeof(maskHashAlg)); SECStatus rv = SECOID_SetAlgorithmID(arena_.get(), &maskHashAlg, maskHashAlgTag, NULL); ASSERT_EQ(SECSuccess, rv); SECItem *maskHashAlgItem = SEC_ASN1EncodeItem(arena_.get(), NULL, &maskHashAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)); params->maskAlg = (SECAlgorithmID *)PORT_ArenaZAlloc( arena_.get(), sizeof(SECAlgorithmID)); ASSERT_NE(nullptr, params->maskAlg); rv = SECOID_SetAlgorithmID(arena_.get(), params->maskAlg, SEC_OID_PKCS1_MGF1, maskHashAlgItem); ASSERT_EQ(SECSuccess, rv); } void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag, SECOidTag maskHashAlgTag, unsigned long saltLength) { CreatePssParams(params, hashAlgTag, maskHashAlgTag); SECItem *saltLengthItem = SEC_ASN1EncodeInteger(arena_.get(), ¶ms->saltLength, saltLength); ASSERT_EQ(¶ms->saltLength, saltLengthItem); } void CheckHashAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) { // If hash algorithm is SHA-1, it must be omitted in the parameters if (hashAlgTag == SEC_OID_SHA1) { EXPECT_EQ(nullptr, params->hashAlg); } else { EXPECT_NE(nullptr, params->hashAlg); EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(params->hashAlg)); } } void CheckMaskAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) { SECStatus rv; // If hash algorithm is SHA-1, it must be omitted in the parameters if (hashAlgTag == SEC_OID_SHA1) EXPECT_EQ(nullptr, params->hashAlg); else { EXPECT_NE(nullptr, params->maskAlg); EXPECT_EQ(SEC_OID_PKCS1_MGF1, SECOID_GetAlgorithmTag(params->maskAlg)); SECAlgorithmID hashAlg; rv = SEC_QuickDERDecodeItem(arena_.get(), &hashAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), ¶ms->maskAlg->parameters); ASSERT_EQ(SECSuccess, rv); EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(&hashAlg)); } } void CheckSaltLength(SECKEYRSAPSSParams *params, SECOidTag hashAlg) { // If the salt length parameter is missing, that means it is 20 (default) if (!params->saltLength.data) { return; } unsigned long value; SECStatus rv = SEC_ASN1DecodeInteger(¶ms->saltLength, &value); ASSERT_EQ(SECSuccess, rv); // The salt length are usually the same as the hash length, // except for the case where the hash length exceeds the limit // set by the key length switch (hashAlg) { case SEC_OID_SHA1: EXPECT_EQ(20UL, value); break; case SEC_OID_SHA224: EXPECT_EQ(28UL, value); break; case SEC_OID_SHA256: EXPECT_EQ(32UL, value); break; case SEC_OID_SHA384: EXPECT_EQ(48UL, value); break; case SEC_OID_SHA512: // Truncated from 64, because our private key is 1024-bit EXPECT_EQ(62UL, value); break; default: FAIL(); } } }; class SignParamsTest : public SignParamsTestF, public ::testing::WithParamInterface> {}; class SignParamsSourceTest : public SignParamsTestF, public ::testing::WithParamInterface {}; TEST_P(SignParamsTest, CreateRsa) { SECOidTag hashAlg = std::get<0>(GetParam()); SECOidTag srcHashAlg = std::get<1>(GetParam()); SECItem *srcParams; if (srcHashAlg != SEC_OID_UNKNOWN) { SECKEYRSAPSSParams pssParams; ASSERT_NO_FATAL_FAILURE( CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); ASSERT_NE(nullptr, srcParams); } else { srcParams = NULL; } SECItem *params = SEC_CreateSignatureAlgorithmParameters( arena_.get(), nullptr, SEC_OID_PKCS1_RSA_ENCRYPTION, hashAlg, srcParams, privk_.get()); // PKCS#1 RSA actually doesn't take any parameters, but if it is // given, return a copy of it if (srcHashAlg != SEC_OID_UNKNOWN) { EXPECT_EQ(srcParams->len, params->len); EXPECT_EQ(0, memcmp(params->data, srcParams->data, srcParams->len)); } else { EXPECT_EQ(nullptr, params); } } TEST_P(SignParamsTest, CreateRsaPss) { SECOidTag hashAlg = std::get<0>(GetParam()); SECOidTag srcHashAlg = std::get<1>(GetParam()); SECItem *srcParams; if (srcHashAlg != SEC_OID_UNKNOWN) { SECKEYRSAPSSParams pssParams; ASSERT_NO_FATAL_FAILURE( CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); ASSERT_NE(nullptr, srcParams); } else { srcParams = NULL; } SECItem *params = SEC_CreateSignatureAlgorithmParameters( arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, srcParams, privk_.get()); if (hashAlg != SEC_OID_UNKNOWN && srcHashAlg != SEC_OID_UNKNOWN && hashAlg != srcHashAlg) { EXPECT_EQ(nullptr, params); return; } EXPECT_NE(nullptr, params); SECKEYRSAPSSParams pssParams; PORT_Memset(&pssParams, 0, sizeof(pssParams)); SECStatus rv = SEC_QuickDERDecodeItem(arena_.get(), &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate), params); ASSERT_EQ(SECSuccess, rv); if (hashAlg == SEC_OID_UNKNOWN) { if (!pssParams.hashAlg) { hashAlg = SEC_OID_SHA1; } else { hashAlg = SECOID_GetAlgorithmTag(pssParams.hashAlg); } if (srcHashAlg == SEC_OID_UNKNOWN) { // If both hashAlg and srcHashAlg is unset, NSS will decide the hash // algorithm based on the key length; in this case it's SHA256 EXPECT_EQ(SEC_OID_SHA256, hashAlg); } else { EXPECT_EQ(srcHashAlg, hashAlg); } } ASSERT_NO_FATAL_FAILURE(CheckHashAlg(&pssParams, hashAlg)); ASSERT_NO_FATAL_FAILURE(CheckMaskAlg(&pssParams, hashAlg)); ASSERT_NO_FATAL_FAILURE(CheckSaltLength(&pssParams, hashAlg)); // The default trailer field (1) must be omitted EXPECT_EQ(nullptr, pssParams.trailerField.data); } TEST_P(SignParamsTest, CreateRsaPssWithECPrivateKey) { SECOidTag hashAlg = std::get<0>(GetParam()); SECOidTag srcHashAlg = std::get<1>(GetParam()); SECItem *srcParams; if (srcHashAlg != SEC_OID_UNKNOWN) { SECKEYRSAPSSParams pssParams; ASSERT_NO_FATAL_FAILURE( CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); ASSERT_NE(nullptr, srcParams); } else { srcParams = NULL; } SECItem *params = SEC_CreateSignatureAlgorithmParameters( arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, srcParams, ecPrivk_.get()); EXPECT_EQ(nullptr, params); } TEST_P(SignParamsTest, CreateRsaPssWithInvalidHashAlg) { SECOidTag srcHashAlg = std::get<1>(GetParam()); SECItem *srcParams; if (srcHashAlg != SEC_OID_UNKNOWN) { SECKEYRSAPSSParams pssParams; ASSERT_NO_FATAL_FAILURE( CreatePssParams(&pssParams, srcHashAlg, srcHashAlg)); srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); ASSERT_NE(nullptr, srcParams); } else { srcParams = NULL; } SECItem *params = SEC_CreateSignatureAlgorithmParameters( arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, SEC_OID_MD5, srcParams, privk_.get()); EXPECT_EQ(nullptr, params); } TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidHashAlg) { SECOidTag hashAlg = GetParam(); SECItem *srcParams; SECKEYRSAPSSParams pssParams; ASSERT_NO_FATAL_FAILURE( CreatePssParams(&pssParams, SEC_OID_MD5, SEC_OID_MD5)); srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); ASSERT_NE(nullptr, srcParams); SECItem *params = SEC_CreateSignatureAlgorithmParameters( arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, srcParams, privk_.get()); EXPECT_EQ(nullptr, params); } TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidSaltLength) { SECOidTag hashAlg = GetParam(); SECItem *srcParams; SECKEYRSAPSSParams pssParams; ASSERT_NO_FATAL_FAILURE( CreatePssParams(&pssParams, SEC_OID_SHA512, SEC_OID_SHA512, 100)); srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); ASSERT_NE(nullptr, srcParams); SECItem *params = SEC_CreateSignatureAlgorithmParameters( arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, srcParams, privk_.get()); EXPECT_EQ(nullptr, params); } TEST_P(SignParamsSourceTest, CreateRsaPssWithHashMismatch) { SECOidTag hashAlg = GetParam(); SECItem *srcParams; SECKEYRSAPSSParams pssParams; ASSERT_NO_FATAL_FAILURE( CreatePssParams(&pssParams, SEC_OID_SHA256, SEC_OID_SHA512)); srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate)); ASSERT_NE(nullptr, srcParams); SECItem *params = SEC_CreateSignatureAlgorithmParameters( arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg, srcParams, privk_.get()); EXPECT_EQ(nullptr, params); } INSTANTIATE_TEST_SUITE_P( SignParamsTestCases, SignParamsTest, ::testing::Combine(::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1, SEC_OID_SHA224, SEC_OID_SHA256, SEC_OID_SHA384, SEC_OID_SHA512), ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1, SEC_OID_SHA224, SEC_OID_SHA256, SEC_OID_SHA384, SEC_OID_SHA512))); INSTANTIATE_TEST_SUITE_P(SignParamsSourceTestCases, SignParamsSourceTest, ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1, SEC_OID_SHA224, SEC_OID_SHA256, SEC_OID_SHA384, SEC_OID_SHA512)); } // namespace nss_test