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