blob: cd7852af5396dd27d636493c65705b746dc030b6 [file]
// 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-MTLS"
#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"
#include "pw_span/cast.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;
auto public_key_u8 = span_cast<uint8_t>(public_key);
auto digest_u8 = span_cast<uint8_t>(digest);
auto signature_u8 = span_cast<uint8_t>(signature);
// 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_u8.data(), public_key_u8.size())) {
PW_LOG_DEBUG("Bad public key format");
return Status::InvalidArgument();
}
// Load the signature.
if (signature_u8.size() != kP256CurveOrderBytes * 2) {
PW_LOG_DEBUG("Bad signature format");
return Status::InvalidArgument();
}
if (mbedtls_mpi_read_binary(
&ctx.r, signature_u8.data(), kP256CurveOrderBytes) ||
mbedtls_mpi_read_binary(&ctx.s,
signature_u8.subspan(kP256CurveOrderBytes).data(),
kP256CurveOrderBytes)) {
return Status::Internal();
}
// Digest must be 32 bytes or longer (and be truncated).
if (digest_u8.size() < kP256CurveOrderBytes) {
PW_LOG_DEBUG("Digest is too short");
return Status::InvalidArgument();
}
// Verify the signature.
if (mbedtls_ecdsa_verify(&ctx.grp,
digest_u8.data(),
digest_u8.size(),
&ctx.Q,
&ctx.r,
&ctx.s)) {
PW_LOG_DEBUG("Signature verification failed");
return Status::Unauthenticated();
}
return OkStatus();
}
} // namespace pw::crypto::ecdsa