aboutsummaryrefslogtreecommitdiff
path: root/pw_crypto/ecdsa_mbedtls.cc
blob: 92e6258ffb7064e91ef9c3a2cfcd76124be7f116 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#define PW_LOG_MODULE_NAME "ECDSA"
#define PW_LOG_LEVEL PW_LOG_LEVEL_WARN

#include "mbedtls/ecdsa.h"
#include "pw_crypto/ecdsa.h"
#include "pw_function/function.h"
#include "pw_log/log.h"

namespace pw::crypto::ecdsa {

namespace {

// Defer calls a given function upon exiting a scope.
class Defer {
 public:
  Defer(Function<void()>&& callback) : callback_(std::move(callback)) {}
  ~Defer() { callback_(); }

 private:
  Function<void()> callback_;
};

}  // namespace

constexpr size_t kP256CurveOrderBytes = 32;

Status VerifyP256Signature(ConstByteSpan public_key,
                           ConstByteSpan digest,
                           ConstByteSpan signature) {
  // Use a local structure to avoid going over the default inline storage
  // for the `cleanup` callable used below.
  struct {
    // The elliptic curve group.
    mbedtls_ecp_group grp;
    // The public key point.
    mbedtls_ecp_point Q;
    // The signature (r, s).
    mbedtls_mpi r, s;
  } ctx;

  const uint8_t* public_key_data =
      reinterpret_cast<const uint8_t*>(public_key.data());
  const uint8_t* digest_data = reinterpret_cast<const uint8_t*>(digest.data());
  const uint8_t* signature_data =
      reinterpret_cast<const uint8_t*>(signature.data());

  // These init functions never fail.
  mbedtls_ecp_group_init(&ctx.grp);
  mbedtls_ecp_point_init(&ctx.Q);
  mbedtls_mpi_init(&ctx.r);
  mbedtls_mpi_init(&ctx.s);

  // Auto clean up on exit.
  Defer cleanup([&ctx](void) {
    mbedtls_ecp_group_free(&ctx.grp);
    mbedtls_ecp_point_free(&ctx.Q);
    mbedtls_mpi_free(&ctx.r);
    mbedtls_mpi_free(&ctx.s);
  });

  // Load the curve parameters.
  if (mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_SECP256R1)) {
    return Status::Internal();
  }

  // Load the public key.
  if (mbedtls_ecp_point_read_binary(
          &ctx.grp, &ctx.Q, public_key_data, public_key.size())) {
    PW_LOG_DEBUG("Bad public key format");
    return Status::InvalidArgument();
  }

  // Load the signature.
  if (signature.size() != kP256CurveOrderBytes * 2) {
    PW_LOG_DEBUG("Bad signature format");
    return Status::InvalidArgument();
  }

  if (mbedtls_mpi_read_binary(&ctx.r, signature_data, kP256CurveOrderBytes) ||
      mbedtls_mpi_read_binary(&ctx.s,
                              signature_data + kP256CurveOrderBytes,
                              kP256CurveOrderBytes)) {
    return Status::Internal();
  }

  // Digest must be 32 bytes or longer (and be truncated).
  if (digest.size() < kP256CurveOrderBytes) {
    PW_LOG_DEBUG("Digest is too short");
    return Status::InvalidArgument();
  }

  // Verify the signature.
  if (mbedtls_ecdsa_verify(
          &ctx.grp, digest_data, digest.size(), &ctx.Q, &ctx.r, &ctx.s)) {
    PW_LOG_DEBUG("Signature verification failed");
    return Status::Unauthenticated();
  }

  return OkStatus();
}

}  // namespace pw::crypto::ecdsa