1 // Copyright 2021 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 #define PW_LOG_MODULE_NAME "ECDSA-MTLS"
15 #define PW_LOG_LEVEL PW_LOG_LEVEL_WARN
16
17 #include "mbedtls/ecdsa.h"
18 #include "pw_crypto/ecdsa.h"
19 #include "pw_function/function.h"
20 #include "pw_log/log.h"
21
22 namespace pw::crypto::ecdsa {
23
24 namespace {
25
26 // Defer calls a given function upon exiting a scope.
27 class Defer {
28 public:
Defer(Function<void ()> && callback)29 Defer(Function<void()>&& callback) : callback_(std::move(callback)) {}
~Defer()30 ~Defer() { callback_(); }
31
32 private:
33 Function<void()> callback_;
34 };
35
36 } // namespace
37
38 constexpr size_t kP256CurveOrderBytes = 32;
39
VerifyP256Signature(ConstByteSpan public_key,ConstByteSpan digest,ConstByteSpan signature)40 Status VerifyP256Signature(ConstByteSpan public_key,
41 ConstByteSpan digest,
42 ConstByteSpan signature) {
43 // Use a local structure to avoid going over the default inline storage
44 // for the `cleanup` callable used below.
45 struct {
46 // The elliptic curve group.
47 mbedtls_ecp_group grp;
48 // The public key point.
49 mbedtls_ecp_point Q;
50 // The signature (r, s).
51 mbedtls_mpi r, s;
52 } ctx;
53
54 const uint8_t* public_key_data =
55 reinterpret_cast<const uint8_t*>(public_key.data());
56 const uint8_t* digest_data = reinterpret_cast<const uint8_t*>(digest.data());
57 const uint8_t* signature_data =
58 reinterpret_cast<const uint8_t*>(signature.data());
59
60 // These init functions never fail.
61 mbedtls_ecp_group_init(&ctx.grp);
62 mbedtls_ecp_point_init(&ctx.Q);
63 mbedtls_mpi_init(&ctx.r);
64 mbedtls_mpi_init(&ctx.s);
65
66 // Auto clean up on exit.
67 Defer cleanup([&ctx](void) {
68 mbedtls_ecp_group_free(&ctx.grp);
69 mbedtls_ecp_point_free(&ctx.Q);
70 mbedtls_mpi_free(&ctx.r);
71 mbedtls_mpi_free(&ctx.s);
72 });
73
74 // Load the curve parameters.
75 if (mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_SECP256R1)) {
76 return Status::Internal();
77 }
78
79 // Load the public key.
80 if (mbedtls_ecp_point_read_binary(
81 &ctx.grp, &ctx.Q, public_key_data, public_key.size())) {
82 PW_LOG_DEBUG("Bad public key format");
83 return Status::InvalidArgument();
84 }
85
86 // Load the signature.
87 if (signature.size() != kP256CurveOrderBytes * 2) {
88 PW_LOG_DEBUG("Bad signature format");
89 return Status::InvalidArgument();
90 }
91
92 if (mbedtls_mpi_read_binary(&ctx.r, signature_data, kP256CurveOrderBytes) ||
93 mbedtls_mpi_read_binary(&ctx.s,
94 signature_data + kP256CurveOrderBytes,
95 kP256CurveOrderBytes)) {
96 return Status::Internal();
97 }
98
99 // Digest must be 32 bytes or longer (and be truncated).
100 if (digest.size() < kP256CurveOrderBytes) {
101 PW_LOG_DEBUG("Digest is too short");
102 return Status::InvalidArgument();
103 }
104
105 // Verify the signature.
106 if (mbedtls_ecdsa_verify(
107 &ctx.grp, digest_data, digest.size(), &ctx.Q, &ctx.r, &ctx.s)) {
108 PW_LOG_DEBUG("Signature verification failed");
109 return Status::Unauthenticated();
110 }
111
112 return OkStatus();
113 }
114
115 } // namespace pw::crypto::ecdsa
116