1 /*
2 * Curve25519
3 *
4 * Copyright:
5 * (C) 2014 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.pubkey.algo.curve25519;
12 
13 import botan.constants;
14 
15 static if (BOTAN_HAS_CURVE25519):
16 import botan.asn1.der_enc;
17 import botan.asn1.ber_dec;
18 import botan.pubkey.pk_keys;
19 import botan.rng.rng;
20 import botan.pubkey.pk_ops;
21 import botan.pubkey.algo.curve25519_donna;
22 import memutils.helpers;
23 import botan.utils.types;
24 import botan.utils.mem_ops;
25 import botan.utils.loadstor;
26 import botan.asn1.ber_dec;
27 import botan.asn1.der_enc;
28 
29 /**
30 * This class represents Curve25519 Public Keys.
31 */
32 struct Curve25519PublicKey
33 {
34 public:
35 	enum algoName = "Curve25519";
36 	this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 
37 	{
38 		m_owned = true;
39 		m_pub = new Curve25519PublicKeyImpl(alg_id, key_bits);
40 	}
41 	
42 	this(PrivateKey pkey) { m_pub = cast(Curve25519PublicKeyImpl) pkey; }
43 	this(PublicKey pkey) { m_pub = cast(Curve25519PublicKeyImpl) pkey; }
44 	
45 	mixin Embed!(m_pub, m_owned);
46 	
47 	bool m_owned;
48 	Curve25519PublicKeyImpl m_pub;
49 }
50 
51 /**
52 * This class represents Curve25519 Private Keys.
53 */
54 struct Curve25519PrivateKey
55 {
56 public:	
57 	enum algoName = "Curve25519";
58 	/// Create a new Curve 25519 private key
59 	this(RandomNumberGenerator rng) 
60 	{
61 		m_owned = true;
62 		m_priv = new Curve25519PrivateKeyImpl(rng);
63 	}
64 
65 	/// Load an existing Curve 25519 private key
66 	this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits, RandomNumberGenerator rng) 
67 	{
68 		m_owned = true;
69 		m_priv = new Curve25519PrivateKeyImpl(alg_id, key_bits, rng);
70 	}
71 	
72 	this(PrivateKey pkey) { m_priv = cast(Curve25519PrivateKeyImpl) pkey; }
73 	
74 	mixin Embed!(m_priv, m_owned);
75 	bool m_owned;
76 	Curve25519PrivateKeyImpl m_priv;
77 }
78 
79 class Curve25519PublicKeyImpl : PublicKey
80 {
81 public:
82 	this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 
83 	{
84 		BERDecoder(key_bits)
85 			    .startCons(ASN1Tag.SEQUENCE)
86 				.decode(m_public, ASN1Tag.OCTET_STRING)
87 				.verifyEnd()
88 				.endCons();
89 		
90 		sizeCheck(m_public.length, "public key");
91 	}
92 	
93 	/// Used for object casting to the right type in the factory.
94 	final override @property string algoName() const { return "Curve25519"; }
95 	
96 	final override size_t maxInputBits() const { return 256; }
97 	
98 	final override size_t messagePartSize() const { return 0; }
99 	
100 	final override size_t messageParts() const { return 1; }
101 	
102 	final override AlgorithmIdentifier algorithmIdentifier() const
103 	{
104 		return AlgorithmIdentifier(getOid(), AlgorithmIdentifierImpl.USE_NULL_PARAM);
105 	}
106 	
107 	final override Vector!ubyte x509SubjectPublicKey() const
108 	{
109 		return DEREncoder()
110 			    .startCons(ASN1Tag.SEQUENCE)
111 				.encode(m_public, ASN1Tag.OCTET_STRING)
112 				.endCons()
113 				.getContentsUnlocked();
114 	}
115 	
116 	Vector!ubyte publicValue() const { return unlock(m_public); }
117 	
118 	override bool checkKey(RandomNumberGenerator rng, bool b) const { return true; }
119 	
120 	override size_t estimatedStrength() const { return 128; }
121 	
122 protected:
123 	this() { }
124 	SecureVector!ubyte m_public;
125 }
126 
127 /**
128 * This abstract class represents ECC private keys
129 */
130 final class Curve25519PrivateKeyImpl : Curve25519PublicKeyImpl, PrivateKey, PKKeyAgreementKey
131 {
132 public:
133 	/**
134     * ECPrivateKey constructor
135     */
136 	this(RandomNumberGenerator rng) 
137 	{		
138 		super();
139 		m_private = rng.randomVec(32);
140 		m_public = curve25519Basepoint(m_private);
141 	}
142 	
143 	this(const ref AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits, RandomNumberGenerator rng) 
144 	{
145 		super();
146 		BERDecoder(key_bits)
147 			    .startCons(ASN1Tag.SEQUENCE)
148 				.decode(m_public, ASN1Tag.OCTET_STRING)
149 				.decode(m_private, ASN1Tag.OCTET_STRING)
150 				.verifyEnd()
151 				.endCons();
152 		
153 		sizeCheck(m_public.length, "public key");
154 		sizeCheck(m_private.length, "private key");
155 		
156 		loadCheck(rng);
157 		
158 	}
159 	
160 	override bool checkKey(RandomNumberGenerator rng, bool b) const
161 	{
162 		return curve25519Basepoint(m_private) == m_public;
163 	}
164 	
165 	SecureVector!ubyte pkcs8PrivateKey() const
166 	{
167 		return DEREncoder()
168 			    .startCons(ASN1Tag.SEQUENCE)
169 				.encode(m_public, ASN1Tag.OCTET_STRING)
170 				.encode(m_private, ASN1Tag.OCTET_STRING)
171 				.endCons()
172 				.getContents();
173 	}
174 	
175 	override AlgorithmIdentifier pkcs8AlgorithmIdentifier() const { return super.algorithmIdentifier(); }
176 	
177 	override Vector!ubyte publicValue() const { return super.publicValue(); }
178 	
179 	SecureVector!ubyte agree(const(ubyte)* w, size_t w_len) const {		
180 		sizeCheck(w_len, "public value");
181 		return curve25519(m_private, w);
182 	}
183 private:
184 	SecureVector!ubyte m_private;
185 }
186 /**
187 * Curve25519 operation
188 */
189 final class Curve25519KAOperation : KeyAgreement
190 {
191 public:
192 	this(in PrivateKey pkey) {
193 		this(cast(Curve25519PrivateKeyImpl) pkey);
194 	}
195 	this(in Curve25519PrivateKeyImpl pkey) {
196 		m_key = pkey;
197 	}
198 	this(in Curve25519PrivateKey pkey) {
199 		this(pkey.m_priv);
200 	}
201 	
202 	SecureVector!ubyte agree(const(ubyte)* w, size_t w_len)
203 	{
204 		return m_key.agree(w, w_len);
205 	}
206 private:
207 	const Curve25519PrivateKeyImpl m_key;
208 }
209 
210 
211 
212 private:
213 
214 void sizeCheck(size_t size, const string str)
215 {
216 	if(size != 32)
217 		throw new DecodingError("Invalid size " ~ size.to!string ~ " for Curve25519 " ~ str);
218 }
219 
220 SecureVector!ubyte curve25519(const ref SecureVector!ubyte secret,
221 	const(ubyte)* pubval)
222 {
223 	auto output = SecureVector!ubyte(32);
224 	const int rc = curve25519Donna(output.ptr, secret.ptr, pubval);
225 	assert(rc == 0, "Return value of curve25519Donna is ok");
226 	return output.move();
227 }
228 
229 SecureVector!ubyte curve25519Basepoint(const ref SecureVector!ubyte secret)
230 {
231 	ubyte[32] basepoint; basepoint[0] = 9;
232 	return curve25519(secret, cast(const(ubyte)*)basepoint.ptr);
233 }
234 
235 static if (BOTAN_TEST):
236 
237 import botan.test;
238 import botan.pubkey.test;
239 import botan.rng.auto_rng;
240 import botan.pubkey.pubkey;
241 import botan.asn1.oids;
242 import botan.codec.hex;
243 import core.atomic;
244 import memutils.hashmap;
245 import botan.pubkey.pkcs8;
246 import std.datetime;
247 import botan.pubkey.x509_key;
248 
249 private shared size_t total_tests;
250 
251 size_t curve25519ScalarKat(string secret_h, string basepoint_h,	string out_h)
252 {
253 	atomicOp!"+="(total_tests, 1);
254 	Vector!ubyte secret = hexDecode(secret_h);
255 	Vector!ubyte basepoint = hexDecode(basepoint_h);
256 	Vector!ubyte output = hexDecode(out_h);
257 	
258 	auto got = Vector!ubyte(32);
259 	curve25519Donna(got.ptr, secret.ptr, basepoint.ptr);
260 	
261 	if(got != output)
262 	{
263 		logError("Got " ~ hexEncode(got) ~ " exp " ~ hexEncode(output));
264 		return 1;
265 	}
266 	
267 	return 0;
268 }
269 
270 size_t c25519Roundtrip()
271 {
272 	atomicOp!"+="(total_tests, 1);
273 
274 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
275 	
276 	try
277 	{
278 		// First create keys
279 		auto a_priv_gen = Curve25519PrivateKey(*rng);
280 		auto b_priv_gen = Curve25519PrivateKey(*rng);
281 		
282 		string a_pass = "alice pass";
283 		string b_pass = "bob pass";
284 
285 		// Then serialize to encrypted storage
286 		auto pbe_time = 10.msecs;
287 		string a_priv_pem = pkcs8.PEM_encode(a_priv_gen, *rng, a_pass, pbe_time);
288 		string b_priv_pem = pkcs8.PEM_encode(b_priv_gen, *rng, b_pass, pbe_time);
289 		
290 		// Reload back into memory
291 		auto a_priv_ds = cast(DataSource) DataSourceMemory(a_priv_pem);
292 		auto b_priv_ds = cast(DataSource) DataSourceMemory(b_priv_pem);
293 		
294 		Unique!PKKeyAgreementKey a_priv = cast(PKKeyAgreementKey)pkcs8.loadKey(a_priv_ds, *rng, { return a_pass; });
295 		Unique!PKKeyAgreementKey b_priv = cast(PKKeyAgreementKey)pkcs8.loadKey(b_priv_ds, *rng, b_pass);
296 		
297 		// Export public keys as PEM
298 		string a_pub_pem = x509_key.PEM_encode(*a_priv);
299 		string b_pub_pem = x509_key.PEM_encode(*b_priv);
300 		
301 		auto a_pub_ds = cast(DataSource) DataSourceMemory(a_pub_pem);
302 		auto b_pub_ds = cast(DataSource) DataSourceMemory(b_pub_pem);
303 		
304 		Unique!PublicKey a_pub = x509_key.loadKey(a_pub_ds);
305 		Unique!PublicKey b_pub = x509_key.loadKey(b_pub_ds);
306 		
307 		auto a_pub_key = Curve25519PublicKey(*a_pub);
308 		auto b_pub_key = Curve25519PublicKey(*b_pub);
309 		
310 		auto a_ka = scoped!PKKeyAgreement(*a_priv, "KDF2(SHA-256)");
311 		auto b_ka = scoped!PKKeyAgreement(*b_priv, "KDF2(SHA-256)");
312 		
313 		string context = "shared context value";
314 		SymmetricKey a_key = a_ka.deriveKey(32, b_pub_key.publicValue(), context);
315 		SymmetricKey b_key = b_ka.deriveKey(32, a_pub_key.publicValue(), context);
316 		
317 		if(a_key != b_key)
318 			return 1;
319 	}
320 	catch(Exception e)
321 	{
322 		writeln("C25519 rt fail: ", e.toString());
323 		return 1;
324 	}
325 	
326 	return 0;
327 }
328 
329 static if (BOTAN_HAS_TESTS && !SKIP_CURVE25519_TEST) unittest
330 {
331 	logDebug("Testing curve25519.d ...");
332 	size_t fails = 0;
333 
334 
335 	File c25519_scalar = File("../test_data/pubkey/c25519_scalar.vec", "r");
336 	
337 	fails += runTestsBb(c25519_scalar, "Curve25519 ScalarMult", "Out", true,
338 		(ref HashMap!(string, string) m) {
339 			return curve25519ScalarKat(m["Secret"], m["Basepoint"], m["Out"]);
340 		});
341 	fails += c25519Roundtrip();
342 
343 	testReport("curve25519", total_tests, fails);
344 }
345