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