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