// 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" 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