Skip to content

Commit

Permalink
security: implement decompression of ecc points
Browse files Browse the repository at this point in the history
  • Loading branch information
glmax authored and riebl committed May 13, 2019
1 parent 37131e7 commit 2a302ab
Show file tree
Hide file tree
Showing 15 changed files with 146 additions and 11 deletions.
4 changes: 3 additions & 1 deletion tools/certify/commands/extract-public-key.cpp
Expand Up @@ -2,6 +2,7 @@
#include <boost/program_options.hpp>
#include <iostream>
#include <stdexcept>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/basic_elements.hpp>
#include <vanetza/security/persistence.hpp>

Expand Down Expand Up @@ -51,10 +52,11 @@ int ExtractPublicKeyCommand::execute()
{
std::cout << "Loading key... ";

std::unique_ptr<Backend> backend = create_backend("CryptoPP");
ecdsa256::PublicKey public_key;
if (certificate_path.length() > 0) {
auto certificate = load_certificate_from_file(certificate_path);
auto certificate_key = get_public_key(certificate);
auto certificate_key = get_public_key(certificate, *backend);

if (!certificate_key) {
std::cerr << "Reading public key from certificate failed." << std::endl;
Expand Down
2 changes: 1 addition & 1 deletion tools/certify/commands/generate-aa.cpp
Expand Up @@ -77,7 +77,7 @@ int GenerateAaCommand::execute()
return 1;
}

subject_key = ecdsa256::create_public_key(boost::get<Uncompressed>(subject_key_etsi_ecdsa.public_key));
subject_key = ecdsa256::create_public_key(crypto_backend.decompress_ecc_point(subject_key_etsi_ecdsa.public_key));
}
std::cout << "OK" << std::endl;

Expand Down
2 changes: 2 additions & 0 deletions vanetza/security/CMakeLists.txt
Expand Up @@ -55,6 +55,8 @@ if(TARGET OpenSSL::Crypto)
if (${OPENSSL_VERSION} VERSION_EQUAL 2.0.0)
# found LibreSSL most likely, should be API compatible to OpenSSL 1.0.1
target_compile_definitions(security PRIVATE "OPENSSL_API_COMPAT=0x10000000L")
elseif (${OPENSSL_VERSION} VERSION_EQUAL 1.1.1)
target_compile_definitions(security PRIVATE "OPENSSL_API_COMPAT=0x10101000L")
elseif (${OPENSSL_VERSION} VERSION_LESS 1.1.0)
target_compile_definitions(security PRIVATE "OPENSSL_API_COMPAT=0x10000000L")
else()
Expand Down
8 changes: 8 additions & 0 deletions vanetza/security/backend.hpp
Expand Up @@ -38,6 +38,14 @@ class Backend
*/
virtual bool verify_data(const ecdsa256::PublicKey& public_key, const ByteBuffer& data, const EcdsaSignature& sig) = 0;

/**
* \brief decompress a possibly compressed elliptic curve point
*
* \param ecc_point elliptic curve point
* \return uncompressed point
*/
virtual Uncompressed decompress_ecc_point(const EccPoint& ecc_point) = 0;

virtual ~Backend() = default;
};

Expand Down
33 changes: 33 additions & 0 deletions vanetza/security/backend_cryptopp.cpp
@@ -1,4 +1,5 @@
#include <vanetza/security/backend_cryptopp.hpp>
#include <vanetza/security/ecc_point_decompression_visitor.hpp>
#include <vanetza/security/ecc_point.hpp>
#include <cryptopp/oids.h>
#include <cassert>
Expand Down Expand Up @@ -57,6 +58,38 @@ bool BackendCryptoPP::verify_data(const PublicKey& public_key, const ByteBuffer&
return verifier.VerifyMessage(msg.data(), msg.size(), sig.data(), sig.size());
}

