/src/rnp/src/lib/crypto/ecdsa.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2017-2024, [Ribose Inc](https://www.ribose.com). |
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 |
7 | | * are met: |
8 | | * 1. Redistributions of source code must retain the above copyright |
9 | | * notice, this list of conditions and the following disclaimer. |
10 | | * 2. Redistributions in binary form must reproduce the above copyright |
11 | | * notice, this list of conditions and the following disclaimer in the |
12 | | * documentation and/or other materials provided with the distribution. |
13 | | * |
14 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
16 | | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS |
18 | | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
19 | | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
20 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
21 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
22 | | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
23 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
24 | | * POSSIBILITY OF SUCH DAMAGE. |
25 | | */ |
26 | | |
27 | | #include "ecdsa.h" |
28 | | #include "utils.h" |
29 | | #include <botan/ffi.h> |
30 | | #include <string.h> |
31 | | #include "botan_utils.hpp" |
32 | | |
33 | | namespace pgp { |
34 | | namespace ecdsa { |
35 | | |
36 | | static bool |
37 | | load_public_key(rnp::botan::Pubkey &pubkey, const ec::Key &keydata) |
38 | 58.4k | { |
39 | 58.4k | auto curve = ec::Curve::get(keydata.curve); |
40 | 58.4k | if (!curve) { |
41 | 0 | RNP_LOG("unknown curve"); |
42 | 0 | return false; |
43 | 0 | } |
44 | 58.4k | if (!keydata.p.size() || (keydata.p[0] != 0x04)) { |
45 | 51 | RNP_LOG("Failed to load public key: %02x", keydata.p[0]); |
46 | 0 | return false; |
47 | 51 | } |
48 | 58.3k | const size_t curve_order = curve->bytes(); |
49 | 58.3k | if (keydata.p.size() != 2 * curve_order + 1) { |
50 | 58 | return false; |
51 | 58 | } |
52 | 58.3k | rnp::bn px(&keydata.p[1], curve_order); |
53 | 58.3k | rnp::bn py(&keydata.p[1] + curve_order, curve_order); |
54 | | |
55 | 58.3k | if (!px || !py) { |
56 | 0 | return false; |
57 | 0 | } |
58 | | |
59 | 58.3k | bool res = !botan_pubkey_load_ecdsa(&pubkey.get(), px.get(), py.get(), curve->botan_name); |
60 | 58.3k | if (!res) { |
61 | 35 | RNP_LOG("failed to load ecdsa %s public key", curve->botan_name); |
62 | 35 | } |
63 | 0 | return res; |
64 | 58.3k | } |
65 | | |
66 | | static bool |
67 | | load_secret_key(rnp::botan::Privkey &seckey, const ec::Key &keydata) |
68 | 0 | { |
69 | 0 | auto curve = ec::Curve::get(keydata.curve); |
70 | 0 | if (!curve) { |
71 | 0 | return false; |
72 | 0 | } |
73 | | |
74 | 0 | rnp::bn x(keydata.x); |
75 | 0 | if (!x) { |
76 | 0 | return false; |
77 | 0 | } |
78 | | |
79 | 0 | bool res = !botan_privkey_load_ecdsa(&seckey.get(), x.get(), curve->botan_name); |
80 | 0 | if (!res) { |
81 | 0 | RNP_LOG("Can't load private %s key", curve->botan_name); |
82 | 0 | } |
83 | 0 | return res; |
84 | 0 | } |
85 | | |
86 | | rnp_result_t |
87 | | validate_key(rnp::RNG &rng, const ec::Key &key, bool secret) |
88 | 0 | { |
89 | 0 | rnp::botan::Pubkey bpkey; |
90 | 0 | if (!load_public_key(bpkey, key) || botan_pubkey_check_key(bpkey.get(), rng.handle(), 0)) { |
91 | 0 | return RNP_ERROR_BAD_PARAMETERS; |
92 | 0 | } |
93 | 0 | if (!secret) { |
94 | 0 | return RNP_SUCCESS; |
95 | 0 | } |
96 | | |
97 | 0 | rnp::botan::Privkey bskey; |
98 | 0 | if (!load_secret_key(bskey, key) || |
99 | 0 | botan_privkey_check_key(bskey.get(), rng.handle(), 0)) { |
100 | 0 | return RNP_ERROR_BAD_PARAMETERS; |
101 | 0 | } |
102 | 0 | return RNP_SUCCESS; |
103 | 0 | } |
104 | | |
105 | | const char * |
106 | | padding_str_for(pgp_hash_alg_t hash_alg) |
107 | 58.2k | { |
108 | 58.2k | switch (hash_alg) { |
109 | 3.78k | case PGP_HASH_MD5: |
110 | 3.78k | return "Raw(MD5)"; |
111 | 2.91k | case PGP_HASH_SHA1: |
112 | 2.91k | return "Raw(SHA-1)"; |
113 | 911 | case PGP_HASH_RIPEMD: |
114 | 911 | return "Raw(RIPEMD-160)"; |
115 | 19.5k | case PGP_HASH_SHA256: |
116 | 19.5k | return "Raw(SHA-256)"; |
117 | 11.1k | case PGP_HASH_SHA384: |
118 | 11.1k | return "Raw(SHA-384)"; |
119 | 16.9k | case PGP_HASH_SHA512: |
120 | 16.9k | return "Raw(SHA-512)"; |
121 | 618 | case PGP_HASH_SHA224: |
122 | 618 | return "Raw(SHA-224)"; |
123 | 531 | case PGP_HASH_SHA3_256: |
124 | 531 | return "Raw(SHA-3(256))"; |
125 | 1.15k | case PGP_HASH_SHA3_512: |
126 | 1.15k | return "Raw(SHA-3(512))"; |
127 | 718 | case PGP_HASH_SM3: |
128 | 718 | return "Raw(SM3)"; |
129 | 0 | default: |
130 | 0 | return "Raw"; |
131 | 58.2k | } |
132 | 58.2k | } |
133 | | |
134 | | rnp_result_t |
135 | | sign(rnp::RNG & rng, |
136 | | ec::Signature & sig, |
137 | | pgp_hash_alg_t hash_alg, |
138 | | const rnp::secure_bytes &hash, |
139 | | const ec::Key & key) |
140 | 0 | { |
141 | 0 | auto curve = ec::Curve::get(key.curve); |
142 | 0 | if (!curve) { |
143 | 0 | return RNP_ERROR_BAD_PARAMETERS; |
144 | 0 | } |
145 | | |
146 | 0 | rnp::botan::Privkey b_key; |
147 | 0 | if (!load_secret_key(b_key, key)) { |
148 | 0 | RNP_LOG("Can't load private key"); |
149 | 0 | return RNP_ERROR_GENERIC; |
150 | 0 | } |
151 | | |
152 | 0 | rnp::botan::op::Sign signer; |
153 | 0 | auto pad = padding_str_for(hash_alg); |
154 | 0 | if (botan_pk_op_sign_create(&signer.get(), b_key.get(), pad, 0) || |
155 | 0 | botan_pk_op_sign_update(signer.get(), hash.data(), hash.size())) { |
156 | 0 | return RNP_ERROR_GENERIC; |
157 | 0 | } |
158 | | |
159 | 0 | const size_t curve_order = curve->bytes(); |
160 | 0 | size_t sig_len = 2 * curve_order; |
161 | 0 | std::vector<uint8_t> out_buf(sig_len); |
162 | |
|
163 | 0 | if (botan_pk_op_sign_finish(signer.get(), rng.handle(), out_buf.data(), &sig_len)) { |
164 | 0 | RNP_LOG("Signing failed"); |
165 | 0 | return RNP_ERROR_GENERIC; |
166 | 0 | } |
167 | | |
168 | | // Allocate memory and copy results |
169 | 0 | sig.r.assign(out_buf.data(), curve_order); |
170 | 0 | sig.s.assign(out_buf.data() + curve_order, curve_order); |
171 | 0 | return RNP_SUCCESS; |
172 | 0 | } |
173 | | |
174 | | rnp_result_t |
175 | | verify(const ec::Signature & sig, |
176 | | pgp_hash_alg_t hash_alg, |
177 | | const rnp::secure_bytes &hash, |
178 | | const ec::Key & key) |
179 | 58.5k | { |
180 | 58.5k | auto curve = ec::Curve::get(key.curve); |
181 | 58.5k | if (!curve) { |
182 | 0 | RNP_LOG("unknown curve"); |
183 | 0 | return RNP_ERROR_BAD_PARAMETERS; |
184 | 0 | } |
185 | | |
186 | 58.5k | size_t curve_order = curve->bytes(); |
187 | 58.5k | size_t r_blen = sig.r.size(); |
188 | 58.5k | size_t s_blen = sig.s.size(); |
189 | 58.5k | if ((r_blen > curve_order) || (s_blen > curve_order) || |
190 | 58.5k | (curve_order > MAX_CURVE_BYTELEN)) { |
191 | 123 | return RNP_ERROR_BAD_PARAMETERS; |
192 | 123 | } |
193 | | |
194 | 58.4k | rnp::botan::Pubkey pub; |
195 | 58.4k | if (!load_public_key(pub, key)) { |
196 | 144 | return RNP_ERROR_SIGNATURE_INVALID; |
197 | 144 | } |
198 | | |
199 | 58.2k | rnp::botan::op::Verify verifier; |
200 | 58.2k | auto pad = padding_str_for(hash_alg); |
201 | 58.2k | if (botan_pk_op_verify_create(&verifier.get(), pub.get(), pad, 0) || |
202 | 58.2k | botan_pk_op_verify_update(verifier.get(), hash.data(), hash.size())) { |
203 | 0 | return RNP_ERROR_SIGNATURE_INVALID; |
204 | 0 | } |
205 | | |
206 | 58.2k | std::vector<uint8_t> sign_buf(2 * curve_order, 0); |
207 | | // Both can't fail |
208 | 58.2k | sig.r.copy(sign_buf.data() + curve_order - r_blen); |
209 | 58.2k | sig.s.copy(sign_buf.data() + 2 * curve_order - s_blen); |
210 | | |
211 | 58.2k | if (botan_pk_op_verify_finish(verifier.get(), sign_buf.data(), sign_buf.size())) { |
212 | 53.1k | return RNP_ERROR_SIGNATURE_INVALID; |
213 | 53.1k | } |
214 | 5.17k | return RNP_SUCCESS; |
215 | 58.2k | } |
216 | | } // namespace ecdsa |
217 | | } // namespace pgp |