/src/rnp/src/lib/crypto/eddsa.cpp
Line | Count | Source |
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 <string.h> |
28 | | #include <cassert> |
29 | | #include <botan/ffi.h> |
30 | | #include "eddsa.h" |
31 | | #include "utils.h" |
32 | | #include "botan_utils.hpp" |
33 | | |
34 | | namespace pgp { |
35 | | namespace eddsa { |
36 | | |
37 | | static bool |
38 | | load_public_key(rnp::botan::Pubkey &pubkey, const ec::Key &keydata) |
39 | 79.9k | { |
40 | 79.9k | if (keydata.curve != PGP_CURVE_ED25519) { |
41 | 32 | return false; |
42 | 32 | } |
43 | | /* |
44 | | * See draft-ietf-openpgp-rfc4880bis-01 section 13.3 |
45 | | */ |
46 | 79.9k | if ((keydata.p.size() != 33) || (keydata.p[0] != 0x40)) { |
47 | 114 | return false; |
48 | 114 | } |
49 | 79.8k | if (botan_pubkey_load_ed25519(&pubkey.get(), &keydata.p[1])) { |
50 | 0 | return false; |
51 | 0 | } |
52 | 79.8k | return true; |
53 | 79.8k | } |
54 | | |
55 | | static bool |
56 | | load_secret_key(rnp::botan::Privkey &seckey, const ec::Key &keydata) |
57 | 0 | { |
58 | 0 | if (keydata.curve != PGP_CURVE_ED25519) { |
59 | 0 | return false; |
60 | 0 | } |
61 | 0 | size_t sz = keydata.x.size(); |
62 | 0 | if (!sz || (sz > 32)) { |
63 | 0 | return false; |
64 | 0 | } |
65 | 0 | uint8_t keybuf[32] = {0}; |
66 | 0 | keydata.x.copy(keybuf + 32 - sz); |
67 | 0 | if (botan_privkey_load_ed25519(&seckey.get(), keybuf)) { |
68 | 0 | return false; |
69 | 0 | } |
70 | | |
71 | 0 | return true; |
72 | 0 | } |
73 | | |
74 | | rnp_result_t |
75 | | validate_key(rnp::RNG &rng, const ec::Key &key, bool secret) |
76 | 0 | { |
77 | 0 | rnp::botan::Pubkey bpkey; |
78 | 0 | if (!load_public_key(bpkey, key) || botan_pubkey_check_key(bpkey.get(), rng.handle(), 0)) { |
79 | 0 | return RNP_ERROR_BAD_PARAMETERS; |
80 | 0 | } |
81 | | |
82 | 0 | if (!secret) { |
83 | 0 | return RNP_SUCCESS; |
84 | 0 | } |
85 | | |
86 | 0 | rnp::botan::Privkey bskey; |
87 | 0 | if (!load_secret_key(bskey, key) || |
88 | 0 | botan_privkey_check_key(bskey.get(), rng.handle(), 0)) { |
89 | 0 | return RNP_ERROR_BAD_PARAMETERS; |
90 | 0 | } |
91 | 0 | return RNP_SUCCESS; |
92 | 0 | } |
93 | | |
94 | | rnp_result_t |
95 | | generate(rnp::RNG &rng, ec::Key &key) |
96 | 0 | { |
97 | 0 | rnp::botan::Privkey eddsa; |
98 | 0 | if (botan_privkey_create(&eddsa.get(), "Ed25519", NULL, rng.handle())) { |
99 | 0 | return RNP_ERROR_GENERIC; |
100 | 0 | } |
101 | | |
102 | 0 | uint8_t key_bits[64]; |
103 | 0 | if (botan_privkey_ed25519_get_privkey(eddsa.get(), key_bits)) { |
104 | 0 | return RNP_ERROR_GENERIC; |
105 | 0 | } |
106 | | |
107 | | // First 32 bytes of key_bits are the EdDSA seed (private key) |
108 | | // Second 32 bytes are the EdDSA public key |
109 | 0 | key.x.assign(key_bits, 32); |
110 | | // insert the required 0x40 prefix on the public key |
111 | 0 | key_bits[31] = 0x40; |
112 | 0 | key.p.assign(key_bits + 31, 33); |
113 | 0 | key.curve = PGP_CURVE_ED25519; |
114 | 0 | return RNP_SUCCESS; |
115 | 0 | } |
116 | | |
117 | | rnp_result_t |
118 | | verify(const ec::Signature &sig, const rnp::secure_bytes &hash, const ec::Key &key) |
119 | 80.0k | { |
120 | | // Unexpected size for Ed25519 signature |
121 | 80.0k | if ((sig.r.size() > 32) || (sig.s.size() > 32)) { |
122 | 99 | return RNP_ERROR_SIGNATURE_INVALID; |
123 | 99 | } |
124 | | |
125 | 79.9k | rnp::botan::Pubkey eddsa; |
126 | 79.9k | if (!load_public_key(eddsa, key)) { |
127 | 146 | return RNP_ERROR_BAD_PARAMETERS; |
128 | 146 | } |
129 | | |
130 | 79.8k | rnp::botan::op::Verify verify_op; |
131 | 79.8k | if (botan_pk_op_verify_create(&verify_op.get(), eddsa.get(), "Pure", 0) || |
132 | 79.8k | botan_pk_op_verify_update(verify_op.get(), hash.data(), hash.size())) { |
133 | 0 | return RNP_ERROR_SIGNATURE_INVALID; |
134 | 0 | } |
135 | | |
136 | 79.8k | uint8_t bn_buf[64] = {0}; |
137 | 79.8k | sig.r.copy(bn_buf + 32 - sig.r.size()); |
138 | 79.8k | sig.s.copy(bn_buf + 64 - sig.s.size()); |
139 | | |
140 | 79.8k | if (botan_pk_op_verify_finish(verify_op.get(), bn_buf, 64)) { |
141 | 73.9k | return RNP_ERROR_SIGNATURE_INVALID; |
142 | 73.9k | } |
143 | 5.94k | return RNP_SUCCESS; |
144 | 79.8k | } |
145 | | |
146 | | rnp_result_t |
147 | | sign(rnp::RNG &rng, ec::Signature &sig, const rnp::secure_bytes &hash, const ec::Key &key) |
148 | 0 | { |
149 | 0 | rnp::botan::Privkey eddsa; |
150 | 0 | if (!load_secret_key(eddsa, key)) { |
151 | 0 | return RNP_ERROR_BAD_PARAMETERS; |
152 | 0 | } |
153 | | |
154 | 0 | rnp::botan::op::Sign sign_op; |
155 | 0 | if (botan_pk_op_sign_create(&sign_op.get(), eddsa.get(), "Pure", 0) || |
156 | 0 | botan_pk_op_sign_update(sign_op.get(), hash.data(), hash.size())) { |
157 | 0 | return RNP_ERROR_SIGNING_FAILED; |
158 | 0 | } |
159 | | |
160 | 0 | uint8_t bn_buf[64] = {0}; |
161 | 0 | size_t sig_size = sizeof(bn_buf); |
162 | 0 | if (botan_pk_op_sign_finish(sign_op.get(), rng.handle(), bn_buf, &sig_size)) { |
163 | 0 | return RNP_ERROR_SIGNING_FAILED; |
164 | 0 | } |
165 | | // Unexpected size... |
166 | 0 | assert(sig_size == 64); |
167 | 0 | sig.r.assign(bn_buf, 32); |
168 | 0 | sig.s.assign(bn_buf + 32, 32); |
169 | 0 | return RNP_SUCCESS; |
170 | 0 | } |
171 | | } // namespace eddsa |
172 | | } // namespace pgp |