1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_crypto/ecdsa.h"
16
17 #include <cstring>
18
19 #include "pw_unit_test/framework.h"
20
21 namespace pw::crypto::ecdsa {
22 namespace {
23
24 #define AS_BYTES(s) as_bytes(span(s, sizeof(s) - 1))
25
26 #define ASSERT_FAIL(expr) ASSERT_NE(OkStatus(), expr)
27
28 // TEST_DIGEST/PUBKEY/SIGNATURE are generated using the pkey/ecdsa.c
29 // example in Mbed TLS.
30
31 // The SHA256 digest of "Hello, Pigweed!", 32 bytes.
32 #define TEST_DIGEST \
33 "\x8D\xCE\x14\xEE\x2C\xD9\xFD\x9B\xBD\x8C\x8D\x57\x68\x50\x2C\x2F" \
34 "\xFB\xB3\x52\x36\xCE\x93\x47\x1B\x80\xFC\xA4\x7D\xB5\xF8\x41\x9D"
35
36 // The public key in uncompressed form, 65 bytes.
37 #define TEST_PUBKEY \
38 "\x04" \
39 "\xD1\x82\x2E\x6A\xD2\x4B\x2A\x80\x2E\x8F\xBC\x03\x00\x95\x11\xF9" \
40 "\x81\x24\xA7\x3C\x45\xC8\xBA\xDD\x5F\x77\x1C\xC3\x71\x8B\xB2\xE9" \
41 "\x3A\x0A\x84\xFF\xEA\x13\xC2\x27\xD2\xCF\x42\x7D\xA5\x95\xD6\x88" \
42 "\xCD\x23\x00\x3F\xF9\xD9\x75\x46\xFF\x58\xE9\xBE\xC3\x74\x13\xB8"
43
44 // The ECDSA P256 signature of `DIGEST`.
45 #define TEST_SIGNATURE \
46 "\x16\x54\x43\xD4\x00\x07\xC4\xD7\x26\x2E\x3C\xB1\x65\x54\x00\x6A" \
47 "\x6A\x5B\x4A\xBB\x16\x6F\x44\xD0\x91\x3F\xD3\xC2\x50\xAC\x1A\x87" \
48 "\x86\x41\xEE\x56\xDA\x31\xF2\xFF\x38\x3C\xBB\x32\x3E\x2D\xDB\x98" \
49 "\xEA\x05\x9E\x8F\x91\x8E\x0E\x99\xE5\x4F\x32\x13\x92\x7F\x17\x68"
50
51 // The public key in uncompressed form, missing the header byte.
52 #define MALFORMED_PUBKEY_MISSING_HEADER \
53 "\xD1\x82\x2E\x6A\xD2\x4B\x2A\x80\x2E\x8F\xBC\x03\x00\x95\x11\xF9" \
54 "\x81\x24\xA7\x3C\x45\xC8\xBA\xDD\x5F\x77\x1C\xC3\x71\x8B\xB2\xE9" \
55 "\x3A\x0A\x84\xFF\xEA\x13\xC2\x27\xD2\xCF\x42\x7D\xA5\x95\xD6\x88" \
56 "\xCD\x23\x00\x3F\xF9\xD9\x75\x46\xFF\x58\xE9\xBE\xC3\x74\x13\xB8"
57
58 // The public key in compressed form, wrong the header byte (03 instead of 02).
59 #define MALFORMED_PUBKEY_WRONG_HEADER \
60 "\x03" \
61 "\xD1\x82\x2E\x6A\xD2\x4B\x2A\x80\x2E\x8F\xBC\x03\x00\x95\x11\xF9" \
62 "\x81\x24\xA7\x3C\x45\xC8\xBA\xDD\x5F\x77\x1C\xC3\x71\x8B\xB2\xE9"
63
64 // Tampered signature (first bit flipped).
65 #define TAMPERED_SIGNATURE \
66 "\x17\x54\x43\xD4\x00\x07\xC4\xD7\x26\x2E\x3C\xB1\x65\x54\x00\x6A" \
67 "\x6A\x5B\x4A\xBB\x16\x6F\x44\xD0\x91\x3F\xD3\xC2\x50\xAC\x1A\x87" \
68 "\x86\x41\xEE\x56\xDA\x31\xF2\xFF\x38\x3C\xBB\x32\x3E\x2D\xDB\x98" \
69 "\xEA\x05\x9E\x8F\x91\x8E\x0E\x99\xE5\x4F\x32\x13\x92\x7F\x17\x68"
70
71 // Short signature (last byte removed).
72 #define SHORT_SIGNATURE \
73 "\x16\x54\x43\xD4\x00\x07\xC4\xD7\x26\x2E\x3C\xB1\x65\x54\x00\x6A" \
74 "\x6A\x5B\x4A\xBB\x16\x6F\x44\xD0\x91\x3F\xD3\xC2\x50\xAC\x1A\x87" \
75 "\x86\x41\xEE\x56\xDA\x31\xF2\xFF\x38\x3C\xBB\x32\x3E\x2D\xDB\x98" \
76 "\xEA\x05\x9E\x8F\x91\x8E\x0E\x99\xE5\x4F\x32\x13\x92\x7F\x17"
77
78 // Short digest (last byte removed)
79 #define SHORT_DIGEST \
80 "\x8D\xCE\x14\xEE\x2C\xD9\xFD\x9B\xBD\x8C\x8D\x57\x68\x50\x2C\x2F" \
81 "\xFB\xB3\x52\x36\xCE\x93\x47\x1B\x80\xFC\xA4\x7D\xB5\xF8\x41"
82
83 // Tampered digest (first bit flipped)
84 #define TAMPERED_DIGEST \
85 "\x8C\xCE\x14\xEE\x2C\xD9\xFD\x9B\xBD\x8C\x8D\x57\x68\x50\x2C\x2F" \
86 "\xFB\xB3\x52\x36\xCE\x93\x47\x1B\x80\xFC\xA4\x7D\xB5\xF8\x41\x9D"
87
88 // Tampered public key (last bit flipped).
89 #define TAMPERED_PUBKEY \
90 "\x04" \
91 "\xD1\x82\x2E\x6A\xD2\x4B\x2A\x80\x2E\x8F\xBC\x03\x00\x95\x11\xF9" \
92 "\x81\x24\xA7\x3C\x45\xC8\xBA\xDD\x5F\x77\x1C\xC3\x71\x8B\xB2\xE9" \
93 "\x3A\x0A\x84\xFF\xEA\x13\xC2\x27\xD2\xCF\x42\x7D\xA5\x95\xD6\x88" \
94 "\xCD\x23\x00\x3F\xF9\xD9\x75\x46\xFF\x58\xE9\xBE\xC3\x74\x13\xB9"
95
TEST(EcdsaP256,ValidSignature)96 TEST(EcdsaP256, ValidSignature) {
97 PW_TEST_ASSERT_OK(VerifyP256Signature(
98 AS_BYTES(TEST_PUBKEY), AS_BYTES(TEST_DIGEST), AS_BYTES(TEST_SIGNATURE)));
99 }
100
TEST(EcdsaP256,LongerDigestGetsTruncated)101 TEST(EcdsaP256, LongerDigestGetsTruncated) {
102 PW_TEST_ASSERT_OK(VerifyP256Signature(AS_BYTES(TEST_PUBKEY),
103 AS_BYTES(TEST_DIGEST "extra stuff"),
104 AS_BYTES(TEST_SIGNATURE)));
105 }
106
TEST(EcdsaP256,MalformedPublicKeyMissingHeader)107 TEST(EcdsaP256, MalformedPublicKeyMissingHeader) {
108 ASSERT_EQ(Status::InvalidArgument(),
109 VerifyP256Signature(AS_BYTES(MALFORMED_PUBKEY_MISSING_HEADER),
110 AS_BYTES(TEST_DIGEST),
111 AS_BYTES(TEST_SIGNATURE)));
112 }
113
TEST(EcdsaP256,MalformedPublicKeyWrongHeader)114 TEST(EcdsaP256, MalformedPublicKeyWrongHeader) {
115 ASSERT_FAIL(VerifyP256Signature(AS_BYTES(MALFORMED_PUBKEY_WRONG_HEADER),
116 AS_BYTES(TEST_DIGEST),
117 AS_BYTES(TEST_SIGNATURE)));
118 }
119
TEST(EcdsaP256,TamperedSignature)120 TEST(EcdsaP256, TamperedSignature) {
121 ASSERT_EQ(Status::Unauthenticated(),
122 VerifyP256Signature(AS_BYTES(TEST_PUBKEY),
123 AS_BYTES(TEST_DIGEST),
124 AS_BYTES(TAMPERED_SIGNATURE)));
125 }
126
TEST(EcdsaP256,SignatureTooLong)127 TEST(EcdsaP256, SignatureTooLong) {
128 ASSERT_EQ(Status::InvalidArgument(),
129 VerifyP256Signature(AS_BYTES(TEST_PUBKEY),
130 AS_BYTES(TEST_DIGEST),
131 AS_BYTES(TEST_SIGNATURE "extra stuff")));
132 }
133
TEST(EcdsaP256,SignatureTooShort)134 TEST(EcdsaP256, SignatureTooShort) {
135 ASSERT_EQ(Status::InvalidArgument(),
136 VerifyP256Signature(AS_BYTES(TEST_PUBKEY),
137 AS_BYTES(TEST_DIGEST),
138 AS_BYTES(SHORT_SIGNATURE)));
139 }
140
TEST(EcdsaP256,DigestTooShort)141 TEST(EcdsaP256, DigestTooShort) {
142 ASSERT_EQ(Status::InvalidArgument(),
143 VerifyP256Signature(AS_BYTES(TEST_PUBKEY),
144 AS_BYTES(SHORT_DIGEST),
145 AS_BYTES(TEST_SIGNATURE)));
146 }
147
TEST(EcdsaP256,TamperedDigest)148 TEST(EcdsaP256, TamperedDigest) {
149 ASSERT_EQ(Status::Unauthenticated(),
150 VerifyP256Signature(AS_BYTES(TEST_PUBKEY),
151 AS_BYTES(TAMPERED_DIGEST),
152 AS_BYTES(TEST_SIGNATURE)));
153 }
154
TEST(EcdsaP256,TamperedPubkey)155 TEST(EcdsaP256, TamperedPubkey) {
156 ASSERT_FAIL(VerifyP256Signature(AS_BYTES(TAMPERED_PUBKEY),
157 AS_BYTES(TEST_DIGEST),
158 AS_BYTES(TEST_SIGNATURE)));
159 }
160
161 } // namespace
162 } // namespace pw::crypto::ecdsa
163