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