class CryptoPPEccPointDecompressionVisitor: public EccPointDecompressionVisitor
{
public:
virtual Uncompressed decompress(ByteBuffer x, EccPointType type) override {
// Only with actually compressed points that provide the bit of the y coordinate, we can perform decompression.
if (type != EccPointType::Compressed_Lsb_Y_0 && type != EccPointType::Compressed_Lsb_Y_1) {
throw std::logic_error("Unsupported compression type!");
}

ByteBuffer compressed(x);
// prepend compression type
compressed.insert(compressed.begin(), static_cast<uint8_t>(type));

BackendCryptoPP::Point point;
CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP> group(CryptoPP::ASN1::secp256r1());
CryptoPP::ECP curve = group.GetCurve();
curve.DecodePoint(point, compressed.data(), compressed.size());

Uncompressed p { x };
auto y_byte_count = point.y.ByteCount();
p.y.resize(y_byte_count);
point.y.Encode(p.y.data(), y_byte_count);

return p;
}
};

Uncompressed BackendCryptoPP::decompress_ecc_point(const EccPoint& ecc_point) {
CryptoPPEccPointDecompressionVisitor visitor;
return boost::apply_visitor(visitor, ecc_point);
}

ecdsa256::KeyPair BackendCryptoPP::generate_key_pair()
{
ecdsa256::KeyPair kp;
Expand Down
4 changes: 4 additions & 0 deletions vanetza/security/backend_cryptopp.hpp
Expand Up @@ -19,6 +19,7 @@ class BackendCryptoPP : public Backend
using PublicKey = CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PublicKey;
using Signer = CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::Signer;
using Verifier = CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::Verifier;
using Point = CryptoPP::ECP::Point;

static constexpr auto backend_name = "CryptoPP";

Expand All @@ -30,6 +31,9 @@ class BackendCryptoPP : public Backend
/// \see Backend::verify_data
bool verify_data(const ecdsa256::PublicKey& public_key, const ByteBuffer& data, const EcdsaSignature& sig) override;

/// \see Backend::decompress_ecc_point
Uncompressed decompress_ecc_point(const EccPoint& ecc_point) override;

/**
* \brief generate a private key and the corresponding public key
* \return generated key pair
Expand Down
6 changes: 6 additions & 0 deletions vanetza/security/backend_null.cpp
@@ -1,5 +1,6 @@
#include <vanetza/common/byte_sequence.hpp>
#include <vanetza/security/backend_null.hpp>
#include <vanetza/security/ecc_point_decompression_visitor.hpp>
#include <vanetza/security/public_key.hpp>
#include <vanetza/security/signature.hpp>

Expand All @@ -20,6 +21,11 @@ bool BackendNull::verify_data(const ecdsa256::PublicKey&, const ByteBuffer&, con
return true;
}

Uncompressed BackendNull::decompress_ecc_point(const EccPoint& ecc_point) {
EccPointDecompressionVisitor visitor;
return boost::apply_visitor(visitor, ecc_point);
}

EcdsaSignature BackendNull::fake_signature() const
{
const std::size_t size = field_size(PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
Expand Down
3 changes: 3 additions & 0 deletions vanetza/security/backend_null.hpp
Expand Up @@ -25,6 +25,9 @@ class BackendNull : public Backend
/// \see Backend::verify_data
bool verify_data(const ecdsa256::PublicKey& public_key, const ByteBuffer& data, const EcdsaSignature& sig) override;

/// \see Backend::decompress_ecc_point
Uncompressed decompress_ecc_point(const EccPoint& ecc_point) override;

private:
EcdsaSignature fake_signature() const;
};
Expand Down
37 changes: 37 additions & 0 deletions vanetza/security/backend_openssl.cpp
@@ -1,8 +1,10 @@
#include <vanetza/security/backend_openssl.hpp>
#include <vanetza/security/ecc_point_decompression_visitor.hpp>
#include <vanetza/security/openssl_wrapper.hpp>
#include <vanetza/security/public_key.hpp>
#include <vanetza/security/signature.hpp>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/sha.h>
Expand Down Expand Up @@ -70,6 +72,41 @@ bool BackendOpenSsl::verify_data(const ecdsa256::PublicKey& key, const ByteBuffe
return (ECDSA_do_verify(digest.data(), digest.size(), signature, pub) == 1);
}

class OpenSslEccPointDecompressionVisitor: public EccPointDecompressionVisitor
{
public:
virtual Uncompressed decompress(ByteBuffer x, EccPointType type) override {
// Only with actually compressed points that provide the bit of the y coordinate, we can perform decompression.
if (type != EccPointType::Compressed_Lsb_Y_0 && type != EccPointType::Compressed_Lsb_Y_1) {
throw std::logic_error("Unsupported compression type!");
}

openssl::BigNumberContext ctx;
openssl::BigNumber x_coordinate(x);
const EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_POINT *point = EC_POINT_new(group);
BIGNUM *y_coordinate = BN_new();
#if OPENSSL_API_COMPAT < 0x10101000L
EC_POINT_set_compressed_coordinates_GFp(group, point, x_coordinate, static_cast<unsigned>(type) % 2, ctx);
EC_POINT_get_affine_coordinates_GFp(group, point, nullptr, y_coordinate, ctx);
#else
EC_POINT_set_compressed_coordinates(group, point, x_coordinate, static_cast<unsigned>(type) % 2, ctx);
EC_POINT_get_affine_coordinates(group, point, nullptr, y_coordinate, ctx);
#endif

Uncompressed p { x };
p.y.resize(BN_num_bytes(y_coordinate));
BN_bn2bin(y_coordinate, p.y.data());

return p;
}
};

Uncompressed BackendOpenSsl::decompress_ecc_point(const EccPoint& ecc_point) {
OpenSslEccPointDecompressionVisitor visitor;
return boost::apply_visitor(visitor, ecc_point);
}

std::array<uint8_t, 32> BackendOpenSsl::calculate_digest(const ByteBuffer& data) const
{
static_assert(SHA256_DIGEST_LENGTH == 32, "Unexpected length of SHA256 digest");
Expand Down
3 changes: 3 additions & 0 deletions vanetza/security/backend_openssl.hpp
Expand Up @@ -33,6 +33,9 @@ class BackendOpenSsl : public Backend
/// \see Backend::verify_data
bool verify_data(const ecdsa256::PublicKey& public_key, const ByteBuffer& data, const EcdsaSignature& sig) override;

/// \see Backend::decompress_ecc_point
Uncompressed decompress_ecc_point(const EccPoint& ecc_point) override;

private:
/// calculate SHA256 digest of data buffer
std::array<uint8_t, 32> calculate_digest(const ByteBuffer& data) const;
Expand Down
8 changes: 4 additions & 4 deletions vanetza/security/certificate.cpp
Expand Up @@ -102,24 +102,24 @@ void sort(Certificate& cert)
});
}

boost::optional<Uncompressed> get_uncompressed_public_key(const Certificate& cert)
boost::optional<Uncompressed> get_uncompressed_public_key(const Certificate& cert, Backend& backend)
{
boost::optional<Uncompressed> public_key_coordinates;
for (auto& attribute : cert.subject_attributes) {
if (get_type(attribute) == SubjectAttributeType::Verification_Key) {
const VerificationKey& verification_key = boost::get<VerificationKey>(attribute);
const EccPoint& ecc_point = boost::get<ecdsa_nistp256_with_sha256>(verification_key.key).public_key;
public_key_coordinates = boost::get<Uncompressed>(ecc_point);
public_key_coordinates = backend.decompress_ecc_point(ecc_point);
break;
}
}

return public_key_coordinates;
}

boost::optional<ecdsa256::PublicKey> get_public_key(const Certificate& cert)
boost::optional<ecdsa256::PublicKey> get_public_key(const Certificate& cert, Backend& backend)
{
auto unc = get_uncompressed_public_key(cert);
auto unc = get_uncompressed_public_key(cert, backend);
boost::optional<ecdsa256::PublicKey> result;
ecdsa256::PublicKey pub;
if (unc && unc->x.size() == pub.x.size() && unc->y.size() == pub.y.size()) {
Expand Down
7 changes: 5 additions & 2 deletions vanetza/security/certificate.hpp
Expand Up @@ -3,6 +3,7 @@

#include <vanetza/common/byte_buffer.hpp>
#include <vanetza/common/its_aid.hpp>
#include <vanetza/security/backend.hpp>
#include <vanetza/security/basic_elements.hpp>
#include <vanetza/security/ecc_point.hpp>
#include <vanetza/security/ecdsa256.hpp>
Expand Down Expand Up @@ -183,16 +184,18 @@ void sort(Certificate& certificate);
/**
* \brief Extract public key from certificate
* \param cert Certificate
* \param backend Backend
* \return Uncompressed public key (if available)
*/
boost::optional<Uncompressed> get_uncompressed_public_key(const Certificate&);
boost::optional<Uncompressed> get_uncompressed_public_key(const Certificate&, Backend& backend);

/**
* \brief Extract public ECDSA256 key from certificate
* \param cert Certificate
* \param backend Backend
* \return public key (if available)
*/
boost::optional<ecdsa256::PublicKey> get_public_key(const Certificate&);
boost::optional<ecdsa256::PublicKey> get_public_key(const Certificate&, Backend& backend);

/**
* Calculate hash id of certificate
Expand Down
4 changes: 2 additions & 2 deletions vanetza/security/default_certificate_validator.cpp
Expand Up @@ -207,7 +207,7 @@ CertificateValidity DefaultCertificateValidator::check_certificate(const Certifi
// authorization tickets may only be signed by authorization authorities
if (subject_type == SubjectType::Authorization_Ticket) {
for (auto& possible_signer : m_cert_cache.lookup(signer_hash, SubjectType::Authorization_Authority)) {
auto verification_key = get_public_key(possible_signer);
auto verification_key = get_public_key(possible_signer, m_crypto_backend);
if (!verification_key) {
continue;
}
Expand All @@ -226,7 +226,7 @@ CertificateValidity DefaultCertificateValidator::check_certificate(const Certifi
// Note: There's no clear specification about this, but there's a test for it in 5.2.7.12.4 of TS 103 096-2 V1.3.1
if (subject_type == SubjectType::Authorization_Authority) {
for (auto& possible_signer : m_trust_store.lookup(signer_hash)) {
auto verification_key = get_public_key(possible_signer);
auto verification_key = get_public_key(possible_signer, m_crypto_backend);
if (!verification_key) {
continue;
}
Expand Down
34 changes: 34 additions & 0 deletions vanetza/security/ecc_point_decompression_visitor.hpp
@@ -0,0 +1,34 @@
#include <boost/variant.hpp>
#include <vanetza/security/ecc_point.hpp>

namespace vanetza
{
namespace security
{

struct EccPointDecompressionVisitor : public boost::static_visitor<Uncompressed> {
Uncompressed operator()(X_Coordinate_Only p) {
// Without the additional bit of the y coordinate, we cannot restore y.
Uncompressed u {p.x};
return u;
}

Uncompressed operator()(Compressed_Lsb_Y_0 p) {
return decompress(p.x, EccPointType::Compressed_Lsb_Y_0);
}

Uncompressed operator()(Compressed_Lsb_Y_1 p) {
return decompress(p.x, EccPointType::Compressed_Lsb_Y_1);
}

Uncompressed operator()(Uncompressed p) {
return Uncompressed(p);
}

virtual Uncompressed decompress(ByteBuffer x, EccPointType type) {
throw std::logic_error("Decompression of EccPoints not supported!");
}
};

} // ns security
} // ns vanetza
2 changes: 1 addition & 1 deletion vanetza/security/verify_service.cpp
Expand Up @@ -286,7 +286,7 @@ VerifyService straight_verify_service(Runtime& rt, CertificateProvider& cert_pro
return confirm;
}

boost::optional<ecdsa256::PublicKey> public_key = get_public_key(cert);
boost::optional<ecdsa256::PublicKey> public_key = get_public_key(cert, backend);

// public key could not be extracted
if (!public_key) {
Expand Down

0 comments on commit 2a302ab

Please sign in to comment.