1 /*
2  *  Copyright (c) 2018, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file includes definitions for performing ECDSA signing.
32  */
33 
34 #ifndef ECDSA_HPP_
35 #define ECDSA_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #if OPENTHREAD_CONFIG_ECDSA_ENABLE
40 
41 #include <stdint.h>
42 #include <stdlib.h>
43 
44 #include "common/error.hpp"
45 #include "crypto/sha256.hpp"
46 
47 namespace ot {
48 namespace Crypto {
49 namespace Ecdsa {
50 
51 /**
52  * @addtogroup core-security
53  *
54  * @{
55  *
56  */
57 
58 /**
59  * This class implements ECDSA key-generation, signing, and verification for NIST P-256 curve using SHA-256 hash.
60  *
61  * NIST P-256 curve is also known as 256-bit Random ECP Group (RFC 5114 - 2.6), or secp256r1 (RFC 4492 - Appendix A).
62  *
63  */
64 class P256
65 {
66 public:
67     static constexpr uint16_t kFieldBitLength = 256; ///< Prime field bit length used by the P-256 curve.
68 
69     /**
70      * Max bytes in binary representation of an MPI (multi-precision int).
71      *
72      */
73     static constexpr uint8_t kMpiSize = kFieldBitLength / 8;
74 
75     class PublicKey;
76     class KeyPair;
77 
78     /**
79      * This class represents an ECDSA signature.
80      *
81      * The signature is encoded as the concatenated binary representation of two MPIs `r` and `s` which are calculated
82      * during signing (RFC 6605 - section 4).
83      *
84      */
85     OT_TOOL_PACKED_BEGIN
86     class Signature
87     {
88         friend class KeyPair;
89         friend class PublicKey;
90 
91     public:
92         static constexpr uint8_t kSize = 2 * kMpiSize; ///< Signature size in bytes (two times the curve MPI size).
93 
94         /**
95          * This method returns the signature as a byte array.
96          *
97          * @returns A pointer to the byte array containing the signature.
98          *
99          */
GetBytes(void) const100         const uint8_t *GetBytes(void) const { return mShared.mKey; }
101 
102     private:
103         OT_TOOL_PACKED_BEGIN
104         struct Mpis
105         {
106             uint8_t mR[kMpiSize];
107             uint8_t mS[kMpiSize];
108         } OT_TOOL_PACKED_END;
109 
110         union OT_TOOL_PACKED_FIELD
111         {
112             Mpis    mMpis;
113             uint8_t mKey[kSize];
114         } mShared;
115     } OT_TOOL_PACKED_END;
116 
117     /**
118      * This class represents a key pair (public and private keys).
119      *
120      * The key pair is stored using Distinguished Encoding Rules (DER) format (per RFC 5915).
121      *
122      */
123     class KeyPair
124     {
125     public:
126         /**
127          * Max buffer size (in bytes) for representing the key-pair in DER format.
128          *
129          */
130         static constexpr uint8_t kMaxDerSize = 125;
131 
132         /**
133          * This constructor initializes a `KeyPair` as empty (no key).
134          *
135          */
KeyPair(void)136         KeyPair(void)
137             : mDerLength(0)
138         {
139         }
140 
141         /**
142          * This method generates and populates the `KeyPair` with a new public/private keys.
143          *
144          * @retval kErrorNone         A new key pair was generated successfully.
145          * @retval kErrorNoBufs       Failed to allocate buffer for key generation.
146          * @retval kErrorNotCapable   Feature not supported.
147          * @retval kErrorFailed       Failed to generate key.
148          *
149          */
150         Error Generate(void);
151 
152         /**
153          * This method gets the associated public key from the `KeyPair`.
154          *
155          * @param[out] aPublicKey     A reference to a `PublicKey` to output the value.
156          *
157          * @retval kErrorNone      Public key was retrieved successfully, and @p aPublicKey is updated.
158          * @retval kErrorParse     The key-pair DER format could not be parsed (invalid format).
159          *
160          */
161         Error GetPublicKey(PublicKey &aPublicKey) const;
162 
163         /**
164          * This method gets the pointer to start of the buffer containing the key-pair info in DER format.
165          *
166          * The length (number of bytes) of DER format is given by `GetDerLength()`.
167          *
168          * @returns The pointer to the start of buffer containing the key-pair in DER format.
169          *
170          */
GetDerBytes(void) const171         const uint8_t *GetDerBytes(void) const { return mDerBytes; }
172 
173         /**
174          * This method gets the length of the byte sequence representation of the key-pair in DER format.
175          *
176          * @returns The length of byte sequence representation of the key-pair in DER format.
177          *
178          */
GetDerLength(void) const179         uint8_t GetDerLength(void) const { return mDerLength; }
180 
181         /**
182          * This method gets the pointer to start of the key-pair buffer in DER format.
183          *
184          * This method gives non-const pointer to the buffer and is intended for populating the buffer and setting
185          * the key-pair (e.g., reading the key-pair from non-volatile settings). The buffer contains `kMaxDerSize`
186          * bytes. After populating the buffer, `SetDerLength()` can be used to set the the number of bytes written.
187          *
188          * @returns The pointer to the start of key-pair buffer in DER format.
189          *
190          */
GetDerBytes(void)191         uint8_t *GetDerBytes(void) { return mDerBytes; }
192 
193         /**
194          * This method sets the length of the byte sequence representation of the key-pair in DER format.
195          *
196          * @param[in] aDerLength   The length (number of bytes).
197          *
198          */
SetDerLength(uint8_t aDerLength)199         void SetDerLength(uint8_t aDerLength) { mDerLength = aDerLength; }
200 
201         /**
202          * This method calculates the ECDSA signature for a hashed message using the private key from `KeyPair`.
203          *
204          * This method uses the deterministic digital signature generation procedure from RFC 6979.
205          *
206          * @param[in]  aHash               The SHA-256 hash value of the message to use for signature calculation.
207          * @param[out] aSignature          A reference to a `Signature` to output the calculated signature value.
208          *
209          * @retval kErrorNone           The signature was calculated successfully and @p aSignature was updated.
210          * @retval kErrorParse          The key-pair DER format could not be parsed (invalid format).
211          * @retval kErrorInvalidArgs    The @p aHash is invalid.
212          * @retval kErrorNoBufs         Failed to allocate buffer for signature calculation.
213          *
214          */
215         Error Sign(const Sha256::Hash &aHash, Signature &aSignature) const;
216 
217     private:
218         Error Parse(void *aContext) const;
219 
220         uint8_t mDerBytes[kMaxDerSize];
221         uint8_t mDerLength;
222     };
223 
224     /**
225      * This class represents a public key.
226      *
227      * The public key is stored as a byte sequence representation of an uncompressed curve point (RFC 6605 - sec 4).
228      *
229      */
230     OT_TOOL_PACKED_BEGIN
231     class PublicKey
232     {
233         friend class KeyPair;
234 
235     public:
236         static constexpr uint8_t kSize = kMpiSize * 2; ///< Size of the public key in bytes.
237 
238         /**
239          * This method gets the pointer to the buffer containing the public key (as an uncompressed curve point).
240          *
241          * @return The pointer to the buffer containing the public key (with `kSize` bytes).
242          *
243          */
GetBytes(void) const244         const uint8_t *GetBytes(void) const { return mData; }
245 
246         /**
247          * This method uses the `PublicKey` to verify the ECDSA signature of a hashed message.
248          *
249          * @param[in] aHash                The SHA-256 hash value of a message to use for signature verification.
250          * @param[in] aSignature           The signature value to verify.
251          *
252          * @retval kErrorNone          The signature was verified successfully.
253          * @retval kErrorSecurity      The signature is invalid.
254          * @retval kErrorInvalidArgs   The key or has is invalid.
255          * @retval kErrorNoBufs        Failed to allocate buffer for signature verification
256          *
257          */
258         Error Verify(const Sha256::Hash &aHash, const Signature &aSignature) const;
259 
260     private:
261         uint8_t mData[kSize];
262     } OT_TOOL_PACKED_END;
263 };
264 
265 /**
266  * This function creates an ECDSA signature.
267  *
268  * @param[out]    aOutput            An output buffer where ECDSA sign should be stored.
269  * @param[inout]  aOutputLength      The length of the @p aOutput buffer.
270  * @param[in]     aInputHash         An input hash.
271  * @param[in]     aInputHashLength   The length of the @p aInputHash buffer.
272  * @param[in]     aPrivateKey        A private key in PEM format.
273  * @param[in]     aPrivateKeyLength  The length of the @p aPrivateKey buffer.
274  *
275  * @retval  kErrorNone         ECDSA sign has been created successfully.
276  * @retval  kErrorNoBufs       Output buffer is too small.
277  * @retval  kErrorInvalidArgs  Private key is not valid EC Private Key.
278  * @retval  kErrorFailed       Error during signing.
279  *
280  */
281 Error Sign(uint8_t *      aOutput,
282            uint16_t &     aOutputLength,
283            const uint8_t *aInputHash,
284            uint16_t       aInputHashLength,
285            const uint8_t *aPrivateKey,
286            uint16_t       aPrivateKeyLength);
287 
288 /**
289  * @}
290  *
291  */
292 
293 } // namespace Ecdsa
294 } // namespace Crypto
295 } // namespace ot
296 
297 #endif // OPENTHREAD_CONFIG_ECDSA_ENABLE
298 
299 #endif // ECDSA_HPP_
300