| // Copyright 2022 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <openssl/bn.h> |
| #include <openssl/ec.h> |
| #include <openssl/evp.h> |
| #include <openssl/sha.h> |
| |
| #include <cerrno> |
| #include <iomanip> |
| #include <iostream> |
| #include <memory> |
| #include <queue> |
| #include <sstream> |
| #include <string> |
| |
| #include "fakes.h" |
| #include "nearby_platform_se.h" |
| |
| static unsigned int random_value = 0; |
| static std::queue<uint8_t> random_sequence; |
| |
| static std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)> anti_spoofing_key( |
| NULL, EVP_PKEY_free); |
| |
| static std::string ArrayToString(const uint8_t *data, size_t length) { |
| std::stringstream output; |
| output << "0x" << std::hex << std::setfill('0') << std::setw(2); |
| for (int i = 0; i < length; i++) { |
| output << (unsigned)data[i]; |
| } |
| return output.str(); |
| } |
| |
| // Generates a random number. |
| uint8_t nearby_platform_Rand() { |
| if (random_sequence.empty()) { |
| return random_value; |
| } else { |
| uint8_t v = random_sequence.front(); |
| random_sequence.pop(); |
| return v; |
| } |
| } |
| |
| #ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA |
| static SHA256_CTX sha256_context; |
| |
| nearby_platform_status nearby_platform_Sha256Start() { |
| SHA256_Init(&sha256_context); |
| return kNearbyStatusOK; |
| } |
| |
| nearby_platform_status nearby_platform_Sha256Update(const void *data, |
| size_t length) { |
| SHA256_Update(&sha256_context, data, length); |
| return kNearbyStatusOK; |
| } |
| |
| nearby_platform_status nearby_platform_Sha256Finish(uint8_t out[32]) { |
| SHA256_Final(out, &sha256_context); |
| return kNearbyStatusOK; |
| } |
| #endif /* NEARBY_FP_ENABLE_ADDITIONAL_DATA */ |
| |
| // Encrypts a data block with AES128 in ECB mode. |
| nearby_platform_status nearby_platform_Aes128Encrypt(const uint8_t input[16], |
| uint8_t output[16], |
| const uint8_t key[16]) { |
| EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); |
| int input_length = 16; |
| int output_length = 16; |
| |
| EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, NULL); |
| |
| if (1 != |
| EVP_EncryptUpdate(ctx, output, &output_length, input, input_length)) { |
| return kNearbyStatusError; |
| } |
| |
| EVP_CIPHER_CTX_free(ctx); |
| return kNearbyStatusOK; |
| } |
| |
| // Encrypts a data block with AES128 in ECB mode. |
| nearby_platform_status nearby_platform_Aes128Decrypt(const uint8_t input[16], |
| uint8_t output[16], |
| const uint8_t key[16]) { |
| EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); |
| int input_length = 16; |
| int output_length = 16; |
| |
| EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, NULL); |
| |
| if (1 != |
| EVP_DecryptUpdate(ctx, output, &output_length, input, input_length)) { |
| return kNearbyStatusError; |
| } |
| |
| EVP_CIPHER_CTX_free(ctx); |
| return kNearbyStatusOK; |
| } |
| |
| static EC_POINT *load_public_key(const uint8_t public_key[64]) { |
| BN_CTX *bn_ctx; |
| EC_KEY *key; |
| EC_POINT *point; |
| const EC_GROUP *group; |
| uint8_t oct_key[65]; |
| |
| oct_key[0] = 0x04; |
| memcpy(oct_key + 1, public_key, 64); |
| bn_ctx = BN_CTX_new(); |
| key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); |
| group = EC_KEY_get0_group(key); |
| point = EC_POINT_new(group); |
| if (1 != EC_POINT_oct2point(group, point, oct_key, sizeof(oct_key), bn_ctx)) |
| return NULL; |
| BN_CTX_free(bn_ctx); |
| EC_KEY_free(key); |
| return point; |
| } |
| |
| // Generates a shared sec256p1 secret using remote party public key and this |
| // device's private key. |
| nearby_platform_status nearby_platform_GenSec256r1Secret( |
| const uint8_t remote_party_public_key[64], uint8_t secret[32]) { |
| EVP_PKEY_CTX *ctx; |
| EVP_PKEY *peerkey; |
| EC_POINT *peer_point; |
| EC_KEY *ec_peer_key; |
| size_t secret_len; |
| |
| /* Create the context for the shared secret derivation */ |
| if (NULL == (ctx = EVP_PKEY_CTX_new(anti_spoofing_key.get(), NULL))) |
| return kNearbyStatusError; |
| |
| /* Initialise */ |
| if (1 != EVP_PKEY_derive_init(ctx)) return kNearbyStatusError; |
| |
| if (NULL == (peer_point = load_public_key(remote_party_public_key))) |
| return kNearbyStatusError; |
| |
| ec_peer_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); |
| |
| if (1 != EC_KEY_set_public_key(ec_peer_key, peer_point)) |
| return kNearbyStatusError; |
| |
| peerkey = EVP_PKEY_new(); |
| if (1 != EVP_PKEY_assign_EC_KEY(peerkey, ec_peer_key)) |
| return kNearbyStatusError; |
| |
| /* Provide the peer public key */ |
| if (1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) return kNearbyStatusError; |
| |
| /* Determine buffer length for shared secret */ |
| if (1 != EVP_PKEY_derive(ctx, NULL, &secret_len)) return kNearbyStatusError; |
| |
| if (secret_len != 32) return kNearbyStatusError; |
| |
| /* Derive the shared secret */ |
| if (1 != (EVP_PKEY_derive(ctx, secret, &secret_len))) |
| return kNearbyStatusError; |
| |
| EVP_PKEY_CTX_free(ctx); |
| EC_POINT_free(peer_point); |
| EVP_PKEY_free(peerkey); |
| |
| std::cout << "Secret: " << ArrayToString(secret, 32) << std::endl; |
| return kNearbyStatusOK; |
| } |
| |
| // Initializes secure element module |
| nearby_platform_status nearby_platform_SecureElementInit() { |
| random_value = 0; |
| random_sequence = std::queue<uint8_t>(); |
| return kNearbyStatusOK; |
| } |
| |
| void nearby_test_fakes_SetRandomNumber(unsigned int value) { |
| random_value = value; |
| } |
| |
| void nearby_test_fakes_SetRandomNumberSequence(std::vector<uint8_t> &value) { |
| for (auto &v : value) random_sequence.push(v); |
| } |
| |
| static BIGNUM *load_private_key(const uint8_t private_key[32]) { |
| uint8_t buffer[37]; |
| buffer[0] = buffer[1] = buffer[2] = 0; |
| buffer[3] = 33; |
| buffer[4] = 0; |
| memcpy(buffer + 5, private_key, 32); |
| return BN_mpi2bn(buffer, sizeof(buffer), NULL); |
| } |
| |
| nearby_platform_status nearby_test_fakes_SetAntiSpoofingKey( |
| const uint8_t private_key[32], const uint8_t public_key[64]) { |
| EC_KEY *key; |
| BIGNUM *prv; |
| EC_POINT *pub; |
| |
| prv = load_private_key(private_key); |
| if (prv == NULL) return kNearbyStatusError; |
| pub = load_public_key(public_key); |
| if (pub == NULL) return kNearbyStatusError; |
| |
| key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); |
| |
| if (1 != EC_KEY_set_private_key(key, prv)) return kNearbyStatusError; |
| if (1 != EC_KEY_set_public_key(key, pub)) return kNearbyStatusError; |
| |
| anti_spoofing_key.reset(EVP_PKEY_new()); |
| if (1 != EVP_PKEY_assign_EC_KEY(anti_spoofing_key.get(), key)) |
| return kNearbyStatusError; |
| BN_free(prv); |
| EC_POINT_free(pub); |
| return kNearbyStatusOK; |
| } |
| |
| nearby_platform_status nearby_test_fakes_GenSec256r1Secret( |
| const uint8_t remote_party_public_key[64], uint8_t secret[32]) { |
| return nearby_platform_GenSec256r1Secret(remote_party_public_key, secret); |
| } |
| |
| nearby_platform_status nearby_test_fakes_Aes128Decrypt( |
| const uint8_t input[AES_MESSAGE_SIZE_BYTES], |
| uint8_t output[AES_MESSAGE_SIZE_BYTES], |
| const uint8_t key[AES_MESSAGE_SIZE_BYTES]) { |
| return nearby_platform_Aes128Decrypt(input, output, key); |
| } |
| |
| nearby_platform_status nearby_test_fakes_Aes128Encrypt( |
| const uint8_t input[AES_MESSAGE_SIZE_BYTES], |
| uint8_t output[AES_MESSAGE_SIZE_BYTES], |
| const uint8_t key[AES_MESSAGE_SIZE_BYTES]) { |
| return nearby_platform_Aes128Encrypt(input, output, key); |
| } |