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