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