| // Copyright 2017 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| #include "tink/subtle/subtle_util_boringssl.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "gtest/gtest.h" |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/str_cat.h" |
| #include "openssl/cipher.h" |
| #include "openssl/curve25519.h" |
| #include "openssl/digest.h" |
| #include "openssl/ec.h" |
| #include "openssl/evp.h" |
| #include "openssl/x509.h" |
| #include "include/rapidjson/document.h" |
| #include "tink/subtle/common_enums.h" |
| #include "tink/subtle/ec_util.h" |
| #include "tink/subtle/wycheproof_util.h" |
| #include "tink/util/secret_data.h" |
| #include "tink/util/status.h" |
| #include "tink/util/statusor.h" |
| #include "tink/util/test_matchers.h" |
| #include "tink/util/test_util.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| namespace { |
| using ::crypto::tink::test::IsOk; |
| using ::crypto::tink::test::StatusIs; |
| using ::testing::IsEmpty; |
| using ::testing::Not; |
| using ::testing::NotNull; |
| using ::testing::StrEq; |
| |
| struct EncodingTestVector { |
| EcPointFormat format; |
| std::string x_hex; |
| std::string y_hex; |
| std::string encoded_hex; |
| EllipticCurveType curve; |
| }; |
| |
| static const std::vector<EncodingTestVector> encoding_test_vector( |
| {{EcPointFormat::UNCOMPRESSED, |
| "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec66" |
| "19b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a", |
| "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac" |
| "aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf", |
| "0400093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec" |
| "6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a" |
| "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac" |
| "aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf", |
| EllipticCurveType::NIST_P521}, |
| {EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED, |
| "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec66" |
| "19b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a", |
| "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac" |
| "aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf", |
| "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec" |
| "6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a" |
| "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac" |
| "aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf", |
| EllipticCurveType::NIST_P521}, |
| {EcPointFormat::COMPRESSED, |
| "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec66" |
| "19b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a", |
| "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327ac" |
| "aac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf", |
| "0300093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec" |
| "6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a", |
| EllipticCurveType::NIST_P521}}); |
| |
| TEST(SubtleUtilBoringSSLTest, EcPointEncode) { |
| for (const EncodingTestVector& test : encoding_test_vector) { |
| std::string x_str = test::HexDecodeOrDie(test.x_hex); |
| std::string y_str = test::HexDecodeOrDie(test.y_hex); |
| bssl::UniquePtr<BIGNUM> x( |
| BN_bin2bn(reinterpret_cast<const unsigned char*>(x_str.data()), |
| x_str.length(), nullptr)); |
| bssl::UniquePtr<BIGNUM> y( |
| BN_bin2bn(reinterpret_cast<const unsigned char*>(y_str.data()), |
| y_str.length(), nullptr)); |
| auto status_or_group = SubtleUtilBoringSSL::GetEcGroup(test.curve); |
| bssl::UniquePtr<EC_POINT> point(EC_POINT_new(status_or_group.ValueOrDie())); |
| EXPECT_EQ(1, EC_POINT_set_affine_coordinates_GFp( |
| status_or_group.ValueOrDie(), point.get(), x.get(), |
| y.get(), nullptr)); |
| auto encoded_or = SubtleUtilBoringSSL::EcPointEncode( |
| test.curve, test.format, point.get()); |
| EXPECT_TRUE(encoded_or.ok()); |
| EXPECT_EQ(test.encoded_hex, test::HexEncode(encoded_or.ValueOrDie())); |
| } |
| } |
| |
| TEST(SubtleUtilBoringSSLTest, EcPointDecode) { |
| for (const EncodingTestVector& test : encoding_test_vector) { |
| std::string x_str = test::HexDecodeOrDie(test.x_hex); |
| std::string y_str = test::HexDecodeOrDie(test.y_hex); |
| std::string encoded_str = test::HexDecodeOrDie(test.encoded_hex); |
| bssl::UniquePtr<BIGNUM> x( |
| BN_bin2bn(reinterpret_cast<const unsigned char*>(x_str.data()), |
| x_str.length(), nullptr)); |
| bssl::UniquePtr<BIGNUM> y( |
| BN_bin2bn(reinterpret_cast<const unsigned char*>(y_str.data()), |
| y_str.length(), nullptr)); |
| auto status_or_group = SubtleUtilBoringSSL::GetEcGroup(test.curve); |
| bssl::UniquePtr<EC_POINT> point(EC_POINT_new(status_or_group.ValueOrDie())); |
| EXPECT_EQ(1, EC_POINT_set_affine_coordinates_GFp( |
| status_or_group.ValueOrDie(), point.get(), x.get(), |
| y.get(), nullptr)); |
| auto status_or_ec_point = SubtleUtilBoringSSL::EcPointDecode( |
| test.curve, test.format, encoded_str); |
| EXPECT_TRUE(status_or_ec_point.ok()); |
| EXPECT_EQ(0, EC_POINT_cmp(status_or_group.ValueOrDie(), point.get(), |
| status_or_ec_point.ValueOrDie().get(), nullptr)); |
| // Modify the 1st byte. |
| encoded_str = std::string("0") + encoded_str.substr(1); |
| auto status_or_ec_point2 = SubtleUtilBoringSSL::EcPointDecode( |
| test.curve, test.format, encoded_str); |
| EXPECT_FALSE(status_or_ec_point2.ok()); |
| EXPECT_LE(0, status_or_ec_point2.status().error_message().find( |
| "point should start with")); |
| } |
| } |
| |
| TEST(SubtleUtilBoringSSLTest, Bn2strAndStr2bn) { |
| int len = 8; |
| std::string bn_str[6] = {"0000000000000000", "0000000000000001", |
| "1000000000000000", "ffffffffffffffff", |
| "0fffffffffffffff", "00ffffffffffffff"}; |
| for (const std::string& s : bn_str) { |
| auto status_or_bn = SubtleUtilBoringSSL::str2bn(test::HexDecodeOrDie(s)); |
| EXPECT_TRUE(status_or_bn.ok()); |
| auto status_or_str = |
| SubtleUtilBoringSSL::bn2str(status_or_bn.ValueOrDie().get(), len); |
| EXPECT_TRUE(status_or_str.ok()); |
| EXPECT_EQ(test::HexDecodeOrDie(s), status_or_str.ValueOrDie()); |
| } |
| } |
| |
| TEST(SubtleUtilBoringSSLTest, ValidateSignatureHash) { |
| EXPECT_TRUE( |
| SubtleUtilBoringSSL::ValidateSignatureHash(HashType::SHA256).ok()); |
| EXPECT_TRUE( |
| SubtleUtilBoringSSL::ValidateSignatureHash(HashType::SHA512).ok()); |
| EXPECT_FALSE(SubtleUtilBoringSSL::ValidateSignatureHash(HashType::SHA1).ok()); |
| EXPECT_FALSE( |
| SubtleUtilBoringSSL::ValidateSignatureHash(HashType::UNKNOWN_HASH).ok()); |
| } |
| |
| static std::string GetError() { |
| auto err = ERR_peek_last_error(); |
| // Sometimes there is no error message on the stack. |
| if (err == 0) { |
| return ""; |
| } |
| std::string lib(ERR_lib_error_string(err)); |
| std::string func(ERR_func_error_string(err)); |
| std::string reason(ERR_reason_error_string(err)); |
| return lib + ":" + func + ":" + reason; |
| } |
| |
| // Test with test vectors from Wycheproof project. |
| bool WycheproofTest(const rapidjson::Value& root) { |
| int errors = 0; |
| for (const rapidjson::Value& test_group : root["testGroups"].GetArray()) { |
| std::string curve_str = test_group["curve"].GetString(); |
| // Tink only supports secp256r1, secp384r1 or secp521r1. |
| if (!(curve_str == "secp256r1" || curve_str == "secp384r1" || |
| curve_str == "secp521r1")) { |
| continue; |
| } |
| EllipticCurveType curve = |
| WycheproofUtil::GetEllipticCurveType(test_group["curve"]); |
| for (const rapidjson::Value& test : test_group["tests"].GetArray()) { |
| std::string id = absl::StrCat(test["tcId"].GetInt()); |
| std::string comment = test["comment"].GetString(); |
| std::string pub_bytes = WycheproofUtil::GetBytes(test["public"]); |
| std::string priv_bytes = WycheproofUtil::GetBytes(test["private"]); |
| std::string expected_shared_bytes = |
| WycheproofUtil::GetBytes(test["shared"]); |
| std::string result = test["result"].GetString(); |
| EcPointFormat format = EcPointFormat::UNCOMPRESSED; |
| for (const rapidjson::Value& flag : test["flags"].GetArray()) { |
| if (std::string(flag.GetString()) == "CompressedPoint") { |
| format = EcPointFormat::COMPRESSED; |
| } |
| } |
| // Wycheproof's ECDH public key uses ASN encoding while Tink uses X9.62 |
| // format point encoding. For the purpose of testing, we note the |
| // followings: |
| // + The prefix of ASN encoding contains curve name, so we can skip test |
| // vector with "UnnamedCurve". |
| // + The suffix of ASN encoding is X9.62 format point encoding. |
| // TODO(quannguyen): Use X9.62 test vectors once it's available. |
| bool skip = false; |
| for (const rapidjson::Value& flag : test["flags"].GetArray()) { |
| if (std::string(flag.GetString()) == "UnnamedCurve") { |
| skip = true; |
| break; |
| } |
| } |
| if (skip) { |
| continue; |
| } |
| auto status_or_point_size = EcUtil::EncodingSizeInBytes(curve, format); |
| if (!status_or_point_size.ok()) { |
| continue; |
| } |
| size_t point_size = status_or_point_size.ValueOrDie(); |
| if (point_size > pub_bytes.size()) { |
| continue; |
| } |
| pub_bytes = pub_bytes.substr(pub_bytes.size() - point_size, point_size); |
| auto status_or_ec_point = |
| SubtleUtilBoringSSL ::EcPointDecode(curve, format, pub_bytes); |
| if (!status_or_ec_point.ok()) { |
| if (result == "valid") { |
| ADD_FAILURE() << "Could not decode public key with tcId:" << id |
| << " error:" << GetError() |
| << status_or_ec_point.status(); |
| } |
| continue; |
| } |
| bssl::UniquePtr<EC_POINT> pub_key = |
| std::move(status_or_ec_point.ValueOrDie()); |
| bssl::UniquePtr<BIGNUM> priv_key( |
| BN_bin2bn(reinterpret_cast<const unsigned char*>(priv_bytes.data()), |
| priv_bytes.size(), nullptr)); |
| auto status_or_shared = SubtleUtilBoringSSL ::ComputeEcdhSharedSecret( |
| curve, priv_key.get(), pub_key.get()); |
| if (status_or_shared.ok()) { |
| util::SecretData shared = status_or_shared.ValueOrDie(); |
| if (result == "invalid") { |
| ADD_FAILURE() << "Computed shared secret with invalid test vector" |
| << ", tcId= " << id; |
| errors++; |
| } else if (util::SecretDataAsStringView(shared) != |
| expected_shared_bytes) { |
| ADD_FAILURE() << "Computed wrong shared secret with tcId: " << id; |
| errors++; |
| } |
| } else { |
| if (result == "valid" || result == "acceptable") { |
| ADD_FAILURE() << "Could not compute shared secret with tcId:" << id; |
| errors++; |
| } |
| } |
| } |
| } |
| return errors == 0; |
| } |
| |
| TEST(SubtleUtilBoringSSLTest, ComputeEcdhSharedSecretWithWycheproofTest) { |
| // placeholder_disabled_subtle_test, please ignore |
| ASSERT_TRUE(WycheproofTest( |
| *WycheproofUtil ::ReadTestVectors("ecdh_test.json"))); |
| ASSERT_TRUE(WycheproofTest( |
| *WycheproofUtil ::ReadTestVectors("ecdh_secp256r1_test.json"))); |
| ASSERT_TRUE(WycheproofTest( |
| *WycheproofUtil ::ReadTestVectors("ecdh_secp384r1_test.json"))); |
| ASSERT_TRUE(WycheproofTest( |
| *WycheproofUtil ::ReadTestVectors("ecdh_secp521r1_test.json"))); |
| } |
| |
| TEST(CreatesNewRsaKeyPairTest, BasicSanityChecks) { |
| SubtleUtilBoringSSL::RsaPublicKey public_key; |
| SubtleUtilBoringSSL::RsaPrivateKey private_key; |
| |
| bssl::UniquePtr<BIGNUM> e(BN_new()); |
| BN_set_word(e.get(), RSA_F4); |
| ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key, |
| &public_key), |
| IsOk()); |
| EXPECT_THAT(private_key.n, Not(IsEmpty())); |
| EXPECT_THAT(private_key.e, Not(IsEmpty())); |
| EXPECT_THAT(private_key.d, Not(IsEmpty())); |
| |
| EXPECT_THAT(private_key.p, Not(IsEmpty())); |
| EXPECT_THAT(private_key.q, Not(IsEmpty())); |
| EXPECT_THAT(private_key.dp, Not(IsEmpty())); |
| EXPECT_THAT(private_key.dq, Not(IsEmpty())); |
| EXPECT_THAT(private_key.crt, Not(IsEmpty())); |
| |
| EXPECT_THAT(public_key.n, Not(IsEmpty())); |
| EXPECT_THAT(public_key.e, Not(IsEmpty())); |
| |
| EXPECT_EQ(public_key.n, private_key.n); |
| EXPECT_EQ(public_key.e, private_key.e); |
| } |
| |
| TEST(CreatesNewRsaKeyPairTest, FailsOnLargeE) { |
| // OpenSSL requires the "e" value to be at most 32 bits. |
| SubtleUtilBoringSSL::RsaPublicKey public_key; |
| SubtleUtilBoringSSL::RsaPrivateKey private_key; |
| |
| bssl::UniquePtr<BIGNUM> e(BN_new()); |
| BN_set_word(e.get(), 1L << 33); |
| ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key, |
| &public_key), |
| StatusIs(util::error::INTERNAL)); |
| } |
| |
| TEST(CreatesNewRsaKeyPairTest, KeyIsWellFormed) { |
| SubtleUtilBoringSSL::RsaPublicKey public_key; |
| SubtleUtilBoringSSL::RsaPrivateKey private_key; |
| bssl::UniquePtr<BIGNUM> e(BN_new()); |
| BN_set_word(e.get(), RSA_F4); |
| ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key, |
| &public_key), |
| IsOk()); |
| auto n = std::move(SubtleUtilBoringSSL::str2bn(private_key.n).ValueOrDie()); |
| auto d = std::move( |
| SubtleUtilBoringSSL::str2bn(util::SecretDataAsStringView(private_key.d)) |
| .ValueOrDie()); |
| auto p = std::move( |
| SubtleUtilBoringSSL::str2bn(util::SecretDataAsStringView(private_key.p)) |
| .ValueOrDie()); |
| auto q = std::move( |
| SubtleUtilBoringSSL::str2bn(util::SecretDataAsStringView(private_key.q)) |
| .ValueOrDie()); |
| auto dp = std::move( |
| SubtleUtilBoringSSL::str2bn(util::SecretDataAsStringView(private_key.dp)) |
| .ValueOrDie()); |
| auto dq = std::move( |
| SubtleUtilBoringSSL::str2bn(util::SecretDataAsStringView(private_key.dq)) |
| .ValueOrDie()); |
| bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new()); |
| |
| // Check n = p * q. |
| { |
| auto n_calc = bssl::UniquePtr<BIGNUM>(BN_new()); |
| ASSERT_TRUE(BN_mul(n_calc.get(), p.get(), q.get(), ctx.get())); |
| ASSERT_TRUE(BN_equal_consttime(n_calc.get(), n.get())); |
| } |
| |
| // Check n size >= 2048 bit. |
| EXPECT_GE(BN_num_bits(n.get()), 2048); |
| |
| // dp = d mod (p - 1) |
| { |
| auto pm1 = bssl::UniquePtr<BIGNUM>(BN_dup(p.get())); |
| ASSERT_TRUE(BN_sub_word(pm1.get(), 1)); |
| auto dp_calc = bssl::UniquePtr<BIGNUM>(BN_new()); |
| ASSERT_TRUE(BN_mod(dp_calc.get(), d.get(), pm1.get(), ctx.get())); |
| |
| ASSERT_TRUE(BN_equal_consttime(dp_calc.get(), dp.get())); |
| } |
| |
| // dq = d mod (q - 1) |
| { |
| auto qm1 = bssl::UniquePtr<BIGNUM>(BN_dup(q.get())); |
| ASSERT_TRUE(BN_sub_word(qm1.get(), 1)); |
| auto dq_calc = bssl::UniquePtr<BIGNUM>(BN_new()); |
| ASSERT_TRUE(BN_mod(dq_calc.get(), d.get(), qm1.get(), ctx.get())); |
| |
| ASSERT_TRUE(BN_equal_consttime(dq_calc.get(), dq.get())); |
| } |
| } |
| |
| TEST(CreatesNewRsaKeyPairTest, GeneratesDifferentKeysEveryTime) { |
| SubtleUtilBoringSSL::RsaPublicKey public_key; |
| bssl::UniquePtr<BIGNUM> e(BN_new()); |
| BN_set_word(e.get(), RSA_F4); |
| |
| std::vector<SubtleUtilBoringSSL::RsaPrivateKey> generated_keys; |
| std::generate_n(std::back_inserter(generated_keys), 4, [&]() { |
| SubtleUtilBoringSSL::RsaPrivateKey private_key; |
| EXPECT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair( |
| 2048, e.get(), &private_key, &public_key), |
| IsOk()); |
| return private_key; |
| }); |
| |
| // Iterate through a two-element sliding windows, comparing two consecutive |
| // elements in the list. |
| for (std::size_t i = 0; i + 1 < generated_keys.size(); ++i) { |
| const auto& left = generated_keys[i]; |
| const auto& right = generated_keys[i + 1]; |
| |
| // The only fieldthat should be equal. |
| ASSERT_EQ(left.e, right.e); |
| |
| ASSERT_NE(left.n, right.n); |
| ASSERT_NE(left.d, right.d); |
| |
| ASSERT_NE(left.p, right.p); |
| ASSERT_NE(left.q, right.q); |
| ASSERT_NE(left.dp, right.dp); |
| ASSERT_NE(left.dq, right.dq); |
| ASSERT_NE(left.crt, right.crt); |
| } |
| } |
| |
| TEST(CreatesNewEd25519KeyPairTest, BoringSSLPrivateKeySuffix) { |
| // Generate a new key pair. |
| uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN]; |
| uint8_t out_private_key[ED25519_PRIVATE_KEY_LEN]; |
| |
| ED25519_keypair(out_public_key, out_private_key); |
| std::string pk = std::string(reinterpret_cast<const char*>(out_public_key), |
| ED25519_PUBLIC_KEY_LEN); |
| std::string sk = std::string(reinterpret_cast<const char*>(out_private_key), |
| ED25519_PRIVATE_KEY_LEN); |
| ASSERT_EQ(pk.length(), 32); |
| ASSERT_EQ(sk.length(), 64); |
| // BoringSSL's ED25519_keypair returns a private key with the last 32-bytes |
| // equal to the public key. If this changes you must update |
| // SubtleUtilBoringSSL::GetNewEd25519Key(). |
| ASSERT_EQ(sk.substr(32, std::string::npos), pk); |
| } |
| |
| TEST(CreatesNewEd25519KeyPairTest, KeyIsWellFormed) { |
| auto keypair = SubtleUtilBoringSSL::GetNewEd25519Key(); |
| ASSERT_EQ(keypair->public_key.length(), 32); |
| ASSERT_EQ(keypair->private_key.length(), 32); |
| ASSERT_TRUE(keypair->public_key != keypair->private_key); |
| } |
| |
| TEST(CreatesNewEd25519KeyPairTest, GeneratesDifferentKeysEveryTime) { |
| auto keypair1 = SubtleUtilBoringSSL::GetNewEd25519Key(); |
| auto keypair2 = SubtleUtilBoringSSL::GetNewEd25519Key(); |
| ASSERT_NE(keypair1->public_key, keypair2->public_key); |
| ASSERT_NE(keypair1->private_key, keypair2->private_key); |
| ASSERT_NE(keypair1->public_key, keypair1->private_key); |
| } |
| |
| TEST(CreateNewX25519KeyTest, KeyIsWellFormed) { |
| auto ec_key_or_status = |
| SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::CURVE25519); |
| ASSERT_THAT(ec_key_or_status.status(), IsOk()); |
| auto ec_key = ec_key_or_status.ValueOrDie(); |
| EXPECT_EQ(ec_key.curve, EllipticCurveType::CURVE25519); |
| EXPECT_EQ(ec_key.pub_x.length(), X25519_PUBLIC_VALUE_LEN); |
| EXPECT_TRUE(ec_key.pub_y.empty()); |
| EXPECT_EQ(ec_key.priv.size(), X25519_PRIVATE_KEY_LEN); |
| } |
| |
| TEST(CreateNewX25519KeyTest, GeneratesDifferentKeysEveryTime) { |
| auto keypair1 = SubtleUtilBoringSSL::GenerateNewX25519Key(); |
| auto keypair2 = SubtleUtilBoringSSL::GenerateNewX25519Key(); |
| |
| EXPECT_FALSE(std::equal(keypair1->private_key, |
| &keypair1->private_key[X25519_PRIVATE_KEY_LEN], |
| keypair2->private_key)); |
| EXPECT_FALSE(std::equal(keypair1->public_value, |
| &keypair1->public_value[X25519_PUBLIC_VALUE_LEN], |
| keypair2->public_value)); |
| } |
| |
| TEST(EcKeyFromX25519KeyTest, RoundTripKey) { |
| auto x25519_key = SubtleUtilBoringSSL::GenerateNewX25519Key(); |
| ASSERT_THAT(x25519_key, NotNull()); |
| auto ec_key = SubtleUtilBoringSSL::EcKeyFromX25519Key(x25519_key.get()); |
| ASSERT_EQ(ec_key.curve, EllipticCurveType::CURVE25519); |
| |
| auto roundtrip_key_or_status = |
| SubtleUtilBoringSSL::X25519KeyFromEcKey(ec_key); |
| ASSERT_THAT(roundtrip_key_or_status.status(), IsOk()); |
| |
| auto roundtrip_key = std::move(roundtrip_key_or_status.ValueOrDie()); |
| EXPECT_TRUE(std::equal(x25519_key->private_key, |
| &x25519_key->private_key[X25519_PRIVATE_KEY_LEN], |
| roundtrip_key->private_key)); |
| EXPECT_TRUE(std::equal(x25519_key->public_value, |
| &x25519_key->public_value[X25519_PUBLIC_VALUE_LEN], |
| roundtrip_key->public_value)); |
| } |
| |
| TEST(X25519KeyFromEcKeyTest, RejectNistPCurves) { |
| auto ec_key_or_status = |
| SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::NIST_P256); |
| ASSERT_THAT(ec_key_or_status.status(), IsOk()); |
| |
| auto x25519_key_or_status = |
| SubtleUtilBoringSSL::X25519KeyFromEcKey(ec_key_or_status.ValueOrDie()); |
| EXPECT_THAT(x25519_key_or_status.status(), |
| StatusIs(util::error::INVALID_ARGUMENT)); |
| } |
| |
| TEST(SubtleUtilBoringSSLTest, ValidateRsaModulusSize) { |
| SubtleUtilBoringSSL::RsaPublicKey public_key; |
| SubtleUtilBoringSSL::RsaPrivateKey private_key; |
| bssl::UniquePtr<BIGNUM> e(BN_new()); |
| BN_set_word(e.get(), RSA_F4); |
| ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(2048, e.get(), &private_key, |
| &public_key), |
| IsOk()); |
| auto n_2048 = |
| std::move(SubtleUtilBoringSSL::str2bn(private_key.n).ValueOrDie()); |
| ASSERT_THAT( |
| SubtleUtilBoringSSL::ValidateRsaModulusSize(BN_num_bits(n_2048.get())), |
| IsOk()); |
| |
| ASSERT_THAT(SubtleUtilBoringSSL::GetNewRsaKeyPair(1024, e.get(), &private_key, |
| &public_key), |
| IsOk()); |
| auto n_1024 = |
| std::move(SubtleUtilBoringSSL::str2bn(private_key.n).ValueOrDie()); |
| ASSERT_THAT( |
| SubtleUtilBoringSSL::ValidateRsaModulusSize(BN_num_bits(n_1024.get())), |
| Not(IsOk())); |
| } |
| |
| TEST(SublteUtilBoringSSLTest, GetCipherForKeySize) { |
| EXPECT_EQ(SubtleUtilBoringSSL::GetAesCtrCipherForKeySize(16), |
| EVP_aes_128_ctr()); |
| EXPECT_EQ(SubtleUtilBoringSSL::GetAesCtrCipherForKeySize(32), |
| EVP_aes_256_ctr()); |
| EXPECT_EQ(SubtleUtilBoringSSL::GetAesCtrCipherForKeySize(64), nullptr); |
| } |
| |
| TEST(ComputeHashTest, AcceptsNullStringView) { |
| auto null_hash = |
| boringssl::ComputeHash(absl::string_view(nullptr, 0), *EVP_sha512()); |
| auto empty_hash = boringssl::ComputeHash("", *EVP_sha512()); |
| std::string str; |
| auto empty_str_hash = boringssl::ComputeHash(str, *EVP_sha512()); |
| |
| ASSERT_THAT(null_hash.status(), IsOk()); |
| ASSERT_THAT(empty_hash.status(), IsOk()); |
| ASSERT_THAT(empty_str_hash.status(), IsOk()); |
| |
| EXPECT_EQ(null_hash.ValueOrDie(), empty_hash.ValueOrDie()); |
| EXPECT_EQ(null_hash.ValueOrDie(), empty_str_hash.ValueOrDie()); |
| } |
| |
| using ComputeHashSamplesTest = ::testing::TestWithParam< |
| std::tuple<HashType, absl::string_view, absl::string_view>>; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| NistSampleCases, ComputeHashSamplesTest, |
| ::testing::Values( |
| std::make_tuple( |
| HashType::SHA256, "af397a8b8dd73ab702ce8e53aa9f", |
| "d189498a3463b18e846b8ab1b41583b0b7efc789dad8a7fb885bbf8fb5b45c5c"), |
| std::make_tuple( |
| HashType::SHA256, "59eb45bbbeb054b0b97334d53580ce03f699", |
| "32c38c54189f2357e96bd77eb00c2b9c341ebebacc2945f97804f59a93238288"), |
| std::make_tuple( |
| HashType::SHA512, |
| "16b17074d3e3d97557f9ed77d920b4b1bff4e845b345a922", |
| "6884134582a760046433abcbd53db8ff1a89995862f305b887020f6da6c7b903a3" |
| "14721e972bf438483f452a8b09596298a576c903c91df4a414c7bd20fd1d07"), |
| std::make_tuple( |
| HashType::SHA512, |
| "7651ab491b8fa86f969d42977d09df5f8bee3e5899180b52c968b0db057a6f02a8" |
| "86ad617a84915a", |
| "f35e50e2e02b8781345f8ceb2198f068ba103476f715cfb487a452882c9f0de0c7" |
| "20b2a088a39d06a8a6b64ce4d6470dfeadc4f65ae06672c057e29f14c4daf9"))); |
| |
| TEST_P(ComputeHashSamplesTest, ComputesHash) { |
| const EVP_MD* hasher = |
| SubtleUtilBoringSSL::EvpHash(std::get<0>(GetParam())).ValueOrDie(); |
| std::string data = absl::HexStringToBytes(std::get<1>(GetParam())); |
| std::string expected_hash = absl::HexStringToBytes(std::get<2>(GetParam())); |
| |
| auto hash_or = boringssl::ComputeHash(data, *hasher); |
| ASSERT_THAT(hash_or.status(), IsOk()); |
| std::string hash(reinterpret_cast<char*>(hash_or.ValueOrDie().data()), |
| hash_or.ValueOrDie().size()); |
| EXPECT_THAT(hash, StrEq(expected_hash)); |
| } |
| |
| } // namespace |
| } // namespace subtle |
| } // namespace tink |
| } // namespace crypto |