blob: 98b439b814ddbdf9e16d8a1c8dd6840ea0f89136 [file] [log] [blame]
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for performing ECDSA signing.
*/
#ifndef ECDSA_HPP_
#define ECDSA_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_ECDSA_ENABLE
#include <stdint.h>
#include <stdlib.h>
#include "common/error.hpp"
#include "crypto/sha256.hpp"
namespace ot {
namespace Crypto {
namespace Ecdsa {
/**
* @addtogroup core-security
*
* @{
*
*/
/**
* This class implements ECDSA key-generation, signing, and verification for NIST P-256 curve using SHA-256 hash.
*
* NIST P-256 curve is also known as 256-bit Random ECP Group (RFC 5114 - 2.6), or secp256r1 (RFC 4492 - Appendix A).
*
*/
class P256
{
public:
static constexpr uint16_t kFieldBitLength = 256; ///< Prime field bit length used by the P-256 curve.
/**
* Max bytes in binary representation of an MPI (multi-precision int).
*
*/
static constexpr uint8_t kMpiSize = kFieldBitLength / 8;
class PublicKey;
class KeyPair;
/**
* This class represents an ECDSA signature.
*
* The signature is encoded as the concatenated binary representation of two MPIs `r` and `s` which are calculated
* during signing (RFC 6605 - section 4).
*
*/
OT_TOOL_PACKED_BEGIN
class Signature
{
friend class KeyPair;
friend class PublicKey;
public:
static constexpr uint8_t kSize = 2 * kMpiSize; ///< Signature size in bytes (two times the curve MPI size).
/**
* This method returns the signature as a byte array.
*
* @returns A pointer to the byte array containing the signature.
*
*/
const uint8_t *GetBytes(void) const { return mShared.mKey; }
private:
OT_TOOL_PACKED_BEGIN
struct Mpis
{
uint8_t mR[kMpiSize];
uint8_t mS[kMpiSize];
} OT_TOOL_PACKED_END;
union OT_TOOL_PACKED_FIELD
{
Mpis mMpis;
uint8_t mKey[kSize];
} mShared;
} OT_TOOL_PACKED_END;
/**
* This class represents a key pair (public and private keys).
*
* The key pair is stored using Distinguished Encoding Rules (DER) format (per RFC 5915).
*
*/
class KeyPair
{
public:
/**
* Max buffer size (in bytes) for representing the key-pair in DER format.
*
*/
static constexpr uint8_t kMaxDerSize = 125;
/**
* This constructor initializes a `KeyPair` as empty (no key).
*
*/
KeyPair(void)
: mDerLength(0)
{
}
/**
* This method generates and populates the `KeyPair` with a new public/private keys.
*
* @retval kErrorNone A new key pair was generated successfully.
* @retval kErrorNoBufs Failed to allocate buffer for key generation.
* @retval kErrorNotCapable Feature not supported.
* @retval kErrorFailed Failed to generate key.
*
*/
Error Generate(void);
/**
* This method gets the associated public key from the `KeyPair`.
*
* @param[out] aPublicKey A reference to a `PublicKey` to output the value.
*
* @retval kErrorNone Public key was retrieved successfully, and @p aPublicKey is updated.
* @retval kErrorParse The key-pair DER format could not be parsed (invalid format).
*
*/
Error GetPublicKey(PublicKey &aPublicKey) const;
/**
* This method gets the pointer to start of the buffer containing the key-pair info in DER format.
*
* The length (number of bytes) of DER format is given by `GetDerLength()`.
*
* @returns The pointer to the start of buffer containing the key-pair in DER format.
*
*/
const uint8_t *GetDerBytes(void) const { return mDerBytes; }
/**
* This method gets the length of the byte sequence representation of the key-pair in DER format.
*
* @returns The length of byte sequence representation of the key-pair in DER format.
*
*/
uint8_t GetDerLength(void) const { return mDerLength; }
/**
* This method gets the pointer to start of the key-pair buffer in DER format.
*
* This method gives non-const pointer to the buffer and is intended for populating the buffer and setting
* the key-pair (e.g., reading the key-pair from non-volatile settings). The buffer contains `kMaxDerSize`
* bytes. After populating the buffer, `SetDerLength()` can be used to set the the number of bytes written.
*
* @returns The pointer to the start of key-pair buffer in DER format.
*
*/
uint8_t *GetDerBytes(void) { return mDerBytes; }
/**
* This method sets the length of the byte sequence representation of the key-pair in DER format.
*
* @param[in] aDerLength The length (number of bytes).
*
*/
void SetDerLength(uint8_t aDerLength) { mDerLength = aDerLength; }
/**
* This method calculates the ECDSA signature for a hashed message using the private key from `KeyPair`.
*
* This method uses the deterministic digital signature generation procedure from RFC 6979.
*
* @param[in] aHash The SHA-256 hash value of the message to use for signature calculation.
* @param[out] aSignature A reference to a `Signature` to output the calculated signature value.
*
* @retval kErrorNone The signature was calculated successfully and @p aSignature was updated.
* @retval kErrorParse The key-pair DER format could not be parsed (invalid format).
* @retval kErrorInvalidArgs The @p aHash is invalid.
* @retval kErrorNoBufs Failed to allocate buffer for signature calculation.
*
*/
Error Sign(const Sha256::Hash &aHash, Signature &aSignature) const;
private:
Error Parse(void *aContext) const;
uint8_t mDerBytes[kMaxDerSize];
uint8_t mDerLength;
};
/**
* This class represents a public key.
*
* The public key is stored as a byte sequence representation of an uncompressed curve point (RFC 6605 - sec 4).
*
*/
OT_TOOL_PACKED_BEGIN
class PublicKey : public Equatable<PublicKey>
{
friend class KeyPair;
public:
static constexpr uint8_t kSize = kMpiSize * 2; ///< Size of the public key in bytes.
/**
* This method gets the pointer to the buffer containing the public key (as an uncompressed curve point).
*
* @return The pointer to the buffer containing the public key (with `kSize` bytes).
*
*/
const uint8_t *GetBytes(void) const { return mData; }
/**
* This method uses the `PublicKey` to verify the ECDSA signature of a hashed message.
*
* @param[in] aHash The SHA-256 hash value of a message to use for signature verification.
* @param[in] aSignature The signature value to verify.
*
* @retval kErrorNone The signature was verified successfully.
* @retval kErrorSecurity The signature is invalid.
* @retval kErrorInvalidArgs The key or has is invalid.
* @retval kErrorNoBufs Failed to allocate buffer for signature verification
*
*/
Error Verify(const Sha256::Hash &aHash, const Signature &aSignature) const;
private:
uint8_t mData[kSize];
} OT_TOOL_PACKED_END;
};
/**
* This function creates an ECDSA signature.
*
* @param[out] aOutput An output buffer where ECDSA sign should be stored.
* @param[in,out] aOutputLength The length of the @p aOutput buffer.
* @param[in] aInputHash An input hash.
* @param[in] aInputHashLength The length of the @p aInputHash buffer.
* @param[in] aPrivateKey A private key in PEM format.
* @param[in] aPrivateKeyLength The length of the @p aPrivateKey buffer.
*
* @retval kErrorNone ECDSA sign has been created successfully.
* @retval kErrorNoBufs Output buffer is too small.
* @retval kErrorInvalidArgs Private key is not valid EC Private Key.
* @retval kErrorFailed Error during signing.
*
*/
Error Sign(uint8_t * aOutput,
uint16_t & aOutputLength,
const uint8_t *aInputHash,
uint16_t aInputHashLength,
const uint8_t *aPrivateKey,
uint16_t aPrivateKeyLength);
/**
* @}
*
*/
} // namespace Ecdsa
} // namespace Crypto
} // namespace ot
#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE
#endif // ECDSA_HPP_