xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/sm/ecdh_key.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 #include "pw_bluetooth_sapphire/internal/host/sm/ecdh_key.h"
16 
17 #include <openssl/ec_key.h>
18 
19 #include <algorithm>
20 #include <cstddef>
21 #include <memory>
22 
23 #include "openssl/base.h"
24 #include "openssl/bn.h"
25 #include "openssl/ec.h"
26 #include "openssl/ecdh.h"
27 #include "openssl/nid.h"
28 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
29 #include "pw_bluetooth_sapphire/internal/host/common/uint256.h"
30 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
31 
32 namespace bt::sm {
33 
ParseFromPublicKey(sm::PairingPublicKeyParams pub_key)34 std::optional<EcdhKey> EcdhKey::ParseFromPublicKey(
35     sm::PairingPublicKeyParams pub_key) {
36   auto new_key = EcdhKey();
37   new_key.key_ = EC_KEY_new_by_curve_name(EC_curve_nist2nid("P-256"));
38   if (!new_key.key_) {
39     return std::nullopt;
40   }
41   BIGNUM x, y;
42   BN_init(&x);
43   BN_init(&y);
44   // Assert on le2bn output. le2bn "returns NULL on allocation failure", but
45   // allocation should never fail on Fuchsia per overcommit semantics.
46   PW_CHECK(BN_le2bn(pub_key.x, sizeof(pub_key.x), &x));
47   PW_CHECK(BN_le2bn(pub_key.y, sizeof(pub_key.y), &y));
48 
49   // One potential cause of failure is if pub_key is not a valid ECDH key on the
50   // P-256 curve.
51   int success =
52       (EC_KEY_set_public_key_affine_coordinates(new_key.key_, &x, &y) == 1);
53   BN_free(&x);
54   BN_free(&y);
55   if (success) {
56     return new_key;
57   }
58   return std::nullopt;
59 }
60 
operator =(EcdhKey && other)61 EcdhKey& EcdhKey::operator=(EcdhKey&& other) noexcept {
62   this->key_ = other.key_;
63   other.key_ = nullptr;
64   return *this;
65 }
66 
EcdhKey(EcdhKey && other)67 EcdhKey::EcdhKey(EcdhKey&& other) noexcept : key_(other.key_) {
68   other.key_ = nullptr;
69 }
70 
GetSerializedPublicKey() const71 sm::PairingPublicKeyParams EcdhKey::GetSerializedPublicKey() const {
72   sm::PairingPublicKeyParams params;
73   BIGNUM x, y;
74   BN_init(&x);
75   BN_init(&y);
76   PW_CHECK(EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key_),
77                                                EC_KEY_get0_public_key(key_),
78                                                &x,
79                                                &y,
80                                                nullptr) == 1);
81   PW_CHECK(BN_bn2le_padded(params.x, sizeof(params.x), &x) == 1);
82   PW_CHECK(BN_bn2le_padded(params.y, sizeof(params.y), &y) == 1);
83   BN_free(&x);
84   BN_free(&y);
85   return params;
86 }
87 
GetPublicKeyX() const88 UInt256 EcdhKey::GetPublicKeyX() const {
89   BIGNUM x;
90   BN_init(&x);
91   bool success =
92       EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key_),
93                                           EC_KEY_get0_public_key(key_),
94                                           &x,
95                                           /*y=*/nullptr,
96                                           /*ctx=*/nullptr) == 1;
97   PW_CHECK(success);
98   UInt256 out{};
99   success = BN_bn2le_padded(out.data(), out.size(), &x) == 1;
100   PW_CHECK(success);
101   BN_free(&x);
102   return out;
103 }
104 
GetPublicKeyY() const105 UInt256 EcdhKey::GetPublicKeyY() const {
106   BIGNUM y;
107   BN_init(&y);
108   bool success =
109       EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key_),
110                                           EC_KEY_get0_public_key(key_),
111                                           /*x=*/nullptr,
112                                           &y,
113                                           /*ctx=*/nullptr) == 1;
114   PW_CHECK(success);
115   UInt256 out{};
116   success = BN_bn2le_padded(out.data(), out.size(), &y) == 1;
117   PW_CHECK(success);
118   BN_free(&y);
119   return out;
120 }
121 
EcdhKey()122 EcdhKey::EcdhKey() : key_(nullptr) {}
123 
~EcdhKey()124 EcdhKey::~EcdhKey() {
125   if (key_) {
126     EC_KEY_free(key_);
127   }
128 }
129 
LocalEcdhKey()130 LocalEcdhKey::LocalEcdhKey() : EcdhKey() {}
131 
LocalEcdhKey(LocalEcdhKey && other)132 LocalEcdhKey::LocalEcdhKey(LocalEcdhKey&& other) noexcept
133     : EcdhKey(std::move(other)) {}
134 
operator =(LocalEcdhKey && other)135 LocalEcdhKey& LocalEcdhKey::operator=(LocalEcdhKey&& other) noexcept {
136   EcdhKey::operator=(std::move(other));
137   return *this;
138 }
139 
Create()140 std::optional<LocalEcdhKey> LocalEcdhKey::Create() {
141   auto new_key = LocalEcdhKey();
142   new_key.set_boringssl_key(
143       EC_KEY_new_by_curve_name(EC_curve_nist2nid("P-256")));
144   if (!new_key.boringssl_key()) {
145     return std::nullopt;
146   }
147   if (EC_KEY_generate_key(new_key.mut_boringssl_key()) != 1) {
148     return std::nullopt;
149   }
150   return new_key;
151 }
152 
CalculateDhKey(const EcdhKey & peer_public_key) const153 UInt256 LocalEcdhKey::CalculateDhKey(const EcdhKey& peer_public_key) const {
154   UInt256 out{0};
155   bool success =
156       ECDH_compute_key(out.data(),
157                        out.size(),
158                        EC_KEY_get0_public_key(peer_public_key.boringssl_key()),
159                        boringssl_key(),
160                        /*kdf=*/nullptr) == out.size();
161   PW_CHECK(success);
162   std::reverse(out.begin(), out.end());
163   return out;
164 }
165 
SetPrivateKeyForTesting(const UInt256 & private_key)166 void LocalEcdhKey::SetPrivateKeyForTesting(const UInt256& private_key) {
167   BIGNUM pkey;
168   BN_init(&pkey);
169   BN_le2bn(private_key.data(), private_key.size(), &pkey);
170   PW_CHECK(EC_KEY_set_private_key(mut_boringssl_key(), &pkey) == 1,
171            "Could not set private key in test");
172   BN_free(&pkey);
173 }
174 
175 }  // namespace bt::sm
176