| // Copyright 2020 Google LLC |
| // |
| // 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 "pqcrypto/cc/subtle/cecpq2_hkdf_sender_kem_boringssl.h" |
| |
| #include "absl/memory/memory.h" |
| #include "absl/strings/str_cat.h" |
| #include "openssl/bn.h" |
| #include "openssl/curve25519.h" |
| #include "openssl/hrss.h" |
| #include "tink/subtle/common_enums.h" |
| #include "tink/subtle/hkdf.h" |
| #include "tink/subtle/random.h" |
| #include "tink/subtle/subtle_util.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| |
| // This method only redirects the object creation to the appropriate class |
| // based on the chosen curve. As of now, the only curve supported is |
| // Curve25519. This method was designed to be generic enough to faciliate the |
| // extension of this hybrid KEM to support other curves. |
| // static |
| util::StatusOr<std::unique_ptr<const Cecpq2HkdfSenderKemBoringSsl>> |
| Cecpq2HkdfSenderKemBoringSsl::New(subtle::EllipticCurveType curve, |
| const absl::string_view ec_pubx, |
| const absl::string_view ec_puby, |
| const absl::string_view marshalled_hrss_pub) { |
| switch (curve) { |
| case EllipticCurveType::CURVE25519: |
| return Cecpq2HkdfX25519SenderKemBoringSsl::New(curve, ec_pubx, ec_puby, |
| marshalled_hrss_pub); |
| default: |
| return util::Status(util::error::UNIMPLEMENTED, |
| "Unsupported elliptic curve"); |
| } |
| } |
| |
| Cecpq2HkdfX25519SenderKemBoringSsl::Cecpq2HkdfX25519SenderKemBoringSsl( |
| const absl::string_view peer_ec_pubx, |
| const absl::string_view marshalled_hrss_pub) { |
| peer_public_key_x25519_ = |
| reinterpret_cast<const uint8_t*>(peer_ec_pubx.data()); |
| peer_marshalled_public_key_hrss_ = |
| reinterpret_cast<const uint8_t*>(marshalled_hrss_pub.data()); |
| } |
| |
| // static |
| util::StatusOr<std::unique_ptr<const Cecpq2HkdfSenderKemBoringSsl>> |
| Cecpq2HkdfX25519SenderKemBoringSsl::New( |
| subtle::EllipticCurveType curve, const absl::string_view pubx, |
| const absl::string_view puby, const absl::string_view marshalled_hrss_pub) { |
| auto status = |
| internal::CheckFipsCompatibility<Cecpq2HkdfX25519SenderKemBoringSsl>(); |
| if (!status.ok()) return status; |
| |
| // Basic input checking |
| if (curve != CURVE25519) { |
| return util::Status(util::error::INVALID_ARGUMENT, |
| "curve is not CURVE25519"); |
| } |
| if (pubx.size() != X25519_PUBLIC_VALUE_LEN) { |
| return util::Status(util::error::INVALID_ARGUMENT, |
| "pubx has unexpected length"); |
| } |
| if (!puby.empty()) { |
| return util::Status(util::error::INVALID_ARGUMENT, "puby is not empty"); |
| } |
| if (marshalled_hrss_pub.size() != HRSS_PUBLIC_KEY_BYTES) { |
| return util::Status(util::error::INVALID_ARGUMENT, |
| "marshalled_hrss_pub has unexpected length"); |
| } |
| |
| // If input parameters are ok, create a CECPQ2 Sender KEM instance |
| std::unique_ptr<const Cecpq2HkdfSenderKemBoringSsl> sender_kem( |
| new Cecpq2HkdfX25519SenderKemBoringSsl(pubx, marshalled_hrss_pub)); |
| return std::move(sender_kem); |
| } |
| |
| util::StatusOr<std::unique_ptr<const Cecpq2HkdfSenderKemBoringSsl::KemKey>> |
| Cecpq2HkdfX25519SenderKemBoringSsl::GenerateKey( |
| subtle::HashType hash, absl::string_view hkdf_salt, |
| absl::string_view hkdf_info, uint32_t key_size_in_bytes, |
| subtle::EcPointFormat point_format) const { |
| // Basic input validation: |
| if (point_format != EcPointFormat::COMPRESSED) { |
| return util::Status( |
| util::error::INVALID_ARGUMENT, |
| "X25519 only supports compressed elliptic curve points"); |
| } |
| if (key_size_in_bytes < 32) { |
| return util::Status(util::error::INVALID_ARGUMENT, |
| "key size length is smaller than 32 bytes " |
| "and thus not post-quantum secure."); |
| } |
| |
| // Generate the ephemeral X25519 key pair. Note that the |
| // X25519_kem_bytes holds the X25519 public key |
| util::SecretData ephemeral_x25519_private_key(X25519_PRIVATE_KEY_LEN); |
| std::string x25519_kem_bytes(X25519_PUBLIC_VALUE_LEN, '\0'); |
| X25519_keypair(const_cast<uint8_t*>( |
| reinterpret_cast<const uint8_t*>(x25519_kem_bytes.data())), |
| ephemeral_x25519_private_key.data()); |
| |
| // Generate the x25519 shared secret using peer's X25519 public key and |
| // locally generated ephemeral X25519 private key |
| util::SecretData x25519_shared_secret(X25519_SHARED_KEY_LEN); |
| X25519(x25519_shared_secret.data(), ephemeral_x25519_private_key.data(), |
| peer_public_key_x25519_); |
| |
| // Declare the hrss_shared_secret and hrss_kem_bytes to be used in HRSS encaps |
| util::SecretData hrss_shared_secret; |
| hrss_shared_secret.resize(HRSS_KEY_BYTES); |
| // The hrss_kem_bytes will contain the encrypted shared secret |
| std::string hrss_kem_bytes; |
| subtle::ResizeStringUninitialized(&hrss_kem_bytes, HRSS_CIPHERTEXT_BYTES); |
| |
| // Recover the internal HRSS public key representation from marshalled version |
| struct HRSS_public_key peer_public_key_hrss; |
| HRSS_parse_public_key(&peer_public_key_hrss, |
| peer_marshalled_public_key_hrss_); |
| |
| // Generate entropy to be used in encaps |
| util::SecretData encaps_entropy = |
| crypto::tink::subtle::Random::GetRandomKeyBytes(HRSS_ENCAP_BYTES); |
| |
| // Generate a random shared secret and encapsulate it using peer's HRSS public |
| // key |
| HRSS_encap(const_cast<uint8_t*>( |
| reinterpret_cast<const uint8_t*>(hrss_kem_bytes.data())), |
| reinterpret_cast<uint8_t*>(hrss_shared_secret.data()), |
| &peer_public_key_hrss, encaps_entropy.data()); |
| |
| // Concatenate the two kem_bytes |
| std::string kem_bytes(x25519_kem_bytes); |
| kem_bytes += hrss_kem_bytes; |
| |
| // Concatenate the two shared secrets with the two kem_bytes |
| std::string kem_bytes_and_shared_secrets = absl::StrCat( |
| kem_bytes, util::SecretDataAsStringView(x25519_shared_secret), |
| util::SecretDataAsStringView(hrss_shared_secret)); |
| util::SecretData ikm = |
| util::SecretDataFromStringView(kem_bytes_and_shared_secrets); |
| |
| // Compute the symmetric key from the two shared secrets, kem_bytes, hkdf_salt |
| // and hkdf_info using HKDF |
| auto symmetric_key_or = |
| Hkdf::ComputeHkdf(hash, ikm, hkdf_salt, hkdf_info, key_size_in_bytes); |
| if (!symmetric_key_or.ok()) { |
| return symmetric_key_or.status(); |
| } |
| util::SecretData symmetric_key = symmetric_key_or.ValueOrDie(); |
| |
| // Return the produced pair kem_bytes and symmetric_key |
| return absl::make_unique<const KemKey>(kem_bytes, symmetric_key); |
| } |
| |
| } // namespace subtle |
| } // namespace tink |
| } // namespace crypto |