• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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