forked from summitto/pgp-packet-library
-
Notifications
You must be signed in to change notification settings - Fork 0
/
key_from_raw_data.cpp
117 lines (101 loc) · 5.38 KB
/
key_from_raw_data.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
#include <pgp-packet/packet.h>
#include <ctime>
#include <iostream>
#include <fstream>
#include <sodium.h>
#include <vector>
int main()
{
// initialize libsodium
if (sodium_init() == -1) {
return 1;
}
// create vectors holding the secret and public key points, note
// that we take an extra byte for the public key, since pgp will
// need an extra byte in front of it, it is always the same byte
// for this key type, so it doesn't add any real information but
// pgp still requires it and throws a fit if it is missing.
pgp::vector<uint8_t> public_key_data;
pgp::vector<uint8_t> secret_key_data;
// allocate memory for the keys
public_key_data.resize(crypto_sign_PUBLICKEYBYTES + 1);
secret_key_data.resize(crypto_sign_SECRETKEYBYTES);
// now create a new keypair to be imported into pgp, as noted in
// the declaration above, we have to ignore the first byte as we
// need to set a special tag byte here for pgp to recognize it
// note: error checks are skipped here for brevity
crypto_sign_keypair(public_key_data.data() + 1, secret_key_data.data());
// libsodium puts both the secret key and the public key data in
// the secret key buffer, which pgp does not need, therefore the
// extra data must be truncated before creating the key
secret_key_data.resize(32);
// now set the tag byte required for pgp
public_key_data[0] = 0x40;
// define the creation time of this key (as a unix timestamp)
// and the expiration period (the amount of seconds which the key will be valid after it's creation)
// we define the key as created in the current time and valid for 3600 seconds (an hour);
uint32_t creation = std::time(nullptr);
uint32_t expiration = 3600;
// now create a packet containing this secret key
pgp::packet secret_key_packet{
pgp::in_place_type_t<pgp::secret_key>{}, // we are building a secret key
creation, // created at this unix timestamp
pgp::key_algorithm::eddsa, // using the eddsa key algorithm
pgp::in_place_type_t<pgp::secret_key::eddsa_key_t>{}, // create a key of the eddsa type
std::forward_as_tuple( // arguments for the public key
pgp::curve_oid::ed25519(), // which curve to use
pgp::multiprecision_integer{ std::move(public_key_data) } // move in the public key point
),
std::forward_as_tuple( // secret arguments
pgp::multiprecision_integer{ std::move(secret_key_data) } // copy in the secret key point
)
};
// create a packet describing the user owning this key
pgp::packet user_id_packet{
pgp::in_place_type_t<pgp::user_id>{},
std::string{ "Anne Onymous <anonymous@example.org>" }
};
// retrieve the secret key from the packet generated above
const auto& secret_key = pgp::get<pgp::secret_key>(secret_key_packet.body());
// to complete the set, we need to create a signature packet,
// which certifies that we are the owners of this key.
pgp::packet signature_packet{
pgp::in_place_type_t<pgp::signature>{}, // we are making a signature
secret_key, // we sign it with the secret key
pgp::get<pgp::user_id>(user_id_packet.body()), // for the given user
pgp::signature_subpacket_set{{ // hashed subpackets
pgp::signature_subpacket::signature_creation_time { creation }, // signature creation time
pgp::signature_subpacket::key_expiration_time { expiration }, // key expiration time
pgp::signature_subpacket::issuer_fingerprint{ // fingerprint of the key we are signing with
secret_key.fingerprint()
},
pgp::signature_subpacket::key_flags{ // the privileges for the main key
pgp::key_flag::certification,
pgp::key_flag::signing,
},
}},
pgp::signature_subpacket_set{{ // unhashed subpackets
pgp::signature_subpacket::issuer { // key ID of the key we are signing with
secret_key.key_id()
}
}}
};
// we now have a set of packets, which, when encoded to a file, can
// be imported into a compatible pgp implementation (such as gnupg)
pgp::vector<uint8_t> data;
data.resize(
secret_key_packet .size() +
user_id_packet .size() +
signature_packet .size()
);
// create an encoder writing in the vectors range
pgp::range_encoder encoder{ data };
// encode all the packets into the encoder
secret_key_packet .encode(encoder);
user_id_packet .encode(encoder);
signature_packet .encode(encoder);
// the encoder has now filled the vector with data, which can be written
std::ofstream output{ "keyfile" };
output.write(reinterpret_cast<const char*>(data.data()), data.size());
return 0;
}