Coverage Report

Created: 2026-04-28 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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