#include #include #include #include #include #include #include #include #include #include #include namespace vanetza { namespace security { namespace { int openssl_nid(KeyType key) { int nid; switch (key) { case KeyType::NistP256: nid = NID_X9_62_prime256v1; break; case KeyType::BrainpoolP256r1: nid = NID_brainpoolP256r1; break; case KeyType::BrainpoolP384r1: nid = NID_brainpoolP384r1; break; default: nid = NID_undef; break; } return nid; } } // namespace BackendOpenSsl::BackendOpenSsl() { OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr); } EcdsaSignature BackendOpenSsl::sign_data(const ecdsa256::PrivateKey& key, const ByteBuffer& data) { auto priv_key = internal_private_key(key); auto digest = calculate_sha256_digest(data); // sign message data represented by the digest openssl::Signature signature { ECDSA_do_sign(digest.data(), digest.size(), priv_key) }; const BIGNUM* sig_r = nullptr; const BIGNUM* sig_s = nullptr; ECDSA_SIG_get0(signature, &sig_r, &sig_s); EcdsaSignature ecdsa_signature; X_Coordinate_Only coordinate; if (sig_r && sig_s) { const size_t len = field_size(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256); const auto num_bytes_s = BN_num_bytes(sig_s); assert(len >= static_cast(num_bytes_s)); ecdsa_signature.s.resize(len, 0x00); BN_bn2bin(sig_s, ecdsa_signature.s.data() + len - num_bytes_s); const auto num_bytes_r = BN_num_bytes(sig_r); assert(len >= static_cast(num_bytes_r)); coordinate.x.resize(len, 0x00); BN_bn2bin(sig_r, coordinate.x.data() + len - num_bytes_r); } else { throw openssl::Exception(); } ecdsa_signature.R = std::move(coordinate); return ecdsa_signature; } Signature BackendOpenSsl::sign_digest(const PrivateKey& key, const ByteBuffer& digest) { // sign message data represented by the digest auto priv_key = internal_private_key(key); openssl::Signature signature { ECDSA_do_sign(digest.data(), digest.size(), priv_key) }; const BIGNUM* sig_r = nullptr; const BIGNUM* sig_s = nullptr; ECDSA_SIG_get0(signature, &sig_r, &sig_s); Signature ecdsa_signature; ecdsa_signature.type = key.type; if (sig_r && sig_s) { const size_t len = key_length(key.type); const auto num_bytes_s = BN_num_bytes(sig_s); assert(len >= static_cast(num_bytes_s)); ecdsa_signature.s.resize(len, 0x00); BN_bn2bin(sig_s, ecdsa_signature.s.data() + len - num_bytes_s); const auto num_bytes_r = BN_num_bytes(sig_r); assert(len >= static_cast(num_bytes_r)); ecdsa_signature.r.resize(len, 0x00); BN_bn2bin(sig_r, ecdsa_signature.r.data() + len - num_bytes_r); } else { throw openssl::Exception(); } return ecdsa_signature; } bool BackendOpenSsl::verify_data(const ecdsa256::PublicKey& key, const ByteBuffer& data, const EcdsaSignature& sig) { try { auto digest = calculate_sha256_digest(data); auto pub = internal_public_key(key); openssl::Signature signature(sig); return (ECDSA_do_verify(digest.data(), digest.size(), signature, pub) == 1); } catch (const openssl::Exception&) { return false; } } bool BackendOpenSsl::verify_digest(const PublicKey& gpub, const ByteBuffer& digest, const Signature& gsig) { if (gpub.type != gsig.type) { return false; } try { openssl::Key pub = internal_public_key(gpub); openssl::Signature sig { gsig }; return ECDSA_do_verify(digest.data(), digest.size(), sig, pub) == 1; } catch (const openssl::Exception&) { return false; } } boost::optional BackendOpenSsl::decompress_point(const EccPoint& ecc_point) { struct DecompressionVisitor : public boost::static_visitor { bool operator()(const X_Coordinate_Only&) { return false; } bool operator()(const Compressed_Lsb_Y_0& p) { return decompress(p.x, 0); } bool operator()(const Compressed_Lsb_Y_1& p) { return decompress(p.x, 1); } bool operator()(const Uncompressed& p) { result = p; return true; } bool decompress(const ByteBuffer& x, int y_bit) { try { openssl::BigNumberContext ctx; openssl::BigNumber x_coordinate(x); openssl::Group group(NID_X9_62_prime256v1); openssl::Point point(group); openssl::BigNumber y_coordinate; result.x = x; result.y.resize(result.x.size()); EC_POINT_set_compressed_coordinates(group, point, x_coordinate, y_bit, ctx); EC_POINT_get_affine_coordinates(group, point, nullptr, y_coordinate, ctx); return (BN_bn2binpad(y_coordinate, result.y.data(), result.y.size()) != -1); } catch (const openssl::Exception&) { return false; } } Uncompressed result; }; DecompressionVisitor visitor; if (boost::apply_visitor(visitor, ecc_point)) { return visitor.result; } else { return boost::none; } } ByteBuffer BackendOpenSsl::calculate_hash(HashAlgorithm algo, const ByteBuffer& data) { ByteBuffer result; if (algo == HashAlgorithm::SHA256) { auto digest = calculate_sha256_digest(data); result.assign(digest.begin(), digest.end()); } else if (algo == HashAlgorithm::SHA384) { auto digest = calculate_sha384_digest(data); result.assign(digest.begin(), digest.end()); } return result; } std::array BackendOpenSsl::calculate_sha256_digest(const ByteBuffer& data) const { static_assert(SHA256_DIGEST_LENGTH == 32, "Unexpected length of SHA256 digest"); std::array digest; SHA256_CTX ctx; SHA256_Init(&ctx); SHA256_Update(&ctx, data.data(), data.size()); SHA256_Final(digest.data(), &ctx); return digest; } std::array BackendOpenSsl::calculate_sha384_digest(const ByteBuffer& data) const { static_assert(SHA384_DIGEST_LENGTH == 48, "Unexpected length of SHA384 digest"); std::array digest; SHA384(data.data(), data.size(), digest.data()); return digest; } openssl::Key BackendOpenSsl::internal_private_key(const ecdsa256::PrivateKey& generic) const { openssl::Key key(NID_X9_62_prime256v1); openssl::BigNumber prv(generic.key); EC_KEY_set_private_key(key, prv); // OpenSSL requires public key, so we recreate it from private key openssl::BigNumberContext ctx; const EC_GROUP* group = EC_KEY_get0_group(key); openssl::Point pub(group); openssl::check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx)); EC_KEY_set_public_key(key, pub); openssl::check(EC_KEY_check_key(key)); return key; } openssl::Key BackendOpenSsl::internal_private_key(const PrivateKey& generic) const { openssl::Key key(openssl_nid(generic.type)); openssl::BigNumber prv(generic.key); EC_KEY_set_private_key(key, prv); // OpenSSL requires public key, so we recreate it from private key openssl::BigNumberContext ctx; const EC_GROUP* group = EC_KEY_get0_group(key); openssl::Point pub(group); openssl::check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx)); EC_KEY_set_public_key(key, pub); openssl::check(EC_KEY_check_key(key)); return key; } openssl::Key BackendOpenSsl::internal_public_key(const ecdsa256::PublicKey& generic) const { openssl::Key key(NID_X9_62_prime256v1); openssl::BigNumber x(generic.x); openssl::BigNumber y(generic.y); EC_KEY_set_public_key_affine_coordinates(key, x, y); openssl::check(EC_KEY_check_key(key)); return key; } openssl::Key BackendOpenSsl::internal_public_key(const PublicKey& generic) const { openssl::Key key(openssl_nid(generic.type)); openssl::Point point = internal_ec_point(generic); EC_KEY_set_public_key(key, point); openssl::check(EC_KEY_check_key(key)); return key; } ecdsa256::KeyPair BackendOpenSsl::generate_key_pair() { ecdsa256::KeyPair key_pair; openssl::Key key(NID_X9_62_prime256v1); openssl::check(EC_KEY_generate_key(key)); const BIGNUM* priv_bn = EC_KEY_get0_private_key(key); const EC_POINT* pub_point = EC_KEY_get0_public_key(key); const EC_GROUP* group = EC_KEY_get0_group(key); // extract private key key_pair.private_key.key.fill(0); auto priv_bytes = BN_num_bytes(priv_bn); BN_bn2bin(priv_bn, key_pair.private_key.key.data() + key_pair.private_key.key.size() - priv_bytes); // extract public key coordinates openssl::BigNumber x; openssl::BigNumber y; openssl::BigNumberContext ctx; EC_POINT_get_affine_coordinates(group, pub_point, x, y, ctx); BN_bn2binpad(x, key_pair.public_key.x.data(), key_pair.public_key.x.size()); BN_bn2binpad(y, key_pair.public_key.y.data(), key_pair.public_key.y.size()); return key_pair; } openssl::Point BackendOpenSsl::internal_ec_point(const PublicKey& generic) const { openssl::Group group { openssl_nid(generic.type) }; openssl::Point point { group }; openssl::BigNumberContext bn_ctx; switch (generic.compression) { case KeyCompression::NoCompression: EC_POINT_set_affine_coordinates(group, point, openssl::BigNumber { generic.x }, openssl::BigNumber {generic.y }, bn_ctx); break; case KeyCompression::Y0: EC_POINT_set_compressed_coordinates(group, point, openssl::BigNumber { generic.x }, 0, bn_ctx); break; case KeyCompression::Y1: EC_POINT_set_compressed_coordinates(group, point, openssl::BigNumber { generic.x }, 1, bn_ctx); break; default: // no-op break; } return point; } namespace openssl { PublicKey derive_public_key(const PrivateKey& private_key) { Key ec_key(openssl_nid(private_key.type)); BigNumber prv(private_key.key); EC_KEY_set_private_key(ec_key, prv); const EC_GROUP* group = EC_KEY_get0_group(ec_key); Point pub(group); BigNumberContext ctx; check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx)); BigNumber x; BigNumber y; EC_POINT_get_affine_coordinates(group, pub, x, y, ctx); PublicKey public_key; public_key.type = private_key.type; public_key.compression = KeyCompression::NoCompression; public_key.x.resize(key_length(private_key.type)); public_key.y.resize(key_length(private_key.type)); BN_bn2binpad(x, public_key.x.data(), public_key.x.size()); BN_bn2binpad(y, public_key.y.data(), public_key.y.size()); return public_key; } } // namespace openssl } // namespace security } // namespace vanetza