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