• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #pragma clang diagnostic ignored "-Wcast-qual"
16 
17 #include "pw_bluetooth_sapphire/internal/host/sm/ecdh_key.h"
18 
19 #include <openssl/ec_key.h>
20 
21 #include <algorithm>
22 #include <cstddef>
23 #include <memory>
24 
25 #include "openssl/base.h"
26 #include "openssl/bn.h"
27 #include "openssl/ec.h"
28 #include "openssl/ecdh.h"
29 #include "openssl/nid.h"
30 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
31 #include "pw_bluetooth_sapphire/internal/host/common/uint256.h"
32 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
33 
34 namespace bt::sm {
35 
ParseFromPublicKey(sm::PairingPublicKeyParams pub_key)36 std::optional<EcdhKey> EcdhKey::ParseFromPublicKey(
37     sm::PairingPublicKeyParams pub_key) {
38   auto new_key = EcdhKey();
39   new_key.key_ = EC_KEY_new_by_curve_name(EC_curve_nist2nid("P-256"));
40   if (!new_key.key_) {
41     return std::nullopt;
42   }
43   BIGNUM x, y;
44   BN_init(&x);
45   BN_init(&y);
46   // Assert on le2bn output. le2bn "returns NULL on allocation failure", but
47   // allocation should never fail on Fuchsia per overcommit semantics.
48   BT_ASSERT(BN_le2bn(pub_key.x, sizeof(pub_key.x), &x));
49   BT_ASSERT(BN_le2bn(pub_key.y, sizeof(pub_key.y), &y));
50 
51   // One potential cause of failure is if pub_key is not a valid ECDH key on the
52   // P-256 curve.
53   int success =
54       (EC_KEY_set_public_key_affine_coordinates(new_key.key_, &x, &y) == 1);
55   BN_free(&x);
56   BN_free(&y);
57   if (success) {
58     return new_key;
59   }
60   return std::nullopt;
61 }
62 
operator =(EcdhKey && other)63 EcdhKey& EcdhKey::operator=(EcdhKey&& other) noexcept {
64   this->key_ = other.key_;
65   other.key_ = nullptr;
66   return *this;
67 }
68 
EcdhKey(EcdhKey && other)69 EcdhKey::EcdhKey(EcdhKey&& other) noexcept : key_(other.key_) {
70   other.key_ = nullptr;
71 }
72 
GetSerializedPublicKey() const73 sm::PairingPublicKeyParams EcdhKey::GetSerializedPublicKey() const {
74   sm::PairingPublicKeyParams params;
75   BIGNUM x, y;
76   BN_init(&x);
77   BN_init(&y);
78   BT_ASSERT(EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key_),
79                                                 EC_KEY_get0_public_key(key_),
80                                                 &x,
81                                                 &y,
82                                                 nullptr) == 1);
83   BT_ASSERT(BN_bn2le_padded(params.x, sizeof(params.x), &x) == 1);
84   BT_ASSERT(BN_bn2le_padded(params.y, sizeof(params.y), &y) == 1);
85   BN_free(&x);
86   BN_free(&y);
87   return params;
88 }
89 
GetPublicKeyX() const90 UInt256 EcdhKey::GetPublicKeyX() const {
91   BIGNUM x;
92   BN_init(&x);
93   bool success =
94       EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key_),
95                                           EC_KEY_get0_public_key(key_),
96                                           &x,
97                                           /*y=*/nullptr,
98                                           /*ctx=*/nullptr) == 1;
99   BT_ASSERT(success);
100   UInt256 out{};
101   success = BN_bn2le_padded(out.data(), out.size(), &x) == 1;
102   BT_ASSERT(success);
103   BN_free(&x);
104   return out;
105 }
106 
GetPublicKeyY() const107 UInt256 EcdhKey::GetPublicKeyY() const {
108   BIGNUM y;
109   BN_init(&y);
110   bool success =
111       EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key_),
112                                           EC_KEY_get0_public_key(key_),
113                                           /*x=*/nullptr,
114                                           &y,
115                                           /*ctx=*/nullptr) == 1;
116   BT_ASSERT(success);
117   UInt256 out{};
118   success = BN_bn2le_padded(out.data(), out.size(), &y) == 1;
119   BT_ASSERT(success);
120   BN_free(&y);
121   return out;
122 }
123 
EcdhKey()124 EcdhKey::EcdhKey() : key_(nullptr) {}
125 
~EcdhKey()126 EcdhKey::~EcdhKey() {
127   if (key_) {
128     EC_KEY_free(key_);
129   }
130 }
131 
LocalEcdhKey()132 LocalEcdhKey::LocalEcdhKey() : EcdhKey() {}
133 
LocalEcdhKey(LocalEcdhKey && other)134 LocalEcdhKey::LocalEcdhKey(LocalEcdhKey&& other) noexcept
135     : EcdhKey(std::move(other)) {}
136 
operator =(LocalEcdhKey && other)137 LocalEcdhKey& LocalEcdhKey::operator=(LocalEcdhKey&& other) noexcept {
138   EcdhKey::operator=(std::move(other));
139   return *this;
140 }
141 
Create()142 std::optional<LocalEcdhKey> LocalEcdhKey::Create() {
143   auto new_key = LocalEcdhKey();
144   new_key.set_boringssl_key(
145       EC_KEY_new_by_curve_name(EC_curve_nist2nid("P-256")));
146   if (!new_key.boringssl_key()) {
147     return std::nullopt;
148   }
149   if (EC_KEY_generate_key(new_key.mut_boringssl_key()) != 1) {
150     return std::nullopt;
151   }
152   return new_key;
153 }
154 
CalculateDhKey(const EcdhKey & peer_public_key) const155 UInt256 LocalEcdhKey::CalculateDhKey(const EcdhKey& peer_public_key) const {
156   UInt256 out{0};
157   bool success =
158       ECDH_compute_key(out.data(),
159                        out.size(),
160                        EC_KEY_get0_public_key(peer_public_key.boringssl_key()),
161                        boringssl_key(),
162                        /*kdf=*/nullptr) == out.size();
163   BT_ASSERT(success);
164   std::reverse(out.begin(), out.end());
165   return out;
166 }
167 
SetPrivateKeyForTesting(const UInt256 & private_key)168 void LocalEcdhKey::SetPrivateKeyForTesting(const UInt256& private_key) {
169   BIGNUM pkey;
170   BN_init(&pkey);
171   BN_le2bn(private_key.data(), private_key.size(), &pkey);
172   BT_ASSERT_MSG(EC_KEY_set_private_key(mut_boringssl_key(), &pkey) == 1,
173                 "Could not set private key in test");
174   BN_free(&pkey);
175 }
176 
177 }  // namespace bt::sm
178