forked from calccrypto/OpenPGP
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeygen.cpp
291 lines (239 loc) · 10.4 KB
/
keygen.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#include "keygen.h"
namespace OpenPGP {
namespace KeyGen {
bool fill_key_sigs(SecretKey & private_key, const std::string & passphrase){
RNG::BBS(static_cast <MPI> (static_cast <uint32_t> (now()))); // seed just in case not seeded
if (!private_key.meaningful()){
// "Error: Bad key.\n";
return false;
}
const std::string keyid = private_key.keyid();
const Packet::Tag5::Ptr primary = std::static_pointer_cast <Packet::Tag5> (private_key.get_packets()[0]);
PGP::Packets packets = private_key.get_packets_clone();
Packet::Key::Ptr key = nullptr;
Packet::User::Ptr user = nullptr;
for(Packet::Tag::Ptr & p : packets){
if (Packet::is_key_packet(p -> get_tag())){
key = std::static_pointer_cast <Packet::Key> (p);
user = nullptr;
}
else if (Packet::is_user(p -> get_tag())){
user = std::static_pointer_cast <Packet::User> (p);
}
else if (p -> get_tag() == Packet::SIGNATURE){
Packet::Tag2::Ptr sig = std::static_pointer_cast <Packet::Tag2> (p);
// only fill in keys that are supposed to be signed by the primary key
// don't fill in empty key IDs
if (sig -> get_keyid() == keyid){
if (Signature_Type::is_certification(sig -> get_type())){
if (key -> get_tag() == Packet::SECRET_KEY){
const Packet::Tag5::Ptr tag5 = std::static_pointer_cast <Packet::Tag5> (key);
sig = Sign::primary_key(primary, passphrase, tag5, user, sig);
}
else{
// "Error: Certification signature attempted to be made for a non-primary key.\n";
return false;
}
}
else if (sig -> get_type() == Signature_Type::SUBKEY_BINDING_SIGNATURE){
if (key -> get_tag() == Packet::SECRET_SUBKEY){
const Packet::Tag7::Ptr tag7 = std::static_pointer_cast <Packet::Tag7> (key);
sig = Sign::subkey_binding(primary, passphrase, tag7, sig);
}
else{
// "Error: Subkey Binding signature attempted to be made for a non-subkey.\n";
return false;
}
}
else if (sig -> get_type() == Signature_Type::KEY_REVOCATION_SIGNATURE){
sig = Revoke::sig(primary, passphrase, primary, sig);
}
else if (sig -> get_type() == Signature_Type::SUBKEY_REVOCATION_SIGNATURE){
sig = Revoke::sig(primary, passphrase, key, sig);
}
else if (sig -> get_type() == Signature_Type::CERTIFICATION_REVOCATION_SIGNATURE){
sig = Revoke::uid_sig(primary, passphrase, user, sig);
}
else{
std::cerr << "Warning: Bad or unhandled signature type: 0x" << makehex(sig -> get_type(), 2) << std::endl;
}
}
}
else{
// should never come here
// "Error: Random packet found.\n";
return false;
}
}
private_key.set_packets(packets);
return true;
}
SecretKey generate_key(Config & config){
RNG::BBS(static_cast <MPI> (static_cast <uint32_t> (now()))); // seed just in case not seeded
if (!config.valid()){
// "Error: Bad key generation configuration.\n";
return SecretKey();
}
// collection of packets to be put into final key
PGP::Packets packets;
// Key creation time
const uint32_t time = now();
// generate Primary Key, User ID, and Signature packets
// generate public key values for primary key
PKA::Values pub;
PKA::Values pri;
if (!PKA::generate_keypair(config.pka, PKA::generate_params(config.pka, config.bits >> 1), pri, pub)){
// "Error: Could not generate primary key pair.\n";
return SecretKey();
}
// convert the secret values into a string
std::string secret;
for(MPI const & mpi : pri){
secret += write_MPI(mpi);
}
// Secret Key Packet
Packet::Tag5::Ptr primary = std::make_shared <Packet::Tag5> ();
primary -> set_version(4);
primary -> set_time(time);
primary -> set_pka(config.pka);
primary -> set_mpi(pub);
primary -> set_s2k_con(0); // no passphrase up to here
// encrypt secret only if there is a passphrase
if (config.passphrase.size()){
primary -> set_s2k_con(254);
primary -> set_sym(config.sym);
// Secret Key Packet S2K
S2K::S2K3::Ptr s2k3 = std::make_shared <S2K::S2K3> ();
s2k3 -> set_hash(config.hash);
s2k3 -> set_salt(unhexlify(bintohex(RNG::BBS().rand(64))));
s2k3 -> set_count(96);
// calculate the key from the passphrase
const std::string session_key = s2k3 -> run(config.passphrase, Sym::KEY_LENGTH.at(config.sym) >> 3);
// add checksum to secret
secret += Hash::use(Hash::ID::SHA1, secret);
// encrypt private key value
primary -> set_s2k(s2k3);
primary -> set_IV(unhexlify(bintohex(RNG::BBS().rand(Sym::BLOCK_LENGTH.at(config.sym)))));
secret = use_normal_CFB_encrypt(config.sym, secret, session_key, primary -> get_IV());
}
else{
// add checksum to secret
uint16_t checksum = 0;
for(uint8_t const c : secret){
checksum += static_cast <uint16_t> (c);
}
secret += unhexlify(makehex(checksum, 4));
}
primary -> set_secret(secret);
// first packet is primary key
packets.push_back(primary);
// get ID of primary key
const std::string keyid = primary -> get_keyid();
// generate User ID and Signature packets
for(Config::UserID const & id : config.uids){
// User ID
Packet::Tag13::Ptr uid = std::make_shared <Packet::Tag13> ();
uid -> set_contents(id.user, id.comment, id.email);
packets.push_back(uid);
Packet::Tag2::Ptr sig = std::make_shared <Packet::Tag2> ();
sig -> set_version(4);
sig -> set_type(Signature_Type::POSITIVE_CERTIFICATION_OF_A_USER_ID_AND_PUBLIC_KEY_PACKET);
sig -> set_pka(config.pka);
sig -> set_hash(id.sig);
// set creation time
Subpacket::Tag2::Sub2::Ptr tag2sub2 = std::make_shared <Subpacket::Tag2::Sub2> ();
tag2sub2 -> set_time(time);
sig -> set_hashed_subpackets({tag2sub2});
// set issuer
Subpacket::Tag2::Sub16::Ptr tag2sub16 = std::make_shared <Subpacket::Tag2::Sub16> ();
tag2sub16 -> set_keyid(keyid);
sig -> set_unhashed_subpackets({tag2sub16});
// sign Primary Key and User ID
sig = Sign::primary_key(primary, config.passphrase, primary, uid, sig);
if (!sig){
// "Error: Failed to sign primary config.\n";
return SecretKey();
}
packets.push_back(sig);
}
// generate 0 or more subkeys and associated signature packet
for(Config::SubkeyGen const & skey : config.subkeys){
PKA::Values subkey_pub;
PKA::Values subkey_pri;
if (!PKA::generate_keypair(skey.pka, PKA::generate_params(skey.pka, skey.bits >> 1), subkey_pri, subkey_pub)){
// "Error: Could not generate subkey pair.\n";
return SecretKey();
}
// convert the secret values into a string
secret = "";
for(MPI const & mpi : subkey_pri){
secret += write_MPI(mpi);
}
// Secret Subkey Packet
Packet::Tag7::Ptr subkey = std::make_shared <Packet::Tag7> ();
subkey -> set_version(4);
subkey -> set_time(time);
subkey -> set_pka(skey.pka);
subkey -> set_mpi(subkey_pub);
subkey -> set_s2k_con(0); // no passphrase up to here
// encrypt secret only if there is a passphrase
if (config.passphrase.size()){
subkey -> set_s2k_con(254);
subkey -> set_sym(skey.sym);
// Secret Subkey S2K
S2K::S2K3::Ptr s2k3 = std::make_shared <S2K::S2K3> ();
s2k3 -> set_hash(skey.hash);
s2k3 -> set_salt(unhexlify(bintohex(RNG::BBS().rand(64)))); // new salt value
s2k3 -> set_count(96);
// calculate the key from the passphrase
std::string session_key = s2k3 -> run(config.passphrase, Sym::KEY_LENGTH.at(skey.sym) >> 3);
// add checksum to secret
secret += Hash::use(Hash::ID::SHA1, secret);
// encrypt private key value
subkey -> set_s2k(s2k3);
subkey -> set_IV(unhexlify(bintohex(RNG::BBS().rand(Sym::BLOCK_LENGTH.at(skey.sym)))));
secret = use_normal_CFB_encrypt(skey.sym, secret + Hash::use(Hash::ID::SHA1, secret), session_key, subkey -> get_IV());
}
else{
// add checksum to secret
uint16_t checksum = 0;
for(uint8_t const c : secret){
checksum += c;
}
secret += unhexlify(makehex(checksum, 4));
}
subkey -> set_secret(secret);
packets.push_back(subkey);
// Subkey Binding Signature
Packet::Tag2::Ptr subsig = std::make_shared <Packet::Tag2> ();
subsig -> set_version(4);
subsig -> set_type(Signature_Type::SUBKEY_BINDING_SIGNATURE);
subsig -> set_pka(config.pka);
subsig -> set_hash(skey.sig);
// set creation time
Subpacket::Tag2::Sub2::Ptr tag2sub2 = std::make_shared <Subpacket::Tag2::Sub2> ();
tag2sub2 -> set_time(time);
subsig -> set_hashed_subpackets({tag2sub2});
// set issuer
Subpacket::Tag2::Sub16::Ptr tag2sub16 = std::make_shared <Subpacket::Tag2::Sub16> ();
tag2sub16 -> set_keyid(keyid);
subsig -> set_unhashed_subpackets({tag2sub16});
// sign subkey
subsig = Sign::subkey_binding(primary, config.passphrase, subkey, subsig);
if (!subsig){
// "Error: Subkey signing failure.\n";
return SecretKey();
}
packets.push_back(subsig);
}
// put everything into a private key
SecretKey private_key;
private_key.set_keys({std::make_pair("Version", "cc")});
private_key.set_packets(packets);
private_key.set_armored(true);
// can call fill_key_sigs as well
// return fill_key_sigs(private_key, config.passphrase);
return private_key;
}
}
}