From c8347584f8834643cc1179b0ebad2ed6892d86fa Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 11 Jun 2023 01:46:08 +0200 Subject: [PATCH] Add work on OSNMA receiver --- src/core/libs/osnma_msg_receiver.cc | 6 +- src/core/receiver/gnss_flowgraph.cc | 4 +- src/core/system_parameters/Galileo_OSNMA.h | 1 + src/core/system_parameters/gnss_crypto.cc | 340 ++++++++++++++------- src/core/system_parameters/gnss_crypto.h | 11 +- 5 files changed, 235 insertions(+), 127 deletions(-) diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc index 6d8275763..b8da3b78b 100644 --- a/src/core/libs/osnma_msg_receiver.cc +++ b/src/core/libs/osnma_msg_receiver.cc @@ -340,9 +340,9 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg << ", WN=" << static_cast(d_osnma_data.d_dsm_kroot_message.wn_k) << ", TOW=" << static_cast(d_osnma_data.d_dsm_kroot_message.towh_k) * 3600 << " validated" << std::endl; - std::cout << "Galileo OSNMA: NMAS is " << d_dsm_reader->get_nmas_status(d_osnma_data.d_nma_header.nmas) << ", " - << " Chain in force is " << static_cast(d_osnma_data.d_nma_header.cid) << ", " - << "CPSK is " << d_dsm_reader->get_cpks_status(d_osnma_data.d_nma_header.cpks) << std::endl; + std::cout << "Galileo OSNMA: NMA Status is " << d_dsm_reader->get_nmas_status(d_osnma_data.d_nma_header.nmas) << ", " + << "Chain in force is " << static_cast(d_osnma_data.d_nma_header.cid) << ", " + << "Chain and Public Key Status is " << d_dsm_reader->get_cpks_status(d_osnma_data.d_nma_header.cpks) << std::endl; } // Validate signature } diff --git a/src/core/receiver/gnss_flowgraph.cc b/src/core/receiver/gnss_flowgraph.cc index 7d17fa1b0..3856876ca 100644 --- a/src/core/receiver/gnss_flowgraph.cc +++ b/src/core/receiver/gnss_flowgraph.cc @@ -27,6 +27,7 @@ #include "Galileo_E5a.h" #include "Galileo_E5b.h" #include "Galileo_E6.h" +#include "Galileo_OSNMA.h" #include "channel.h" #include "channel_fsm.h" #include "channel_interface.h" @@ -118,8 +119,7 @@ void GNSSFlowgraph::init() if (configuration_->property("Channels_1B.count", 0) > 0) { enable_osnma_rx_ = true; - const std::string pemfile_default("./OSNMA_PublicKey_20210920133026.pem"); - auto pemFilePath = configuration_->property("GNSS-SDR.OSNMA_pem", pemfile_default); + auto pemFilePath = configuration_->property("GNSS-SDR.OSNMA_pem", PEMFILE_DEFAULT); osnma_rx_ = osnma_msg_receiver_make(pemFilePath); } else diff --git a/src/core/system_parameters/Galileo_OSNMA.h b/src/core/system_parameters/Galileo_OSNMA.h index be43d91f2..2cb486248 100644 --- a/src/core/system_parameters/Galileo_OSNMA.h +++ b/src/core/system_parameters/Galileo_OSNMA.h @@ -160,6 +160,7 @@ const std::unordered_map OSNMA_TABLE_15 = { {std::string("SHA-256"), 512}, {std::string("SHA-512"), 1056}}; // key: ECDSA Curve and hash function, value: {l_ds_bits} +const std::string PEMFILE_DEFAULT("./OSNMA_PublicKey_20210920133026.pem"); /** \} */ /** \} */ #endif // GNSS_SDR_GALILEO_OSNMA_H \ No newline at end of file diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc index 89fdc9e01..0a1eb6623 100644 --- a/src/core/system_parameters/gnss_crypto.cc +++ b/src/core/system_parameters/gnss_crypto.cc @@ -16,10 +16,12 @@ */ #include "gnss_crypto.h" +#include "Galileo_OSNMA.h" #include #include #include #include +#include #if USE_OPENSSL_FALLBACK #include @@ -29,7 +31,6 @@ #include #define OPENSSL_ENGINE nullptr #else -#include #include #endif #else @@ -40,7 +41,7 @@ Gnss_Crypto::Gnss_Crypto(const std::string& filePath) { - d_PublicKey = readPublicKeyFromPEM(filePath); + readPublicKeyFromPEM(filePath); } @@ -56,7 +57,7 @@ void Gnss_Crypto::set_public_key(const std::vector& publickey) } -std::vector Gnss_Crypto::computeSHA256(const std::vector& input) +std::vector Gnss_Crypto::computeSHA256(const std::vector& input) const { std::vector output(32); // SHA256 hash size #if USE_OPENSSL_FALLBACK @@ -101,7 +102,7 @@ std::vector Gnss_Crypto::computeSHA256(const std::vector& inpu } -std::vector Gnss_Crypto::computeSHA3_256(const std::vector& input) +std::vector Gnss_Crypto::computeSHA3_256(const std::vector& input) const { std::vector output(32); // SHA256 hash size #if USE_OPENSSL_FALLBACK @@ -129,7 +130,7 @@ std::vector Gnss_Crypto::computeSHA3_256(const std::vector& in } -std::vector Gnss_Crypto::computeHMAC_SHA_256(const std::vector& key, const std::vector& input) +std::vector Gnss_Crypto::computeHMAC_SHA_256(const std::vector& key, const std::vector& input) const { std::vector output(32); #if USE_OPENSSL_FALLBACK @@ -171,7 +172,6 @@ std::vector Gnss_Crypto::computeHMAC_SHA_256(const std::vector // Resize the HMAC vector to the actual length hmac.resize(hmacLen); - output = hmac; #endif #else @@ -182,13 +182,12 @@ std::vector Gnss_Crypto::computeHMAC_SHA_256(const std::vector gnutls_hmac_output(hmac, output_aux.data()); output = output_aux; gnutls_hmac_deinit(hmac, output_aux.data()); - #endif return output; } -std::vector Gnss_Crypto::computeCMAC_AES(const std::vector& key, const std::vector& input) +std::vector Gnss_Crypto::computeCMAC_AES(const std::vector& key, const std::vector& input) const { std::vector output(16); #if USE_OPENSSL_FALLBACK @@ -254,158 +253,265 @@ std::vector Gnss_Crypto::computeCMAC_AES(const std::vector& ke } -std::vector Gnss_Crypto::readPublicKeyFromPEM(const std::string& filePath) +void Gnss_Crypto::readPublicKeyFromPEM(const std::string& filePath) { - std::vector publicKey; // Open the .pem file std::ifstream pemFile(filePath); if (!pemFile) { - std::cerr << "Failed to open the file: " << filePath << std::endl; - return publicKey; + // PEM file not found + // If it not was the default, maybe it is a configuration error + if (filePath != PEMFILE_DEFAULT) + { + std::cerr << "File " << filePath << " not found" << std::endl; + } + return; } -#if USE_OPENSSL_FALLBACK -#if USE_OPENSSL_3 - // Read the contents of the file into a string + std::vector publicKey; std::string pemContent((std::istreambuf_iterator(pemFile)), std::istreambuf_iterator()); - +#if USE_OPENSSL_FALLBACK // Create a BIO object from the string data BIO* bio = BIO_new_mem_buf(pemContent.c_str(), pemContent.length()); if (!bio) { - // Handle BIO creation error - pemFile.close(); - // ... + std::cerr << "OpenSSL: error creating a BIO object with data read from file " << filePath << ". Aborting import" << std::endl; + return; } - - // Read the PEM data from the BIO +#if USE_OPENSSL_3 + // Read the PEM data from the BIO object EVP_PKEY* evpKey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr); + BIO_free(bio); if (!evpKey) { - // Handle PEM reading error - BIO_free(bio); - pemFile.close(); - // ... + std::cerr << "OpenSSL: error reading the Public Key from file " << filePath << ". Aborting import" << std::endl; + return; } - // Create a memory BIO to write the public key data - BIO* memBio = BIO_new(BIO_s_mem()); - if (!memBio) + // Check if the public key is an EC key + if (EVP_PKEY_base_id(evpKey) != EVP_PKEY_EC) { - // Handle memory BIO creation error + std::cerr << "OpenSSL: Public key imported from file " << filePath << " is not an EC key. Aborting import" << std::endl; EVP_PKEY_free(evpKey); - BIO_free(bio); - pemFile.close(); - // ... + return; } - // Write the public key to the memory BIO - int result = PEM_write_bio_PUBKEY(memBio, evpKey); - if (result != 1) - { - // Handle public key writing error - BIO_free(memBio); - EVP_PKEY_free(evpKey); - BIO_free(bio); - pemFile.close(); - // ... - } - - // Get the pointer to the memory BIO data and its length - char* bioData; - long bioDataLength = BIO_get_mem_data(memBio, &bioData); - - // Copy the public key data to the vector - publicKey.assign(bioData, bioData + bioDataLength); - - // Free resources - BIO_free(memBio); + // Get the EC key from the EVP_PKEY object + EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(evpKey); EVP_PKEY_free(evpKey); - BIO_free(bio); - pemFile.close(); + + if (ecKey == nullptr) + { + std::cout << "OpenSSL: Failed to get the EC key from file " << filePath << ". Aborting import" << std::endl; + return; + } + + // Get the EC group from the EC key + const EC_GROUP* ecGroup = EC_KEY_get0_group(ecKey); + + if (ecGroup == nullptr) + { + std::cout << "OpenSSL: Failed to extract the EC group from file " << filePath << ". Aborting import" << std::endl; + EC_KEY_free(ecKey); + return; + } + + // Check if it is ECDSA P-256 + if (EC_GROUP_get_curve_name(ecGroup) != NID_X9_62_prime256v1) + { + std::cerr << "Invalid curve name in file " << filePath << ". Expected P-256. Aborting import" << std::endl; + EC_KEY_free(ecKey); + return; + } + // Convert the EC parameters to an octet string (raw binary) + // size_t octetSize = i2o_ECPublicKey(ecKey, nullptr); + // std::vector ecParameters(octetSize); + // unsigned char* p = ecParameters.data(); + // i2o_ECPublicKey(ecKey, &p); + std::vector ecParameters(EC_GROUP_get_degree(ecGroup) / 8); + EC_POINT_point2oct(ecGroup, EC_KEY_get0_public_key(ecKey), POINT_CONVERSION_UNCOMPRESSED, ecParameters.data(), ecParameters.size(), nullptr); + + // Get the EC public key from the EC key + const EC_POINT* ecPoint = EC_KEY_get0_public_key(ecKey); + + // Convert the EC public key to an octet string (raw binary) + size_t pointSize = EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); + publicKey = std::vector(pointSize); + EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), pointSize, nullptr); + size_t octetSize = i2o_ECPublicKey(ecKey, nullptr); + std::vector publicKey2(octetSize); + unsigned char* p2 = publicKey2.data(); + i2o_ECPublicKey(ecKey, &p2); + // Clean up the EC key + EC_KEY_free(ecKey); + + std::cout << "EC parameters (size: " << ecParameters.size() << "):"; + for (auto k : ecParameters) + { + std::cout << " " << static_cast(k); + } + std::cout << std::endl; + std::cout << "Public Key (size: " << publicKey.size() << "):"; + for (auto k : publicKey) + { + std::cout << " " << static_cast(k); + } + std::cout << "Public Key2: (size: " << publicKey2.size() << "):"; + for (auto k : publicKey2) + { + std::cout << " " << static_cast(k); + } + std::cout << std::endl; #else - // Read the PEM file contents into a string - std::string pemContents((std::istreambuf_iterator(pemFile)), std::istreambuf_iterator()); - - // Create a BIO object to hold the PEM data - BIO* bio = BIO_new_mem_buf(pemContents.c_str(), -1); - // Load the public key from the BIO - RSA* rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr); + EC_KEY* ecKeyPublic = PEM_read_bio_EC_PUBKEY(bio, nullptr, nullptr, nullptr); BIO_free(bio); - - if (rsa == nullptr) + if (ecKeyPublic == nullptr) { - // Handle error reading public key - return {}; + std::cerr << "OpenSSL: error reading the Public Key from file " << filePath << ". Aborting import" << std::endl; + return; } - // Get the RSA modulus and convert it to a vector of uint8_t - const BIGNUM* rsaModulus = nullptr; - RSA_get0_key(rsa, &rsaModulus, nullptr, nullptr); + // // Get the EC group and EC point from the EC key + const EC_GROUP* ecGroup = EC_KEY_get0_group(ecKeyPublic); + const EC_POINT* ecPoint = EC_KEY_get0_public_key(ecKeyPublic); + // Convert the EC point to an octet string (raw binary) + const size_t octetSize = EC_POINT_point2oct( + ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); + publicKey = std::vector(octetSize); + EC_POINT_point2oct( + ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), octetSize, nullptr); + EC_KEY_free(ecKeyPublic); - BN_bn2bin(rsaModulus, publicKey.data()); - - // Clean up the RSA object - RSA_free(rsa); + std::cout << "Public Key:"; + for (auto k : publicKey) + { + std::cout << " " << static_cast(k); + } + std::cout << std::endl; #endif #else - // Read the contents of the .pem file into a string - std::string pemContents((std::istreambuf_iterator(pemFile)), std::istreambuf_iterator()); - - gnutls_x509_crt_t cert; - gnutls_x509_crt_init(&cert); - - // Import the certificate from the PEM file - gnutls_datum_t pemData; - pemData.data = reinterpret_cast(const_cast(pemContents.data())); - pemData.size = pemContents.size(); - int ret = gnutls_x509_crt_import(cert, &pemData, GNUTLS_X509_FMT_PEM); - if (ret < 0) + // Find the beginning and end of the EC PARAMETERS section + std::size_t beginPos = pemContent.find("-----BEGIN EC PARAMETERS-----"); + std::size_t endPos = pemContent.find("-----END EC PARAMETERS-----"); + if (beginPos == std::string::npos || endPos == std::string::npos) { - std::cerr << "Failed to import certificate from PEM file" << std::endl; - gnutls_x509_crt_deinit(cert); - return publicKey; + std::cerr << "No EC Parameters found in file " << filePath << ". Aborting import" << std::endl; + return; } - // Export the public key data - size_t pubkey_data_size = 0; - ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, nullptr, &pubkey_data_size); - if (ret < 0) - { - std::cerr << "Failed to export public key data" << std::endl; - gnutls_x509_crt_deinit(cert); - return publicKey; - } + // Extract the EC parameters data + std::string ecParamsBase64 = pemContent.substr(beginPos + 30, endPos - beginPos - 31); + std::vector ecParameters = base64Decode(ecParamsBase64); - publicKey.resize(pubkey_data_size); - ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, publicKey.data(), &pubkey_data_size); - if (ret < 0) + std::cout << ecParamsBase64 << std::endl; + std::cout << "Size ecParamsBase64 : " << ecParamsBase64.size() << std::endl; + std::cout << "Size EC : " << ecParameters.size() << std::endl; + for (auto k : ecParameters) { - std::cerr << "Failed to export public key data" << std::endl; - gnutls_x509_crt_deinit(cert); - return publicKey; + std::cout << " " << static_cast(k); } + std::cout << std::endl; - gnutls_x509_crt_deinit(cert); + std::size_t beginPos2 = pemContent.find("-----BEGIN PUBLIC KEY-----"); + std::size_t endPos2 = pemContent.find("-----END PUBLIC KEY-----"); + if (beginPos2 == std::string::npos || endPos2 == std::string::npos) + { + std::cout << "No Public Key found in file " << filePath << ". Aborting import" << std::endl; + return; + } + auto PublickeyBase64 = pemContent.substr(beginPos2 + 27, endPos2 - beginPos2 - 28); + auto readpublickey_long = base64Decode(PublickeyBase64); + publicKey = std::vector(readpublickey_long.begin() + 26, readpublickey_long.end()); // ?? + + std::cout << "Public Key (size: " << publicKey.size() << "):" << std::endl; + for (auto k : publicKey) + { + std::cout << " " << static_cast(k); + } + std::cout << std::endl; #endif - return publicKey; + d_PublicKey = publicKey; + std::cout << "Public key successfully read from file " << filePath << std::endl; } -// // bool signature(const std::vector& publicKey, const std::vector& digest, std::vector& signature) -// // { -// // bool success = false; -// // #if USE_OPENSSL_FALLBACK -// // #else -// // gnutls_global_init(); -// // int result = gnutls_pubkey_verify_data(publicKey.data(), GNUTLS_SIGN_ECDSA_SHA256, digest.data, digest.size(), signature.data(), signature.size()); -// // success = (result == GNUTLS_E_SUCCESS); -// // gnutls_global_deinit(); -// // #endif -// // return success; -// // } +// bool signature(const std::vector& publicKey, const std::vector& digest, const std::vector& signature) +// { +// bool success = false; +// #if USE_OPENSSL_FALLBACK +// #else +// gnutls_global_init(); +// int result = gnutls_pubkey_verify_data(publicKey.data(), GNUTLS_SIGN_ECDSA_SHA256, digest.data(), digest.size(), signature.data(), signature.size()); +// success = (result == GNUTLS_E_SUCCESS); +// gnutls_global_deinit(); +// #endif +// return success; +// } // // bool verifyDigitalSignature(const unsigned char* signature, size_t signatureSize, const unsigned char* message, size_t messageSize, gnutls_pubkey_t publicKey) // // { // // int verificationStatus = gnutls_pubkey_verify_data(publicKey, GNUTLS_DIG_SHA256, 0, message, messageSize, signature, signatureSize); // // return verificationStatus == 0; + + +std::vector Gnss_Crypto::base64Decode(const std::string& encoded_string) +{ + const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + uint8_t char_array_4[4]; + uint8_t char_array_3[3]; + std::vector decoded; + + while (in_len-- && (encoded_string[in_] != '=') && + (isalnum(encoded_string[in_]) || (encoded_string[in_] == '+') || (encoded_string[in_] == '/'))) + { + char_array_4[i++] = encoded_string[in_]; + in_++; + if (i == 4) + { + for (i = 0; i < 4; i++) + { + char_array_4[i] = base64_chars.find(char_array_4[i]); + } + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + { + decoded.push_back(char_array_3[i]); + } + i = 0; + } + } + + if (i) + { + for (j = i; j < 4; j++) + { + char_array_4[j] = 0; + } + + for (j = 0; j < 4; j++) + { + char_array_4[j] = base64_chars.find(char_array_4[j]); + } + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) + { + decoded.push_back(char_array_3[j]); + } + } + + return decoded; +} \ 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 b6151454c..8c4af9a5b 100644 --- a/src/core/system_parameters/gnss_crypto.h +++ b/src/core/system_parameters/gnss_crypto.h @@ -35,14 +35,15 @@ public: Gnss_Crypto() = default; explicit Gnss_Crypto(const std::string& filePath); bool have_public_key() 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; void set_public_key(const std::vector& publickey); - std::vector computeSHA256(const std::vector& input); - std::vector computeSHA3_256(const std::vector& input); - std::vector computeHMAC_SHA_256(const std::vector& key, const std::vector& input); - std::vector computeCMAC_AES(const std::vector& key, const std::vector& input); - std::vector readPublicKeyFromPEM(const std::string& filePath); + void readPublicKeyFromPEM(const std::string& filePath); private: + std::vector base64Decode(const std::string& encoded_string); std::vector d_PublicKey; };