From 659ef2f0a8fc55918710c872847575c1cb45a129 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 18 Jul 2024 07:53:53 +0200 Subject: [PATCH] Improve code formatting --- src/core/system_parameters/gnss_crypto.cc | 1083 ++++++++--------- src/core/system_parameters/gnss_crypto.h | 54 +- .../osnma/gnss_crypto_test.cc | 365 ++++-- 3 files changed, 798 insertions(+), 704 deletions(-) diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc index ce6231617..7b1d837e0 100644 --- a/src/core/system_parameters/gnss_crypto.cc +++ b/src/core/system_parameters/gnss_crypto.cc @@ -133,133 +133,389 @@ bool Gnss_Crypto::have_public_key() const } -std::vector Gnss_Crypto::convert_from_hex_str(const std::string& input) const +bool Gnss_Crypto::verify_signature(const std::vector& message, const std::vector& signature) const { - std::vector result; - - // Iterate over the input string in pairs - for (size_t i = 0; i < input.length(); i += 2) + std::vector digest = this->computeSHA256(message); + if (!have_public_key()) { - // Extract two hexadecimal characters from the input string - std::string hexByte = input.substr(i, 2); - - // Convert the hexadecimal string to an integer value - auto value = static_cast(std::stoul(hexByte, nullptr, 16)); - - // Append the value to the result vector - result.push_back(value); + std::cerr << "Galileo OSNMA KROOT verification error: Public key is not available" << std::endl; + return false; + } + bool success = false; +#if USE_GNUTLS_FALLBACK +#if HAVE_GNUTLS_SIGN_ECDSA_SHA256 + // Convert signature to DER format + std::vector der_sig; + if (!convert_raw_to_der_ecdsa(signature, der_sig)) + { + std::cerr << "Failed to convert raw ECDSA signature to DER format" << std::endl; + return false; } - return result; + // Prepare the digest datum + gnutls_datum_t digest_data = {const_cast(digest.data()), static_cast(digest.size())}; + gnutls_datum_t der_sig_data = {der_sig.data(), static_cast(der_sig.size())}; + + // Verify the DER-encoded signature + int ret = gnutls_pubkey_verify_hash2(d_PublicKey, GNUTLS_SIGN_ECDSA_SHA256, 0, &digest_data, &der_sig_data); + success = (ret >= 0); + if (success) + { + LOG(INFO) << "GnuTLS: OSNMA signature authenticated successfully"; + } + else + { + std::cerr << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret) << std::endl; + LOG(WARNING) << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret); + } +#endif +#else // OpenSSL +#if USE_OPENSSL_3 + EVP_PKEY_CTX* ctx; + ctx = EVP_PKEY_CTX_new(d_PublicKey, nullptr); + bool do_operation = true; + + if (!ctx) + { + do_operation = false; + } + // convert raw signature into DER format + size_t half_size = signature.size() / 2; + std::vector raw_r(signature.begin(), signature.begin() + half_size); + std::vector raw_s(signature.begin() + half_size, signature.end()); + + // Convert raw R and S to BIGNUMs + BIGNUM* r = BN_bin2bn(raw_r.data(), raw_r.size(), nullptr); + BIGNUM* s = BN_bin2bn(raw_s.data(), raw_s.size(), nullptr); + + ECDSA_SIG* sig = ECDSA_SIG_new(); + if (r == nullptr || s == nullptr || sig == nullptr) + { + std::cerr << "Failed to allocate memory for BIGNUMs or ECDSA_SIG" << std::endl; + return false; + } + + if (ECDSA_SIG_set0(sig, r, s) != 1) + { + std::cerr << "Failed to set R and S values in ECDSA_SIG" << std::endl; + ECDSA_SIG_free(sig); // Free the ECDSA_SIG struct as it is no longer needed + return false; + } + + std::vector derSignature; + unsigned char* derSig = nullptr; + int derSigLength = i2d_ECDSA_SIG(sig, &derSig); + + if (derSigLength <= 0) + { + std::cerr << "Failed to convert ECDSA_SIG to DER format" << std::endl; + return false; + } + + derSignature.assign(derSig, derSig + derSigLength); + + if (EVP_PKEY_verify_init(ctx) <= 0) + { + do_operation = false; + } + if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0) + { + do_operation = false; + } + int verification = 0; + if (do_operation) + { + verification = EVP_PKEY_verify(ctx, derSignature.data(), derSignature.size(), digest.data(), digest.size()); + } + EVP_PKEY_CTX_free(ctx); + OPENSSL_free(derSig); + ECDSA_SIG_free(sig); + if (verification == 1) + { + success = true; + LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; + } + else + { + unsigned long errCode = ERR_get_error(); + char* err = ERR_error_string(errCode, nullptr); + std::cerr << "OpenSSL: OSNMA message authentication failed: " << err << std::endl; + LOG(WARNING) << "OpenSSL: OSNMA message authentication failed: " << err; + } +#else // OpenSSL 1.x + std::vector der_sig; + if (!convert_raw_to_der_ecdsa(signature, der_sig)) + { + std::cerr << "Failed to convert raw ECDSA signature to DER format" << std::endl; + return false; + } + int verification = ECDSA_verify(0, digest.data(), SHA256_DIGEST_LENGTH, der_sig.data(), static_cast(der_sig.size()), d_PublicKey); + if (verification == 1) + { + success = true; + LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; + } + else if (verification == 0) + { + std::cerr << "OpenSSL: invalid signature found when verifying message" << std::endl; + LOG(WARNING) << "OpenSSL: invalid signature found when verifying message"; + } + else + { + std::cerr << "OpenSSL: OSNMA message authentication failed" << std::endl; + LOG(WARNING) << "OpenSSL: OSNMA message authentication failed"; + } +#endif +#endif + return success; } -void Gnss_Crypto::read_merkle_xml(const std::string& merkleFilePath) +bool Gnss_Crypto::verify_signature_ecdsa_p521(const std::vector& message, const std::vector& signature) const { - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(merkleFilePath.c_str()); - if (!result) + if (!have_public_key()) { - // XML file not found - // If it was not the default, maybe it is a configuration error, warn user - if (merkleFilePath != MERKLEFILE_DEFAULT) - { - LOG(INFO) << "File " << merkleFilePath << " not found"; - } - // fill default values - d_x_4_0 = convert_from_hex_str("832E15EDE55655EAC6E399A539477B7C034CCE24C3C93FFC904ACD9BF842F04E"); - d_x_3_1 = convert_from_hex_str("84DE3669E6DA551292979E5B8D045787FA967C57CC23638A30237614EDD9171A"); - d_x_2_1 = convert_from_hex_str("DE73D209E4C5BCDC34CD117F2FE40FD08B110009997AD2B3291D3A2CF29943F9"); - d_x_1_1 = convert_from_hex_str("6AAFDE28017BF0744D42819CE40E3A0CDA1ECA3F7A4EA67E134E7AA714C1E843"); - d_x_0_1 = convert_from_hex_str("941BD34EA7DF668B6FC5BE75C1D93464D109BC615CB52C8124847FAFB09CBB2B"); - return; + LOG(ERROR) << "Galileo OSNMA KROOT verification error: Public key is not available"; + return false; } - try + + if (signature.size() != 132) { - pugi::xml_node root = doc.child("signalData"); - pugi::xml_node header = root.child("header"); - pugi::xml_node body = root.child("body"); - - // Accessing data from the header - pugi::xml_node galHeader = header.child("GAL-header"); - pugi::xml_node source = galHeader.child("source").child("GAL-EXT-GOC-SC-GLAd"); - pugi::xml_node destination = galHeader.child("destination").child("GAL-EXT-GOC-SC-GLAd"); - std::string issueDate = galHeader.child("issueDate").text().get(); - std::string signalVersion = galHeader.child("signalVersion").text().get(); - std::string dataVersion = galHeader.child("dataVersion").text().get(); - - LOG(INFO) << "OSNMA Merkletree - Source: " << source.child_value("mission") << " - " << source.child_value("segment") << " - " << source.child_value("element"); - LOG(INFO) << "OSNMA Merkletree - Destination: " << destination.child_value("mission") << " - " << destination.child_value("segment") << " - " << destination.child_value("element"); - LOG(INFO) << "OSNMA Merkletree - Issue Date: " << issueDate; - LOG(INFO) << "OSNMA Merkletree - Signal Version: " << signalVersion; - LOG(INFO) << "OSNMA Merkletree - Data Version: " << dataVersion; - - // Accessing data from the body - pugi::xml_node merkleTree = body.child("MerkleTree"); - - int n = std::stoi(merkleTree.child_value("N")); - std::string hashFunction = merkleTree.child_value("HashFunction"); - - LOG(INFO) << "OSNMA Merkletree - N: " << n; - LOG(INFO) << "OSNMA Merkletree - Hash Function: " << hashFunction; - - for (pugi::xml_node publicKey : merkleTree.children("PublicKey")) - { - int i = std::stoi(publicKey.child_value("i")); - std::string pkid = publicKey.child_value("PKID"); - int lengthInBits = std::stoi(publicKey.child_value("lengthInBits")); - std::string point = publicKey.child_value("point"); - std::string pkType = publicKey.child_value("PKType"); - - LOG(INFO) << "OSNMA Merkletree - Public Key: " << i; - LOG(INFO) << "OSNMA Merkletree - PKID: " << pkid; - LOG(INFO) << "OSNMA Merkletree - Length in Bits: " << lengthInBits; - LOG(INFO) << "OSNMA Merkletree - Point: " << point; - LOG(INFO) << "OSNMA Merkletree - PK Type: " << pkType; - } - for (pugi::xml_node treeNode : merkleTree.children("TreeNode")) - { - int j = std::stoi(treeNode.child_value("j")); - int i = std::stoi(treeNode.child_value("i")); - int lengthInBits = std::stoi(treeNode.child_value("lengthInBits")); - LOG(INFO) << "OSNMA Merkletree - Node length (bits): " << lengthInBits; - std::string x_ji = treeNode.child_value("x_ji"); - LOG(INFO) << "OSNMA Merkletree - Size string (bytes): " << x_ji.size(); - LOG(INFO) << "OSNMA Merkletree - m_" << j << "_" << i << " = " << x_ji; - if (j == 4 && i == 0) - { - d_x_4_0 = convert_from_hex_str(x_ji); - } - if (j == 3 && i == 1) - { - d_x_3_1 = convert_from_hex_str(x_ji); - } - if (j == 2 && i == 1) - { - d_x_2_1 = convert_from_hex_str(x_ji); - } - if (j == 1 && i == 1) - { - d_x_1_1 = convert_from_hex_str(x_ji); - } - if (j == 0 && i == 1) - { - d_x_0_1 = convert_from_hex_str(x_ji); - } - } + LOG(ERROR) << "Invalid signature length for P-521. Expected 132 bytes, got " << signature.size(); + return false; } - catch (const std::exception& e) + + std::vector der_sig; + if (!convert_raw_to_der_ecdsa(signature, der_sig)) { - std::cerr << "Exception raised reading the " << merkleFilePath << " file: " << e.what() << '\n'; - d_x_4_0 = convert_from_hex_str("832E15EDE55655EAC6E399A539477B7C034CCE24C3C93FFC904ACD9BF842F04E"); - d_x_3_1 = convert_from_hex_str("84DE3669E6DA551292979E5B8D045787FA967C57CC23638A30237614EDD9171A"); - d_x_2_1 = convert_from_hex_str("DE73D209E4C5BCDC34CD117F2FE40FD08B110009997AD2B3291D3A2CF29943F9"); - d_x_1_1 = convert_from_hex_str("6AAFDE28017BF0744D42819CE40E3A0CDA1ECA3F7A4EA67E134E7AA714C1E843"); - d_x_0_1 = convert_from_hex_str("941BD34EA7DF668B6FC5BE75C1D93464D109BC615CB52C8124847FAFB09CBB2B"); - return; + LOG(ERROR) << "Failed to convert raw ECDSA signature to DER format"; + return false; } - std::cout << "OSNMA Merkle Tree successfully read from file " << merkleFilePath << std::endl; - LOG(INFO) << "OSNMA Merkle Tree successfully read from file " << merkleFilePath; + bool success = false; +#if USE_GNUTLS_FALLBACK + std::vector digest(64); + gnutls_hash_hd_t hash; + int ret = gnutls_hash_init(&hash, GNUTLS_DIG_SHA512); + if (ret != GNUTLS_E_SUCCESS) + { + LOG(ERROR) << "GnuTLS: gnutls_hash_init failed: " << gnutls_strerror(ret); + return false; + } + + gnutls_hash(hash, message.data(), message.size()); + gnutls_hash_deinit(hash, digest.data()); + + gnutls_datum_t digest_data = {digest.data(), static_cast(digest.size())}; + gnutls_datum_t signature_data = {der_sig.data(), static_cast(der_sig.size())}; + + // Verify the ECDSA signature + ret = gnutls_pubkey_verify_data2(d_PublicKey, GNUTLS_SIGN_ECDSA_SHA512, 0, &digest_data, &signature_data); + + if (ret >= 0) + { + LOG(INFO) << "GnuTLS: OSNMA signature authenticated successfully"; + success = true; + } + else + { + LOG(WARNING) << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret); + } +#else // OpenSSL + // Compute SHA-512 hash of the message + std::vector digest(SHA512_DIGEST_LENGTH); + if (!EVP_Digest(message.data(), message.size(), digest.data(), nullptr, EVP_sha512(), nullptr)) + { + LOG(INFO) << "OpenSSL: EVP_Digest failed"; + return false; + } +#if USE_OPENSSL_3 + // Verify the signature + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new(d_PublicKey, nullptr); + if (pctx == nullptr) + { + LOG(INFO) << "OpenSSL: EVP_PKEY_CTX_new failed"; + return false; + } + + if (EVP_PKEY_verify_init(pctx) <= 0) + { + LOG(INFO) << "OpenSSL: EVP_PKEY_verify_init failed"; + EVP_PKEY_CTX_free(pctx); + return false; + } + + if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha512()) <= 0) + { + LOG(INFO) << "OpenSSL: EVP_PKEY_CTX_set_signature_md failed"; + EVP_PKEY_CTX_free(pctx); + return false; + } + + int verification = EVP_PKEY_verify(pctx, der_sig.data(), der_sig.size(), digest.data(), digest.size()); + EVP_PKEY_CTX_free(pctx); + + if (verification == 1) + { + LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; + success = true; + } + else if (verification == 0) + { + LOG(INFO) << "OpenSSL: invalid signature found when verifying message"; + } + else + { + LOG(INFO) << "OpenSSL: OSNMA message authentication failed"; + } +#else // OpenSSL 1.x + const unsigned char* sig_ptr = der_sig.data(); + ECDSA_SIG* ecdsa_sig = d2i_ECDSA_SIG(nullptr, &sig_ptr, der_sig.size()); + if (ecdsa_sig == nullptr) + { + LOG(INFO) << "OpenSSL: d2i_ECDSA_SIG failed"; + return false; + } + int verification = ECDSA_do_verify(digest.data(), digest.size(), ecdsa_sig, d_PublicKey); + ECDSA_SIG_free(ecdsa_sig); + if (verification == 1) + { + LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; + success = true; + } + else if (verification == 0) + { + LOG(INFO) << "OpenSSL: invalid signature found when verifying message"; + } + else + { + LOG(INFO) << "OpenSSL: OSNMA message authentication failed"; + } +#endif +#endif + return success; +} + + +bool Gnss_Crypto::store_public_key(const std::string& pubKeyFilePath) const +{ + if (!have_public_key()) + { + return false; + } + std::ofstream pubKeyFile(pubKeyFilePath, std::ios::binary); + if (!pubKeyFile.is_open()) + { + LOG(INFO) << "Unable to open file: " << pubKeyFilePath; + return false; + } +#if USE_GNUTLS_FALLBACK + gnutls_datum_t pem_data; +#if HAVE_GNUTLS_PUBKEY_EXPORT2 + int ret = gnutls_pubkey_export2(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data); +#else + size_t output_stata_size; + int ret = gnutls_pubkey_export(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data, &output_stata_size); +#endif + if (ret != GNUTLS_E_SUCCESS) + { + LOG(INFO) << "GnuTLS: Failed to export public key: " << gnutls_strerror(ret); + return false; + } + + pubKeyFile.write((const char*)pem_data.data, pem_data.size); + pubKeyFile.close(); + gnutls_free(pem_data.data); +#else // OpenSSL + BIO* bio = BIO_new(BIO_s_mem()); + if (!bio) + { + LOG(INFO) << "OpenSSL: Failed to create BIO"; + return false; + } +#if USE_OPENSSL_3 + if (!PEM_write_bio_PUBKEY(bio, d_PublicKey)) +#else // OpenSSL 1.x + if (!PEM_write_bio_EC_PUBKEY(bio, d_PublicKey)) +#endif + { + LOG(INFO) << "OpenSSL: Failed to write public key to BIO"; + BIO_free(bio); + return false; + } + + char* bio_data; + auto bio_len = BIO_get_mem_data(bio, &bio_data); + if (bio_len <= 0) + { + LOG(INFO) << "OpenSSL: Failed to get BIO data"; + BIO_free(bio); + return false; + } + + pubKeyFile.write(bio_data, bio_len); + pubKeyFile.close(); + BIO_free(bio); +#endif + return true; +} + + +std::vector Gnss_Crypto::getPublicKey() const +{ + if (!have_public_key()) + { + return {}; + } +#if USE_GNUTLS_FALLBACK + gnutls_datum_t pem_data = {nullptr, 0}; + + int ret = gnutls_pubkey_export2(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data); + if (ret != GNUTLS_E_SUCCESS) + { + LOG(INFO) << "GnuTLS: Failed to export public key to PEM format."; + return {}; + } + std::vector output(pem_data.data, pem_data.data + pem_data.size); + + // Free the allocated memory by gnutls_pubkey_export2 + gnutls_free(pem_data.data); +#else // OpenSSL + // Create a BIO for the memory buffer + BIO* mem = BIO_new(BIO_s_mem()); + if (!mem) + { + LOG(INFO) << "OpenSSL: Failed to create BIO."; + return {}; + } +#if USE_OPENSSL_3 + if (!PEM_write_bio_PUBKEY(mem, d_PublicKey)) +#else // OpenSSL 1.x + if (!PEM_write_bio_EC_PUBKEY(mem, d_PublicKey)) +#endif + { + BIO_free(mem); + LOG(INFO) << "OpenSSL: Failed to write public key to PEM format."; + return {}; + } + + // Get the length of the data in the BIO + BUF_MEM* mem_ptr; + BIO_get_mem_ptr(mem, &mem_ptr); + + // Copy the data from the BIO to a std::vector + std::vector output(mem_ptr->length); + memcpy(output.data(), mem_ptr->data, mem_ptr->length); + + // Clean up the BIO + BIO_free(mem); +#endif + return output; +} + + +std::vector Gnss_Crypto::getMerkleRoot() const +{ + return d_x_4_0; } @@ -573,6 +829,152 @@ std::vector Gnss_Crypto::computeCMAC_AES(const std::vector& ke } +void Gnss_Crypto::set_public_key(const std::vector& publicKey) +{ +#if USE_GNUTLS_FALLBACK + gnutls_pubkey_t pubkey{}; + gnutls_datum_t pemDatum = {const_cast(publicKey.data()), static_cast(publicKey.size())}; + gnutls_pubkey_init(&pubkey); + int ret = gnutls_pubkey_import(pubkey, &pemDatum, GNUTLS_X509_FMT_PEM); + if (ret != GNUTLS_E_SUCCESS) + { + gnutls_pubkey_deinit(pubkey); + std::cerr << "GnuTLS: error setting the public key" << std::endl; + std::cerr << "GnuTLS error: " << gnutls_strerror(ret) << std::endl; + LOG(WARNING) << "GnuTLS: error setting the OSNMA public key: " << gnutls_strerror(ret); + return; + } + pubkey_copy(pubkey, &d_PublicKey); + gnutls_pubkey_deinit(pubkey); +#else // OpenSSL + BIO* bio = nullptr; + EVP_PKEY* pkey = nullptr; + bio = BIO_new_mem_buf(const_cast(publicKey.data()), publicKey.size()); + if (!bio) + { + LOG(INFO) << "OpenSSL: Failed to create BIO for key."; + return; + } + + pkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr); + BIO_free(bio); + + if (!pkey) + { + std::cerr << "OpenSSL: error setting the OSNMA public key." << std::endl; + LOG(INFO) << "OpenSSL: error setting the OSNMA public key."; + return; + } +#if USE_OPENSSL_3 + if (!pubkey_copy(pkey, &d_PublicKey)) + { + return; + } +#else + EC_KEY* ec_pkey = EVP_PKEY_get1_EC_KEY(pkey); + if (!pubkey_copy(ec_pkey, &d_PublicKey)) + { + return; + } + EC_KEY_free(ec_pkey); +#endif // OpenSSL 1.x + EVP_PKEY_free(pkey); +#endif + LOG(INFO) << "OSNMA Public Key successfully set up."; +} + + +void Gnss_Crypto::setMerkleRoot(const std::vector& v) +{ + d_x_4_0 = v; +} + + +void Gnss_Crypto::read_merkle_xml(const std::string& merkleFilePath) +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(merkleFilePath.c_str()); + if (!result) + { + // XML file not found + // If it was not the default, maybe it is a configuration error, warn user + if (merkleFilePath != MERKLEFILE_DEFAULT && !merkleFilePath.empty()) + { + LOG(INFO) << "File " << merkleFilePath << " not found"; + } + // fill default values + d_x_4_0 = convert_from_hex_str("832E15EDE55655EAC6E399A539477B7C034CCE24C3C93FFC904ACD9BF842F04E"); + return; + } + try + { + pugi::xml_node root = doc.child("signalData"); + pugi::xml_node header = root.child("header"); + pugi::xml_node body = root.child("body"); + + // Accessing data from the header + pugi::xml_node galHeader = header.child("GAL-header"); + pugi::xml_node source = galHeader.child("source").child("GAL-EXT-GOC-SC-GLAd"); + pugi::xml_node destination = galHeader.child("destination").child("GAL-EXT-GOC-SC-GLAd"); + std::string issueDate = galHeader.child("issueDate").text().get(); + std::string signalVersion = galHeader.child("signalVersion").text().get(); + std::string dataVersion = galHeader.child("dataVersion").text().get(); + + LOG(INFO) << "OSNMA Merkletree - Source: " << source.child_value("mission") << " - " << source.child_value("segment") << " - " << source.child_value("element"); + LOG(INFO) << "OSNMA Merkletree - Destination: " << destination.child_value("mission") << " - " << destination.child_value("segment") << " - " << destination.child_value("element"); + LOG(INFO) << "OSNMA Merkletree - Issue Date: " << issueDate; + LOG(INFO) << "OSNMA Merkletree - Signal Version: " << signalVersion; + LOG(INFO) << "OSNMA Merkletree - Data Version: " << dataVersion; + + // Accessing data from the body + pugi::xml_node merkleTree = body.child("MerkleTree"); + + int n = std::stoi(merkleTree.child_value("N")); + std::string hashFunction = merkleTree.child_value("HashFunction"); + + LOG(INFO) << "OSNMA Merkletree - N: " << n; + LOG(INFO) << "OSNMA Merkletree - Hash Function: " << hashFunction; + + for (pugi::xml_node publicKey : merkleTree.children("PublicKey")) + { + int i = std::stoi(publicKey.child_value("i")); + std::string pkid = publicKey.child_value("PKID"); + int lengthInBits = std::stoi(publicKey.child_value("lengthInBits")); + std::string point = publicKey.child_value("point"); + std::string pkType = publicKey.child_value("PKType"); + + LOG(INFO) << "OSNMA Merkletree - Public Key: " << i; + LOG(INFO) << "OSNMA Merkletree - PKID: " << pkid; + LOG(INFO) << "OSNMA Merkletree - Length in Bits: " << lengthInBits; + LOG(INFO) << "OSNMA Merkletree - Point: " << point; + LOG(INFO) << "OSNMA Merkletree - PK Type: " << pkType; + } + for (pugi::xml_node treeNode : merkleTree.children("TreeNode")) + { + int j = std::stoi(treeNode.child_value("j")); + int i = std::stoi(treeNode.child_value("i")); + int lengthInBits = std::stoi(treeNode.child_value("lengthInBits")); + LOG(INFO) << "OSNMA Merkletree - Node length (bits): " << lengthInBits; + std::string x_ji = treeNode.child_value("x_ji"); + LOG(INFO) << "OSNMA Merkletree - Size string (bytes): " << x_ji.size(); + LOG(INFO) << "OSNMA Merkletree - m_" << j << "_" << i << " = " << x_ji; + if (j == 4 && i == 0) + { + d_x_4_0 = convert_from_hex_str(x_ji); + } + } + } + catch (const std::exception& e) + { + std::cerr << "Exception raised reading the " << merkleFilePath << " file: " << e.what() << '\n'; + d_x_4_0 = convert_from_hex_str("832E15EDE55655EAC6E399A539477B7C034CCE24C3C93FFC904ACD9BF842F04E"); + return; + } + std::cout << "OSNMA Merkle Tree successfully read from file " << merkleFilePath << std::endl; + LOG(INFO) << "OSNMA Merkle Tree successfully read from file " << merkleFilePath; +} + + void Gnss_Crypto::readPublicKeyFromPEM(const std::string& pemFilePath) { // Open the .pem file @@ -728,317 +1130,6 @@ bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath) } -bool Gnss_Crypto::verify_signature(const std::vector& message, const std::vector& signature) const -{ - std::vector digest = this->computeSHA256(message); - if (!have_public_key()) - { - std::cerr << "Galileo OSNMA KROOT verification error: Public key is not available" << std::endl; - return false; - } - bool success = false; -#if USE_GNUTLS_FALLBACK -#if HAVE_GNUTLS_SIGN_ECDSA_SHA256 - // Convert signature to DER format - std::vector der_sig; - if (!convert_raw_to_der_ecdsa(signature, der_sig)) - { - std::cerr << "Failed to convert raw ECDSA signature to DER format" << std::endl; - return false; - } - - // Prepare the digest datum - gnutls_datum_t digest_data = {const_cast(digest.data()), static_cast(digest.size())}; - gnutls_datum_t der_sig_data = {der_sig.data(), static_cast(der_sig.size())}; - - // Verify the DER-encoded signature - int ret = gnutls_pubkey_verify_hash2(d_PublicKey, GNUTLS_SIGN_ECDSA_SHA256, 0, &digest_data, &der_sig_data); - success = (ret >= 0); - if (success) - { - LOG(INFO) << "GnuTLS: OSNMA signature authenticated successfully"; - } - else - { - std::cerr << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret) << std::endl; - LOG(WARNING) << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret); - } -#endif -#else // OpenSSL -#if USE_OPENSSL_3 - EVP_PKEY_CTX* ctx; - ctx = EVP_PKEY_CTX_new(d_PublicKey, nullptr); - bool do_operation = true; - - if (!ctx) - { - do_operation = false; - } - // convert raw signature into DER format - size_t half_size = signature.size() / 2; - std::vector raw_r(signature.begin(), signature.begin() + half_size); - std::vector raw_s(signature.begin() + half_size, signature.end()); - - // Convert raw R and S to BIGNUMs - BIGNUM* r = BN_bin2bn(raw_r.data(), raw_r.size(), nullptr); - BIGNUM* s = BN_bin2bn(raw_s.data(), raw_s.size(), nullptr); - - ECDSA_SIG* sig = ECDSA_SIG_new(); - if (r == nullptr || s == nullptr || sig == nullptr) - { - std::cerr << "Failed to allocate memory for BIGNUMs or ECDSA_SIG" << std::endl; - return false; - } - - if (ECDSA_SIG_set0(sig, r, s) != 1) - { - std::cerr << "Failed to set R and S values in ECDSA_SIG" << std::endl; - ECDSA_SIG_free(sig); // Free the ECDSA_SIG struct as it is no longer needed - return false; - } - - std::vector derSignature; - unsigned char* derSig = nullptr; - int derSigLength = i2d_ECDSA_SIG(sig, &derSig); - - if (derSigLength <= 0) - { - std::cerr << "Failed to convert ECDSA_SIG to DER format" << std::endl; - return false; - } - - derSignature.assign(derSig, derSig + derSigLength); - - if (EVP_PKEY_verify_init(ctx) <= 0) - { - do_operation = false; - } - if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0) - { - do_operation = false; - } - int verification = 0; - if (do_operation) - { - verification = EVP_PKEY_verify(ctx, derSignature.data(), derSignature.size(), digest.data(), digest.size()); - } - EVP_PKEY_CTX_free(ctx); - OPENSSL_free(derSig); - ECDSA_SIG_free(sig); - if (verification == 1) - { - success = true; - LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; - } - else - { - unsigned long errCode = ERR_get_error(); - char* err = ERR_error_string(errCode, nullptr); - std::cerr << "OpenSSL: OSNMA message authentication failed: " << err << std::endl; - LOG(WARNING) << "OpenSSL: OSNMA message authentication failed: " << err; - } -#else // OpenSSL 1.x - std::vector der_sig; - if (!convert_raw_to_der_ecdsa(signature, der_sig)) - { - std::cerr << "Failed to convert raw ECDSA signature to DER format" << std::endl; - return false; - } - int verification = ECDSA_verify(0, digest.data(), SHA256_DIGEST_LENGTH, der_sig.data(), static_cast(der_sig.size()), d_PublicKey); - if (verification == 1) - { - success = true; - LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; - } - else if (verification == 0) - { - std::cerr << "OpenSSL: invalid signature found when verifying message" << std::endl; - LOG(WARNING) << "OpenSSL: invalid signature found when verifying message"; - } - else - { - std::cerr << "OpenSSL: OSNMA message authentication failed" << std::endl; - LOG(WARNING) << "OpenSSL: OSNMA message authentication failed"; - } -#endif -#endif - return success; -} - - -bool Gnss_Crypto::verify_signature_p521(const std::vector& message, const std::vector& signature) const -{ - if (!have_public_key()) - { - std::cerr << "Galileo OSNMA KROOT verification error: Public key is not available" << std::endl; - return false; - } - bool success = false; - - // Convert signature to DER format - std::vector der_sig; - if (!convert_raw_to_der_ecdsa(signature, der_sig)) - { - LOG(INFO) << "Failed to convert raw ECDSA signature to DER format"; - return false; - } -#if USE_GNUTLS_FALLBACK - // Compute SHA-512 hash of the message - std::vector digest(64); - gnutls_hash_hd_t hash; - if (gnutls_hash_init(&hash, GNUTLS_DIG_SHA512) != GNUTLS_E_SUCCESS) - { - LOG(INFO) << "OpenSSL: gnutls_hash_init failed"; - return false; - } - gnutls_hash(hash, message.data(), message.size()); - gnutls_hash_deinit(hash, digest.data()); - - gnutls_datum_t digest_data = {const_cast(digest.data()), static_cast(digest.size())}; - gnutls_datum_t signature_data = {const_cast(der_sig.data()), static_cast(der_sig.size())}; - - // Verify the ECDSA signature - int ret = gnutls_pubkey_verify_data2(d_PublicKey, GNUTLS_SIGN_ECDSA_SHA512, 0, &digest_data, &signature_data); - success = (ret >= 0); - if (success) - { - LOG(INFO) << "GnuTLS: OSNMA signature authenticated successfully"; - } - else - { - std::cerr << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret) << std::endl; - LOG(WARNING) << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret); - } -#else // OpenSSL - // Compute SHA-512 hash of the message - std::vector digest(SHA512_DIGEST_LENGTH); - if (!EVP_Digest(message.data(), message.size(), digest.data(), nullptr, EVP_sha512(), nullptr)) - { - LOG(INFO) << "OpenSSL: EVP_Digest failed"; - return false; - } -#if USE_OPENSSL_3 - // Verify the signature - EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new(d_PublicKey, nullptr); - if (pctx == nullptr) - { - LOG(INFO) << "OpenSSL: EVP_PKEY_CTX_new failed"; - return false; - } - - if (EVP_PKEY_verify_init(pctx) <= 0) - { - LOG(INFO) << "OpenSSL: EVP_PKEY_verify_init failed"; - EVP_PKEY_CTX_free(pctx); - return false; - } - - if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha512()) <= 0) - { - LOG(INFO) << "OpenSSL: EVP_PKEY_CTX_set_signature_md failed"; - EVP_PKEY_CTX_free(pctx); - return false; - } - - int verification = EVP_PKEY_verify(pctx, der_sig.data(), der_sig.size(), digest.data(), digest.size()); - EVP_PKEY_CTX_free(pctx); - - if (verification == 1) - { - LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; - success = true; - } - else if (verification == 0) - { - LOG(INFO) << "OpenSSL: invalid signature found when verifying message"; - } - else - { - LOG(INFO) << "OpenSSL: OSNMA message authentication failed"; - } -#else // OpenSSL 1.x - const unsigned char* sig_ptr = der_sig.data(); - ECDSA_SIG* ecdsa_sig = d2i_ECDSA_SIG(nullptr, &sig_ptr, der_sig.size()); - if (ecdsa_sig == nullptr) - { - LOG(INFO) << "OpenSSL: d2i_ECDSA_SIG failed"; - return false; - } - int verification = ECDSA_do_verify(digest.data(), digest.size(), ecdsa_sig, d_PublicKey); - ECDSA_SIG_free(ecdsa_sig); - if (verification == 1) - { - LOG(INFO) << "OpenSSL: OSNMA signature authenticated successfully"; - success = true; - } - else if (verification == 0) - { - LOG(INFO) << "OpenSSL: invalid signature found when verifying message"; - } - else - { - LOG(INFO) << "OpenSSL: OSNMA message authentication failed"; - } -#endif -#endif - return success; -} - - -void Gnss_Crypto::set_public_key(const std::vector& publicKey) -{ -#if USE_GNUTLS_FALLBACK - gnutls_pubkey_t pubkey; - gnutls_datum_t pemDatum = {const_cast(publicKey.data()), static_cast(publicKey.size())}; - gnutls_pubkey_init(&pubkey); - int ret = gnutls_pubkey_import(pubkey, &pemDatum, GNUTLS_X509_FMT_PEM); - if (ret != GNUTLS_E_SUCCESS) - { - gnutls_pubkey_deinit(pubkey); - std::cerr << "GnuTLS: error setting the public key" << std::endl; - std::cerr << "GnuTLS error: " << gnutls_strerror(ret) << std::endl; - return; - } - pubkey_copy(pubkey, &d_PublicKey); - gnutls_pubkey_deinit(pubkey); -#else // OpenSSL - BIO* bio = nullptr; - EVP_PKEY* pkey = nullptr; - bio = BIO_new_mem_buf(const_cast(publicKey.data()), publicKey.size()); - if (!bio) - { - std::cerr << "Failed to create BIO for key \n"; - return; - } - - pkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr); - BIO_free(bio); - - if (!pkey) - { - std::cerr << "OpenSSL: error setting the OSNMA public key." << std::endl; - LOG(INFO) << "OpenSSL: error setting the OSNMA public key."; - return; - } -#if USE_OPENSSL_3 - if (!pubkey_copy(pkey, &d_PublicKey)) - { - return; - } -#else - EC_KEY* ec_pkey = EVP_PKEY_get1_EC_KEY(pkey); - if (!pubkey_copy(ec_pkey, &d_PublicKey)) - { - return; - } - EC_KEY_free(ec_pkey); -#endif // OpenSSL 1.x - EVP_PKEY_free(pkey); -#endif - LOG(INFO) << "OSNMA Public Key successfully set up."; -} - - bool Gnss_Crypto::convert_raw_to_der_ecdsa(const std::vector& raw_signature, std::vector& der_signature) const { if (raw_signature.size() % 2 != 0) @@ -1087,6 +1178,27 @@ bool Gnss_Crypto::convert_raw_to_der_ecdsa(const std::vector& raw_signa } +std::vector Gnss_Crypto::convert_from_hex_str(const std::string& input) const +{ + std::vector result; + + // Iterate over the input string in pairs + for (size_t i = 0; i < input.length(); i += 2) + { + // Extract two hexadecimal characters from the input string + std::string hexByte = input.substr(i, 2); + + // Convert the hexadecimal string to an integer value + auto value = static_cast(std::stoul(hexByte, nullptr, 16)); + + // Append the value to the result vector + result.push_back(value); + } + + return result; +} + + #if USE_GNUTLS_FALLBACK // GnuTLS-specific functions bool Gnss_Crypto::pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest) { @@ -1170,9 +1282,7 @@ bool Gnss_Crypto::pubkey_copy(EVP_PKEY* src, EVP_PKEY** dest) return true; } - -#else // OpenSSL 1.x - +#else // OpenSSL 1.x bool Gnss_Crypto::pubkey_copy(EC_KEY* src, EC_KEY** dest) { // Open a memory buffer @@ -1216,122 +1326,5 @@ bool Gnss_Crypto::pubkey_copy(EC_KEY* src, EC_KEY** dest) return true; } +#endif // OpenSSL #endif -#endif - - -bool Gnss_Crypto::store_public_key(const std::string& pubKeyFilePath) const -{ - if (!have_public_key()) - { - return false; - } - std::ofstream pubKeyFile(pubKeyFilePath, std::ios::binary); - if (!pubKeyFile.is_open()) - { - LOG(INFO) << "Unable to open file: " << pubKeyFilePath; - return false; - } -#if USE_GNUTLS_FALLBACK - gnutls_datum_t pem_data; -#if HAVE_GNUTLS_PUBKEY_EXPORT2 - int ret = gnutls_pubkey_export2(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data); -#else - size_t output_stata_size; - int ret = gnutls_pubkey_export(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data, &output_stata_size); -#endif - if (ret != GNUTLS_E_SUCCESS) - { - LOG(INFO) << "GnuTLS: Failed to export public key: " << gnutls_strerror(ret); - return false; - } - - pubKeyFile.write((const char*)pem_data.data, pem_data.size); - pubKeyFile.close(); - gnutls_free(pem_data.data); -#else // OpenSSL - BIO* bio = BIO_new(BIO_s_mem()); - if (!bio) - { - LOG(INFO) << "OpenSSL: Failed to create BIO"; - return false; - } -#if USE_OPENSSL_3 - if (!PEM_write_bio_PUBKEY(bio, d_PublicKey)) -#else // OpenSSL 1.x - if (!PEM_write_bio_EC_PUBKEY(bio, d_PublicKey)) -#endif - { - LOG(INFO) << "OpenSSL: Failed to write public key to BIO"; - BIO_free(bio); - return false; - } - - char* bio_data; - auto bio_len = BIO_get_mem_data(bio, &bio_data); - if (bio_len <= 0) - { - LOG(INFO) << "OpenSSL: Failed to get BIO data"; - BIO_free(bio); - return false; - } - - pubKeyFile.write(bio_data, bio_len); - pubKeyFile.close(); - BIO_free(bio); -#endif - return true; -} - - -std::vector Gnss_Crypto::getPublicKey() const -{ - if (!have_public_key()) - { - return {}; - } -#if USE_GNUTLS_FALLBACK - gnutls_datum_t pem_data = {nullptr, 0}; - - int ret = gnutls_pubkey_export2(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data); - if (ret != GNUTLS_E_SUCCESS) - { - LOG(INFO) << "GnuTLS: Failed to export public key to PEM format."; - return {}; - } - std::vector output(pem_data.data, pem_data.data + pem_data.size); - - // Free the allocated memory by gnutls_pubkey_export2 - gnutls_free(pem_data.data); -#else // OpenSSL - // Create a BIO for the memory buffer - BIO* mem = BIO_new(BIO_s_mem()); - if (!mem) - { - LOG(INFO) << "OpenSSL: Failed to create BIO."; - return {}; - } -#if USE_OPENSSL_3 - if (!PEM_write_bio_PUBKEY(mem, d_PublicKey)) -#else // OpenSSL 1.x - if (!PEM_write_bio_EC_PUBKEY(mem, d_PublicKey)) -#endif - { - BIO_free(mem); - LOG(INFO) << "OpenSSL: Failed to write public key to PEM format."; - return {}; - } - - // Get the length of the data in the BIO - BUF_MEM* mem_ptr; - BIO_get_mem_ptr(mem, &mem_ptr); - - // Copy the data from the BIO to a std::vector - std::vector output(mem_ptr->length); - memcpy(output.data(), mem_ptr->data, mem_ptr->length); - - // Clean up the BIO - BIO_free(mem); -#endif - return output; -} \ No newline at end of file diff --git a/src/core/system_parameters/gnss_crypto.h b/src/core/system_parameters/gnss_crypto.h index bd0c2a64d..117d5e0a7 100644 --- a/src/core/system_parameters/gnss_crypto.h +++ b/src/core/system_parameters/gnss_crypto.h @@ -34,33 +34,43 @@ /** \addtogroup System_Parameters * \{ */ +/*! + * \brief Class implementing cryptographic functions + * for Navigation Message Authentication + */ class Gnss_Crypto { public: - Gnss_Crypto(); - Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath); - ~Gnss_Crypto(); + Gnss_Crypto(); //!< Default constructor - void set_public_key(const std::vector& publickey); - bool have_public_key() const; - bool verify_signature(const std::vector& message, const std::vector& signature) const; - bool verify_signature_p521(const std::vector& message, const std::vector& signature) const; + /*! + * Constructor with a .crt or .pem file for the ECDSA Public Key + * and a XML file for the Merkle Tree root. + * Files can be downloaded by registering at https://www.gsc-europa.eu/ + */ + Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath); + ~Gnss_Crypto(); //!< Default destructor + + bool have_public_key() const; //!< Returns true if the ECDSA Public Key is already loaded + + bool verify_signature(const std::vector& message, const std::vector& signature) const; //!< Verify ECDSA-P256 signature (message in hex, signature in raw format) + bool verify_signature_ecdsa_p521(const std::vector& message, const std::vector& signature) const; //!< Verify ECDSA-P521 signature (message in hex, signature in raw format) + + /*! + * Stores the ECDSA Public Key in a .pem file, which is read in a following run if the .crt file is not found + */ bool store_public_key(const std::string& pubKeyFilePath) const; - std::vector getPublicKey() const; - std::vector computeSHA256(const std::vector& input) const; - std::vector computeSHA3_256(const std::vector& input) const; - std::vector computeHMAC_SHA_256(const std::vector& key, const std::vector& input) const; - std::vector computeCMAC_AES(const std::vector& key, const std::vector& input) const; + std::vector getPublicKey() const; //!< Gets the ECDSA Public Key in PEM format + std::vector getMerkleRoot() const; //!< Gets the Merkle Tree root node (\f$ x_{4,0} \f$) - inline std::vector getMerkleRoot() const - { - return d_x_4_0; - } - inline void setMerkleRoot(const std::vector& v) - { - d_x_4_0 = v; - } + std::vector computeSHA256(const std::vector& input) const; //!< Computes SHA-256 hash + std::vector computeSHA3_256(const std::vector& input) const; //!< Computes SHA3-256 hash + std::vector computeHMAC_SHA_256(const std::vector& key, const std::vector& input) const; //!< Computes HMAC-SHA-256 message authentication code + std::vector computeCMAC_AES(const std::vector& key, const std::vector& input) const; //!< Computes CMAC-AES message authentication code + + void set_public_key(const std::vector& publickey); //!< Sets the ECDSA Public Key (publickey in PEM format) + void setMerkleRoot(const std::vector& v); //!< Sets the Merkle Tree root node x(\f$ x_{4,0} \f$) private: void read_merkle_xml(const std::string& merkleFilePath); @@ -81,10 +91,6 @@ private: #endif #endif std::vector d_x_4_0; - std::vector d_x_3_1; - std::vector d_x_2_1; - std::vector d_x_1_1; - std::vector d_x_0_1; }; /** \} */ diff --git a/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc b/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc index 994256da1..bbe01d905 100644 --- a/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc +++ b/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc @@ -1,3 +1,21 @@ +/*! + * \file gnss_crypto_test.cc + * \brief Tests for the Gnss_Crypto class. + * \author Carles Fernandez, 2023-2024. cfernandez(at)cttc.es + * Cesare Ghionoiu Martinez, 2023-2024. c.ghionoiu-martinez@tu-braunschweig.de + * + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2024 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + #include "gnss_crypto.h" #include "gnss_sdr_filesystem.h" #include "gnss_sdr_make_unique.h" @@ -10,119 +28,26 @@ class GnssCryptoTest : public ::testing::Test }; -TEST(GnssCryptoTest, TestComputeSHA_256) -{ - auto d_crypto = std::make_unique(); - std::vector message{0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world - - std::vector expected_output = { - 0x18, 0x94, 0xA1, 0x9C, 0x85, 0xBA, 0x15, 0x3A, 0xCB, 0xF7, - 0x43, 0xAC, 0x4E, 0x43, 0xFC, 0x00, 0x4C, 0x89, 0x16, 0x04, 0xB2, - 0x6F, 0x8C, 0x69, 0xE1, 0xE8, 0x3E, 0xA2, 0xAF, 0xC7, 0xC4, 0x8F}; - - std::vector output = d_crypto->computeSHA256(message); - - ASSERT_EQ(expected_output, output); -} - - -TEST(GnssCryptoTest, TestComputeSHA3_256) -{ - auto d_crypto = std::make_unique(); - std::vector message{0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world - - std::vector expected_output = { - 0xCC, 0xB8, 0xF9, 0x23, 0x5F, 0x4A, 0x93, 0x2C, 0xA0, 0xAB, - 0xBB, 0x2C, 0x24, 0x36, 0x72, 0x5E, 0x2E, 0x8D, 0xC7, 0x5B, - 0x99, 0xE7, 0xF6, 0xC4, 0x50, 0x5B, 0x2A, 0x93, 0x6E, 0xB6, 0x3B, 0x3F}; - - std::vector output = d_crypto->computeSHA3_256(message); - - ASSERT_EQ(expected_output, output); -} - - -TEST(GnssCryptoTest, VerifySignature) -{ - auto d_crypto = std::make_unique(); - - // RG example - import crt certificate - result: FAIL - std::vector message = {0x82, 0x10, 0x49, 0x22, 0x04, 0xE0, 0x60, 0x61, 0x0B, 0xDF, 0x26, 0xD7, 0x7B, 0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF}; - std::vector signature = {0xF8, 0xCD, 0x88, 0x29, 0x9F, 0xA4, 0x60, 0x58, 0x00, 0x20, 0x7B, 0xFE, 0xBE, 0xAC, 0x55, 0x02, 0x40, 0x53, 0xF3, 0x0F, 0x7C, 0x69, 0xB3, 0x5C, 0x15, 0xE6, 0x08, 0x00, 0xAC, 0x3B, 0x6F, 0xE3, 0xED, 0x06, 0x39, 0x95, 0x2F, 0x7B, 0x02, 0x8D, 0x86, 0x86, 0x74, 0x45, 0x96, 0x1F, 0xFE, 0x94, 0xFB, 0x22, 0x6B, 0xFF, 0x70, 0x06, 0xE0, 0xC4, 0x51, 0xEE, 0x3F, 0x87, 0x28, 0xC1, 0x77, 0xFB}; - std::vector publicKey{// PEM format - 1000 bits - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, - 0x4D, 0x46, 0x6B, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, 0x6F, 0x5A, 0x49, - 0x7A, 0x6A, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x41, 0x37, 0x4C, 0x4F, 0x5A, 0x4C, 0x77, 0x67, 0x65, 0x39, 0x32, 0x4C, 0x78, 0x4E, 0x2B, 0x46, 0x6B, 0x59, 0x66, 0x38, 0x74, 0x6F, 0x59, 0x79, 0x44, 0x57, 0x50, 0x2F, 0x0A, 0x6F, 0x4A, 0x46, 0x42, 0x44, 0x38, 0x46, 0x59, 0x2B, 0x37, - 0x64, 0x35, 0x67, 0x4F, 0x71, 0x49, 0x61, 0x45, 0x32, 0x52, 0x6A, 0x50, 0x41, 0x6E, 0x4B, 0x49, 0x36, 0x38, 0x73, 0x2F, 0x4F, 0x4B, 0x2F, 0x48, 0x50, 0x67, 0x6F, 0x4C, 0x6B, 0x4F, 0x32, 0x69, 0x6A, 0x51, 0x38, 0x78, 0x41, 0x5A, 0x79, 0x44, 0x64, 0x50, 0x42, 0x31, 0x64, 0x48, 0x53, 0x51, 0x3D, 0x3D, - 0x0A, - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A}; - - d_crypto->set_public_key(publicKey); - bool result = d_crypto->verify_signature(message, signature); - - ASSERT_TRUE(result); -} - - -TEST(GnssCryptoTest, VerifySignatureP521) -{ - std::unique_ptr d_crypto = std::make_unique(); - - // Message to be verified - std::vector message = { - 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64 // "Hello World" - }; - - // Public key in PEM format - std::vector publicKey = { - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, - 0x4d, 0x49, 0x47, 0x62, 0x4d, 0x42, 0x41, 0x47, 0x42, 0x79, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67, 0x45, 0x47, 0x42, 0x53, 0x75, 0x42, 0x42, 0x41, 0x41, - 0x6a, 0x41, 0x34, 0x47, 0x47, 0x41, 0x41, 0x51, 0x41, 0x6f, 0x35, 0x76, 0x77, 0x66, 0x6e, 0x47, 0x57, 0x47, 0x33, 0x44, 0x63, 0x59, 0x75, 0x2b, 0x2f, 0x61, 0x58, - 0x47, 0x32, 0x7a, 0x74, 0x65, 0x41, 0x46, 0x50, 0x54, 0x33, 0x0a, 0x48, 0x36, 0x4c, 0x76, 0x4f, 0x4c, 0x76, 0x49, 0x51, 0x6a, 0x61, 0x2b, 0x6a, 0x74, 0x57, 0x73, - 0x70, 0x4f, 0x38, 0x37, 0x6f, 0x50, 0x32, 0x4e, 0x6d, 0x72, 0x34, 0x6e, 0x50, 0x68, 0x76, 0x62, 0x53, 0x58, 0x52, 0x4d, 0x37, 0x6a, 0x49, 0x69, 0x46, 0x38, 0x47, - 0x70, 0x6b, 0x75, 0x58, 0x6a, 0x75, 0x4e, 0x7a, 0x34, 0x72, 0x61, 0x56, 0x4f, 0x65, 0x49, 0x4d, 0x42, 0x77, 0x45, 0x2b, 0x61, 0x0a, 0x30, 0x4c, 0x76, 0x7a, 0x37, - 0x69, 0x54, 0x4d, 0x5a, 0x46, 0x41, 0x41, 0x51, 0x64, 0x2b, 0x70, 0x47, 0x72, 0x56, 0x54, 0x47, 0x77, 0x66, 0x53, 0x48, 0x49, 0x72, 0x49, 0x49, 0x45, 0x78, 0x74, - 0x5a, 0x35, 0x77, 0x30, 0x38, 0x51, 0x4f, 0x43, 0x58, 0x2f, 0x75, 0x46, 0x65, 0x2b, 0x30, 0x78, 0x52, 0x78, 0x4c, 0x64, 0x2f, 0x33, 0x36, 0x42, 0x4e, 0x74, 0x63, - 0x74, 0x69, 0x2f, 0x45, 0x4c, 0x0a, 0x4b, 0x31, 0x35, 0x67, 0x2b, 0x4b, 0x32, 0x71, 0x67, 0x2f, 0x6c, 0x39, 0x46, 0x42, 0x47, 0x67, 0x4d, 0x2b, 0x51, 0x3d, 0x0a, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a}; - - // ECDSA P-521 signature - std::vector signature = { - 0x01, 0x7b, 0x59, 0xac, 0x3a, 0x03, 0x5c, 0xb4, 0x07, 0xcd, 0xc1, 0xeb, 0xbe, 0xe5, 0xa6, 0xcb, - 0xda, 0x0a, 0xff, 0x4d, 0x38, 0x61, 0x16, 0x0f, 0xb3, 0x77, 0xe5, 0x8a, 0xdc, 0xf3, 0xfd, 0x79, - 0x38, 0x1e, 0xe8, 0x08, 0x3d, 0x5d, 0xbc, 0xc2, 0x80, 0x6e, 0xe9, 0x2b, 0xc3, 0xef, 0x07, 0x3d, - 0x0c, 0x82, 0x4c, 0x9b, 0x7a, 0x5c, 0x2e, 0xd5, 0x46, 0xbd, 0x22, 0x21, 0x13, 0x8a, 0xb2, 0xca, - 0x96, 0x3d, 0x01, 0xba, 0x2a, 0xc4, 0x3f, 0xdb, 0x66, 0x3c, 0x40, 0x26, 0xd9, 0xbc, 0x26, 0xd5, 0x57, 0xd4, - 0xbd, 0x15, 0x16, 0x88, 0x21, 0x3b, 0xaa, 0x07, 0x89, 0xef, 0x29, 0x8f, 0x2f, 0x85, 0x76, 0x58, - 0x9d, 0xca, 0x00, 0xcc, 0xc8, 0x30, 0x88, 0x31, 0x99, 0xc1, 0x94, 0xb9, 0xaf, 0x91, 0xdc, 0xc4, - 0x6f, 0x19, 0x2b, 0x12, 0xa2, 0x82, 0xa5, 0x66, 0x5e, 0x4b, 0xbb, 0xdf, 0x65, 0x81, 0x52, 0x14, - 0x01, 0xd7}; - - d_crypto->set_public_key(publicKey); - bool result = d_crypto->verify_signature_p521(message, signature); - ASSERT_TRUE(result); - - std::vector wrong_signature = signature; - wrong_signature[1] = 1; - bool result2 = d_crypto->verify_signature_p521(message, wrong_signature); - ASSERT_TRUE(!result2); -} - - TEST(GnssCryptoTest, VerifyPubKeyImport) { auto d_crypto = std::make_unique(); - std::vector publicKey{// PEM - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, - 0x4D, 0x46, - 0x6B, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x44, 0x41, 0x51, 0x63, - 0x44, 0x51, 0x67, 0x41, 0x45, 0x53, 0x76, 0x50, 0x75, 0x4F, 0x70, 0x51, 0x6C, 0x4A, 0x54, 0x31, 0x56, 0x77, 0x6C, 0x72, 0x43, 0x4C, 0x63, 0x38, 0x55, 0x54, 0x54, 0x6B, 0x4E, - 0x73, 0x66, 0x78, 0x2F, 0x0A, 0x4D, 0x56, 0x6F, 0x71, 0x47, 0x61, 0x35, 0x4F, 0x31, 0x73, 0x75, 0x6D, 0x57, 0x64, 0x61, 0x5A, 0x66, 0x4F, 0x69, 0x39, 0x48, 0x30, 0x4D, 0x30, - 0x48, 0x46, 0x6E, 0x5A, 0x32, 0x63, 0x72, 0x44, 0x37, 0x6C, 0x6A, 0x6C, 0x36, 0x74, 0x4E, 0x56, 0x52, 0x4F, 0x71, 0x4A, 0x63, 0x57, 0x58, 0x51, 0x6B, 0x6E, 0x4B, 0x69, 0x79, - 0x44, 0x79, 0x48, 0x58, 0x51, 0x3D, 0x3D, 0x0A, - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, - 0x2D, 0x2D, 0x2D, 0x0A}; + // PEM format + std::vector publicKey = { + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x55, + 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, + 0x0A, 0x4D, 0x46, 0x6B, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4B, 0x6F, 0x5A, 0x49, + 0x7A, 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, + 0x6A, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x53, 0x76, + 0x50, 0x75, 0x4F, 0x70, 0x51, 0x6C, 0x4A, 0x54, 0x31, 0x56, 0x77, 0x6C, 0x72, + 0x43, 0x4C, 0x63, 0x38, 0x55, 0x54, 0x54, 0x6B, 0x4E, 0x73, 0x66, 0x78, 0x2F, + 0x0A, 0x4D, 0x56, 0x6F, 0x71, 0x47, 0x61, 0x35, 0x4F, 0x31, 0x73, 0x75, 0x6D, + 0x57, 0x64, 0x61, 0x5A, 0x66, 0x4F, 0x69, 0x39, 0x48, 0x30, 0x4D, 0x30, 0x48, + 0x46, 0x6E, 0x5A, 0x32, 0x63, 0x72, 0x44, 0x37, 0x6C, 0x6A, 0x6C, 0x36, 0x74, + 0x4E, 0x56, 0x52, 0x4F, 0x71, 0x4A, 0x63, 0x57, 0x58, 0x51, 0x6B, 0x6E, 0x4B, + 0x69, 0x79, 0x44, 0x79, 0x48, 0x58, 0x51, 0x3D, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, + 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, + 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A}; d_crypto->set_public_key(publicKey); @@ -137,14 +62,22 @@ TEST(GnssCryptoTest, VerifyPublicKeyStorage) const std::string f1("./osnma_test_file1.pem"); const std::string f2("./osnma_test_file2.pem"); - std::vector publicKey{ - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, - 0x4D, 0x46, 0x6B, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, 0x6F, 0x5A, 0x49, - 0x7A, 0x6A, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x41, 0x37, 0x4C, 0x4F, 0x5A, 0x4C, 0x77, 0x67, 0x65, 0x39, 0x32, 0x4C, 0x78, 0x4E, 0x2B, - 0x46, 0x6B, 0x59, 0x66, 0x38, 0x74, 0x6F, 0x59, 0x79, 0x44, 0x57, 0x50, 0x2F, 0x0A, 0x6F, 0x4A, 0x46, 0x42, 0x44, 0x38, 0x46, 0x59, 0x2B, 0x37, - 0x64, 0x35, 0x67, 0x4F, 0x71, 0x49, 0x61, 0x45, 0x32, 0x52, 0x6A, 0x50, 0x41, 0x6E, 0x4B, 0x49, 0x36, 0x38, 0x73, 0x2F, 0x4F, 0x4B, 0x2F, 0x48, 0x50, 0x67, 0x6F, - 0x4C, 0x6B, 0x4F, 0x32, 0x69, 0x6A, 0x51, 0x38, 0x78, 0x41, 0x5A, 0x79, 0x44, 0x64, 0x50, 0x42, 0x31, 0x64, 0x48, 0x53, 0x51, 0x3D, 0x3D, 0x0A, - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A}; + // PEM format + std::vector publicKey = { + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x55, + 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, + 0x0A, 0x4D, 0x46, 0x6B, 0x77, 0x45, 0x77, 0x59, 0x48, 0x4B, 0x6F, 0x5A, 0x49, + 0x7A, 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, + 0x6A, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, 0x67, 0x41, 0x45, 0x53, 0x76, + 0x50, 0x75, 0x4F, 0x70, 0x51, 0x6C, 0x4A, 0x54, 0x31, 0x56, 0x77, 0x6C, 0x72, + 0x43, 0x4C, 0x63, 0x38, 0x55, 0x54, 0x54, 0x6B, 0x4E, 0x73, 0x66, 0x78, 0x2F, + 0x0A, 0x4D, 0x56, 0x6F, 0x71, 0x47, 0x61, 0x35, 0x4F, 0x31, 0x73, 0x75, 0x6D, + 0x57, 0x64, 0x61, 0x5A, 0x66, 0x4F, 0x69, 0x39, 0x48, 0x30, 0x4D, 0x30, 0x48, + 0x46, 0x6E, 0x5A, 0x32, 0x63, 0x72, 0x44, 0x37, 0x6C, 0x6A, 0x6C, 0x36, 0x74, + 0x4E, 0x56, 0x52, 0x4F, 0x71, 0x4A, 0x63, 0x57, 0x58, 0x51, 0x6B, 0x6E, 0x4B, + 0x69, 0x79, 0x44, 0x79, 0x48, 0x58, 0x51, 0x3D, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, + 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, + 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A}; d_crypto->set_public_key(publicKey); bool result = d_crypto->store_public_key(f1); @@ -172,6 +105,40 @@ TEST(GnssCryptoTest, VerifyPublicKeyStorage) } +TEST(GnssCryptoTest, TestComputeSHA_256) +{ + auto d_crypto = std::make_unique(); + std::vector message{ + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world + + std::vector expected_output = { + 0x18, 0x94, 0xA1, 0x9C, 0x85, 0xBA, 0x15, 0x3A, 0xCB, 0xF7, + 0x43, 0xAC, 0x4E, 0x43, 0xFC, 0x00, 0x4C, 0x89, 0x16, 0x04, 0xB2, + 0x6F, 0x8C, 0x69, 0xE1, 0xE8, 0x3E, 0xA2, 0xAF, 0xC7, 0xC4, 0x8F}; + + std::vector output = d_crypto->computeSHA256(message); + + ASSERT_EQ(expected_output, output); +} + + +TEST(GnssCryptoTest, TestComputeSHA3_256) +{ + auto d_crypto = std::make_unique(); + std::vector message{ + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world + + std::vector expected_output = { + 0xCC, 0xB8, 0xF9, 0x23, 0x5F, 0x4A, 0x93, 0x2C, 0xA0, 0xAB, + 0xBB, 0x2C, 0x24, 0x36, 0x72, 0x5E, 0x2E, 0x8D, 0xC7, 0x5B, + 0x99, 0xE7, 0xF6, 0xC4, 0x50, 0x5B, 0x2A, 0x93, 0x6E, 0xB6, 0x3B, 0x3F}; + + std::vector output = d_crypto->computeSHA3_256(message); + + ASSERT_EQ(expected_output, output); +} + + // Unit test for computeHMAC_SHA_256 function. TEST(GnssCryptoTest, TestComputeHMACSHA256) { @@ -183,7 +150,9 @@ TEST(GnssCryptoTest, TestComputeHMACSHA256) 0xEB, 0x62, 0x3E, 0x95, 0x6B, 0x2B, 0xCE, 0xA3, 0xB4, 0xD4, 0xDB, 0x31, 0xEE, 0x96, 0xAB, 0xFA}; - std::vector message{0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world con 0x0A + std::vector message{ + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A}; // Hello world + std::vector expected_output = { 0xC3, 0x51, 0xF6, 0xFD, 0xDD, 0xC9, 0x8B, 0x41, 0xD6, 0xF4, 0x77, 0x6D, 0xAC, 0xE8, 0xE0, 0x14, @@ -201,21 +170,29 @@ TEST(GnssCryptoTest, TestComputeHMACSHA256_m0) // key and message generated from RG A.6.5.1 auto d_crypto = std::make_unique(); - std::vector key = {// RG K4 @ 345690 + // RG K4 @ 345690 + std::vector key = { 0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; - std::vector message{// m0 - 0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5, 0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B, - 0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB, 0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21, - 0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37, 0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D, - 0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0, 0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00, - 0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07, 0xF7, 0xFC, 0x00}; + // m0 + std::vector message = { + 0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5, + 0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B, + 0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB, + 0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21, + 0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37, + 0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D, + 0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0, + 0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00, + 0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07, + 0xF7, 0xFC, 0x00}; std::vector expected_output = { 0xE3, 0x7B, 0xC4, 0xF8, 0x58, 0xAE, 0x1E, 0x5C, - 0xFD, 0xC4, 0x6F, 0x05, 0x4B, 0x1F, 0x47, 0xB9, 0xD2, 0xEA, 0x61, 0xE1, - 0xEF, 0x09, 0x11, 0x5C, 0xFE, 0x70, 0x68, 0x52, 0xBF, 0xF2, 0x3A, 0x83}; + 0xFD, 0xC4, 0x6F, 0x05, 0x4B, 0x1F, 0x47, 0xB9, + 0xD2, 0xEA, 0x61, 0xE1, 0xEF, 0x09, 0x11, 0x5C, + 0xFE, 0x70, 0x68, 0x52, 0xBF, 0xF2, 0x3A, 0x83}; std::vector output = d_crypto->computeHMAC_SHA_256(key, message); @@ -228,19 +205,22 @@ TEST(GnssCryptoTest, TestComputeHMACSHA256_adkd4) // key and message generated from RG A.6.5.2 auto d_crypto = std::make_unique(); - std::vector key = {// RG K4 @ 345690 + // RG K4 @ 345690 + std::vector key = { 0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; - std::vector message{ + std::vector message = { 0x02, 0x02, 0x4E, 0x05, 0x46, 0x3C, 0x03, 0xBF, - 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x44, 0x92, 0x38, - 0x22, 0x78, 0x97, 0xFD, 0xEF, 0xF9, 0x30, 0x40}; + 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x44, 0x92, + 0x38, 0x22, 0x78, 0x97, 0xFD, 0xEF, 0xF9, 0x30, + 0x40}; std::vector expected_output = { - 0x7B, 0xB2, 0x38, 0xC8, 0x83, 0xC0, 0x6A, 0x2B, 0x50, 0x8F, - 0xE6, 0x3F, 0xB7, 0xF4, 0xF5, 0x4D, 0x44, 0xAB, 0xEE, 0x4D, - 0xCE, 0xB9, 0x3D, 0xCF, 0x65, 0xCB, 0x3A, 0x5B, 0x81, 0x4A, 0x34, 0xE9}; + 0x7B, 0xB2, 0x38, 0xC8, 0x83, 0xC0, 0x6A, 0x2B, + 0x50, 0x8F, 0xE6, 0x3F, 0xB7, 0xF4, 0xF5, 0x4D, + 0x44, 0xAB, 0xEE, 0x4D, 0xCE, 0xB9, 0x3D, 0xCF, + 0x65, 0xCB, 0x3A, 0x5B, 0x81, 0x4A, 0x34, 0xE9}; std::vector output = d_crypto->computeHMAC_SHA_256(key, message); @@ -268,4 +248,119 @@ TEST(GnssCryptoTest, TestComputeCMAC_AES) std::vector output = d_crypto->computeCMAC_AES(key, message); ASSERT_EQ(expected_output, output); -} \ No newline at end of file +} + + +TEST(GnssCryptoTest, VerifySignature) +{ + auto d_crypto = std::make_unique(); + + // RG example - import crt certificate + std::vector message = { + 0x82, 0x10, 0x49, 0x22, 0x04, 0xE0, 0x60, 0x61, 0x0B, 0xDF, + 0x26, 0xD7, 0x7B, 0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, + 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF}; + + // ECDSA P-256 signature, raw format + std::vector signature = { + 0xF8, 0xCD, 0x88, 0x29, 0x9F, 0xA4, 0x60, 0x58, 0x00, 0x20, + 0x7B, 0xFE, 0xBE, 0xAC, 0x55, 0x02, 0x40, 0x53, 0xF3, 0x0F, + 0x7C, 0x69, 0xB3, 0x5C, 0x15, 0xE6, 0x08, 0x00, 0xAC, 0x3B, + 0x6F, 0xE3, 0xED, 0x06, 0x39, 0x95, 0x2F, 0x7B, 0x02, 0x8D, + 0x86, 0x86, 0x74, 0x45, 0x96, 0x1F, 0xFE, 0x94, 0xFB, 0x22, + 0x6B, 0xFF, 0x70, 0x06, 0xE0, 0xC4, 0x51, 0xEE, 0x3F, 0x87, + 0x28, 0xC1, 0x77, 0xFB}; + + // PEM format + std::vector publicKey = { + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, + 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, + 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x46, 0x6B, + 0x77, 0x45, 0x77, 0x59, 0x48, 0x4B, 0x6F, 0x5A, 0x49, 0x7A, + 0x6A, 0x30, 0x43, 0x41, 0x51, 0x59, 0x49, 0x4B, 0x6F, 0x5A, + 0x49, 0x7A, 0x6A, 0x30, 0x44, 0x41, 0x51, 0x63, 0x44, 0x51, + 0x67, 0x41, 0x45, 0x41, 0x37, 0x4C, 0x4F, 0x5A, 0x4C, 0x77, + 0x67, 0x65, 0x39, 0x32, 0x4C, 0x78, 0x4E, 0x2B, 0x46, 0x6B, + 0x59, 0x66, 0x38, 0x74, 0x6F, 0x59, 0x79, 0x44, 0x57, 0x50, + 0x2F, 0x0A, 0x6F, 0x4A, 0x46, 0x42, 0x44, 0x38, 0x46, 0x59, + 0x2B, 0x37, 0x64, 0x35, 0x67, 0x4F, 0x71, 0x49, 0x61, 0x45, + 0x32, 0x52, 0x6A, 0x50, 0x41, 0x6E, 0x4B, 0x49, 0x36, 0x38, + 0x73, 0x2F, 0x4F, 0x4B, 0x2F, 0x48, 0x50, 0x67, 0x6F, 0x4C, + 0x6B, 0x4F, 0x32, 0x69, 0x6A, 0x51, 0x38, 0x78, 0x41, 0x5A, + 0x79, 0x44, 0x64, 0x50, 0x42, 0x31, 0x64, 0x48, 0x53, 0x51, + 0x3D, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, + 0x44, 0x20, 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, + 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A}; + + d_crypto->set_public_key(publicKey); + bool result = d_crypto->verify_signature(message, signature); + ASSERT_TRUE(result); + + std::vector wrong_signature = signature; + wrong_signature[1] = 1; + bool result2 = d_crypto->verify_signature(message, wrong_signature); + ASSERT_TRUE(!result2); +} + + +TEST(GnssCryptoTest, VerifySignatureP521) +{ + std::unique_ptr d_crypto = std::make_unique(); + + // Message to be verified + std::vector message = { + 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64 // "Hello World" + }; + + // Public key in PEM format + std::vector publicKey = { + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, + 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, + 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x47, 0x62, 0x4D, 0x42, 0x41, 0x47, 0x42, + 0x79, 0x71, 0x47, 0x53, 0x4D, 0x34, 0x39, 0x41, 0x67, 0x45, 0x47, 0x42, + 0x53, 0x75, 0x42, 0x42, 0x41, 0x41, 0x6A, 0x41, 0x34, 0x47, 0x47, 0x41, + 0x41, 0x51, 0x41, 0x6F, 0x35, 0x76, 0x77, 0x66, 0x6E, 0x47, 0x57, 0x47, + 0x33, 0x44, 0x63, 0x59, 0x75, 0x2B, 0x2F, 0x61, 0x58, 0x47, 0x32, 0x7A, + 0x74, 0x65, 0x41, 0x46, 0x50, 0x54, 0x33, 0x0A, 0x48, 0x36, 0x4C, 0x76, + 0x4F, 0x4C, 0x76, 0x49, 0x51, 0x6A, 0x61, 0x2B, 0x6A, 0x74, 0x57, 0x73, + 0x70, 0x4F, 0x38, 0x37, 0x6F, 0x50, 0x32, 0x4E, 0x6D, 0x72, 0x34, 0x6E, + 0x50, 0x68, 0x76, 0x62, 0x53, 0x58, 0x52, 0x4D, 0x37, 0x6A, 0x49, 0x69, + 0x46, 0x38, 0x47, 0x70, 0x6B, 0x75, 0x58, 0x6A, 0x75, 0x4E, 0x7A, 0x34, + 0x72, 0x61, 0x56, 0x4F, 0x65, 0x49, 0x4D, 0x42, 0x77, 0x45, 0x2B, 0x61, + 0x0A, 0x30, 0x4C, 0x76, 0x7A, 0x37, 0x69, 0x54, 0x4D, 0x5A, 0x46, 0x41, + 0x41, 0x51, 0x64, 0x2B, 0x70, 0x47, 0x72, 0x56, 0x54, 0x47, 0x77, 0x66, + 0x53, 0x48, 0x49, 0x72, 0x49, 0x49, 0x45, 0x78, 0x74, 0x5A, 0x35, 0x77, + 0x30, 0x38, 0x51, 0x4F, 0x43, 0x58, 0x2F, 0x75, 0x46, 0x65, 0x2B, 0x30, + 0x78, 0x52, 0x78, 0x4C, 0x64, 0x2F, 0x33, 0x36, 0x42, 0x4E, 0x74, 0x63, + 0x74, 0x69, 0x2F, 0x45, 0x4C, 0x0A, 0x4B, 0x31, 0x35, 0x67, 0x2B, 0x4B, + 0x32, 0x71, 0x67, 0x2F, 0x6C, 0x39, 0x46, 0x42, 0x47, 0x67, 0x4D, 0x2B, + 0x51, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, + 0x50, 0x55, 0x42, 0x4C, 0x49, 0x43, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, + 0x2D, 0x2D, 0x2D, 0x0A}; + + // ECDSA P-521 signature, raw format + std::vector signature = { + 0x01, 0x7B, 0x59, 0xAC, 0x3A, 0x03, 0x5C, 0xB4, 0x07, 0xCD, + 0xC1, 0xEB, 0xBE, 0xE5, 0xA6, 0xCB, 0xDA, 0x0A, 0xFF, 0x4D, + 0x38, 0x61, 0x16, 0x0F, 0xB3, 0x77, 0xE5, 0x8A, 0xDC, 0xF3, + 0xFD, 0x79, 0x38, 0x1E, 0xE8, 0x08, 0x3D, 0x5D, 0xBC, 0xC2, + 0x80, 0x6E, 0xE9, 0x2B, 0xC3, 0xEF, 0x07, 0x3D, 0x0C, 0x82, + 0x4C, 0x9B, 0x7A, 0x5C, 0x2E, 0xD5, 0x46, 0xBD, 0x22, 0x21, + 0x13, 0x8A, 0xB2, 0xCA, 0x96, 0x3D, 0x01, 0xBA, 0x2A, 0xC4, + 0x3F, 0xDB, 0x66, 0x3C, 0x40, 0x26, 0xD9, 0xBC, 0x26, 0xD5, + 0x57, 0xD4, 0xBD, 0x15, 0x16, 0x88, 0x21, 0x3B, 0xAA, 0x07, + 0x89, 0xEF, 0x29, 0x8F, 0x2F, 0x85, 0x76, 0x58, 0x9D, 0xCA, + 0x00, 0xCC, 0xC8, 0x30, 0x88, 0x31, 0x99, 0xC1, 0x94, 0xB9, + 0xAF, 0x91, 0xDC, 0xC4, 0x6F, 0x19, 0x2B, 0x12, 0xA2, 0x82, + 0xA5, 0x66, 0x5E, 0x4B, 0xBB, 0xDF, 0x65, 0x81, 0x52, 0x14, + 0x01, 0xD7}; + + d_crypto->set_public_key(publicKey); + bool result = d_crypto->verify_signature_ecdsa_p521(message, signature); + ASSERT_TRUE(result); + + std::vector wrong_signature = signature; + wrong_signature[1] = 1; + bool result2 = d_crypto->verify_signature_ecdsa_p521(message, wrong_signature); + ASSERT_TRUE(!result2); +}