1 /** 2 * GOST 34.10-2001 3 * 4 * Copyright: 5 * (C) 2007 Falko Strenzke, FlexSecure GmbH 6 * Manuel Hartl, FlexSecure GmbH 7 * (C) 2008-2010 Jack Lloyd 8 * (C) 2014-2015 Etienne Cimon 9 * 10 * License: 11 * Botan is released under the Simplified BSD License (see LICENSE.md) 12 */ 13 module botan.pubkey.algo.gost_3410; 14 15 import botan.constants; 16 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_GOST_34_10_2001): 17 18 public import botan.pubkey.pubkey; 19 import botan.pubkey.algo.ecc_key; 20 import botan.pubkey.pk_ops; 21 import botan.pubkey.algo.gost_3410; 22 import botan.asn1.der_enc; 23 import botan.asn1.ber_dec; 24 import botan.math.ec_gfp.point_gfp; 25 import botan.rng.rng; 26 import std.algorithm; 27 import memutils.helpers : Embed; 28 29 struct GOST3410Options // applied to ECPublicKey 30 { 31 enum algoName = "GOST-34.10"; 32 enum msgParts = 2; 33 34 static AlgorithmIdentifier algorithmIdentifier(in ECPublicKey pkey) 35 { 36 //logTrace("Encode algorithmIdentifier x509"); 37 Vector!ubyte params = DEREncoder().startCons(ASN1Tag.SEQUENCE) 38 .encode(OID(pkey.domain().getOid())) 39 .endCons() 40 .getContentsUnlocked(); 41 42 return AlgorithmIdentifier(pkey.getOid(), params); 43 } 44 45 static Vector!ubyte x509SubjectPublicKey(in ECPublicKey pkey) 46 { 47 //logTrace("Encode x509SubjectPublicKey"); 48 // Trust CryptoPro to come up with something obnoxious 49 const BigInt x = pkey.publicPoint().getAffineX(); 50 const BigInt y = pkey.publicPoint().getAffineY(); 51 52 size_t part_size = max(x.bytes(), y.bytes()); 53 54 Vector!ubyte bits = Vector!ubyte(2*part_size); 55 56 x.binaryEncode(&bits[part_size - x.bytes()]); 57 y.binaryEncode(&bits[2*part_size - y.bytes()]); 58 59 // Keys are stored in little endian format (WTF) 60 foreach (size_t i; 0 .. (part_size / 2)) 61 { 62 swap(bits[i], bits[part_size-1-i]); 63 swap(bits[part_size+i], bits[2*part_size-1-i]); 64 } 65 66 return DEREncoder().encode(bits, ASN1Tag.OCTET_STRING).getContentsUnlocked(); 67 } 68 } 69 70 /** 71 * GOST-34.10 Public Key 72 */ 73 struct GOST3410PublicKey 74 { 75 public: 76 alias Options = GOST3410Options; 77 __gshared immutable algoName = Options.algoName; 78 /** 79 * Construct a public key from a given public point. 80 * 81 * Params: 82 * dom_par = the domain parameters associated with this key 83 * public_point = the public point defining this key 84 */ 85 this(const ref ECGroup dom_par, const ref PointGFp public_point) 86 { 87 m_owned = true; 88 m_pub = new ECPublicKey(Options(), dom_par, public_point); 89 } 90 91 /** 92 * Construct from X.509 algorithm id and subject public key bits 93 */ 94 this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 95 { 96 //logTrace("Decode public key"); 97 OID ecc_param_id = OID(); 98 99 // Also includes hash and cipher OIDs... brilliant design guys 100 BERDecoder(alg_id.parameters).startCons(ASN1Tag.SEQUENCE).decode(ecc_param_id); 101 102 ECGroup domain_params = ECGroup(ecc_param_id); 103 SecureVector!ubyte bits; 104 BERDecoder(key_bits).decode(bits, ASN1Tag.OCTET_STRING); 105 106 const size_t part_size = bits.length / 2; 107 108 // Keys are stored in little endian format (WTF) 109 foreach (size_t i; 0 .. (part_size / 2)) 110 { 111 swap(bits[i], bits[part_size-1-i]); 112 swap(bits[part_size+i], bits[2*part_size-1-i]); 113 } 114 115 BigInt x = BigInt(bits.ptr, part_size); 116 BigInt y = BigInt(&bits[part_size], part_size); 117 118 PointGFp public_point = PointGFp(domain_params.getCurve(), &x, &y); 119 m_owned = true; 120 m_pub = new ECPublicKey(Options(), domain_params, public_point); 121 assert(public_point.onTheCurve(), "Loaded GOST 34.10 public key is on the curve"); 122 } 123 124 this(PublicKey pkey) { m_pub = cast(ECPublicKey) pkey; } 125 126 this(PrivateKey pkey) { m_pub = cast(ECPublicKey) pkey; } 127 128 mixin Embed!(m_pub, m_owned); 129 130 bool m_owned; 131 ECPublicKey m_pub; 132 } 133 134 /** 135 * GOST-34.10 Private Key 136 */ 137 struct GOST3410PrivateKey 138 { 139 public: 140 alias Options = GOST3410Options; 141 __gshared immutable algoName = Options.algoName; 142 143 this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 144 { 145 m_owned = true; 146 m_priv = new ECPrivateKey(Options(), alg_id, key_bits); 147 } 148 149 /** 150 * Generate a new private key 151 * Params: 152 * rng = a random number generator 153 * domain = parameters to used for this key 154 * x = the private key; if zero, a new random key is generated 155 */ 156 this(RandomNumberGenerator rng, const ref ECGroup domain, BigInt x = BigInt(0)) 157 { 158 m_owned = true; 159 m_priv = new ECPrivateKey(Options(), rng, domain, x); 160 } 161 162 this(PrivateKey pkey) { m_priv = cast(ECPrivateKey) pkey; } 163 164 mixin Embed!(m_priv, m_owned); 165 166 bool m_owned; 167 ECPrivateKey m_priv; 168 } 169 170 /** 171 * GOST-34.10 signature operation 172 */ 173 final class GOST3410SignatureOperation : Signature 174 { 175 public: 176 this(in PrivateKey pkey) { 177 this(cast(ECPrivateKey) pkey); 178 } 179 180 this(in GOST3410PrivateKey pkey) { 181 this(pkey.m_priv); 182 } 183 184 this(in ECPrivateKey gost_3410) 185 { 186 assert(gost_3410.algoName == GOST3410PublicKey.algoName); 187 m_base_point = &gost_3410.domain().getBasePoint(); 188 m_order = &gost_3410.domain().getOrder(); 189 m_x = &gost_3410.privateValue(); 190 } 191 192 override size_t messageParts() const { return 2; } 193 override size_t messagePartSize() const { return m_order.bytes(); } 194 override size_t maxInputBits() const { return m_order.bits(); } 195 196 override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, 197 RandomNumberGenerator rng) 198 { 199 BigInt k; 200 do 201 k.randomize(rng, m_order.bits()-1); 202 while (k >= *m_order); 203 204 BigInt e = decodeLittleEndian(msg, msg_len); 205 206 e %= *m_order; 207 if (e == 0) 208 e = 1; 209 210 PointGFp k_times_P = (*m_base_point) * &k; 211 212 assert(k_times_P.onTheCurve(), "GOST 34.10 k*g is on the curve"); 213 214 BigInt r = k_times_P.getAffineX() % (*m_order); 215 216 auto s_0 = r * m_x; 217 s_0 += k * &e; 218 BigInt s = s_0 % m_order; 219 220 if (r == 0 || s == 0) 221 throw new InvalidState("GOST 34.10: r == 0 || s == 0"); 222 223 SecureVector!ubyte output = SecureVector!ubyte(2*m_order.bytes()); 224 s.binaryEncode(&output[output.length / 2 - s.bytes()]); 225 r.binaryEncode(&output[output.length - r.bytes()]); 226 return output; 227 } 228 229 private: 230 const PointGFp* m_base_point; 231 const BigInt* m_order; 232 const BigInt* m_x; 233 } 234 235 /** 236 * GOST-34.10 verification operation 237 */ 238 final class GOST3410VerificationOperation : Verification 239 { 240 public: 241 this(in PublicKey pkey) { 242 this(cast(ECPublicKey) pkey); 243 } 244 245 this(in GOST3410PublicKey pkey) { 246 this(pkey.m_pub); 247 } 248 249 this(in ECPublicKey gost) 250 { 251 assert(gost.algoName == GOST3410PublicKey.algoName); 252 m_ec_publickey = gost; 253 m_base_point = &m_ec_publickey.domain().getBasePoint(); 254 m_public_point = &m_ec_publickey.publicPoint(); 255 m_order = &m_ec_publickey.domain().getOrder(); 256 } 257 258 override size_t messageParts() const { return 2; } 259 override size_t messagePartSize() const { return m_order.bytes(); } 260 override size_t maxInputBits() const { return m_order.bits(); } 261 262 override bool withRecovery() const { return false; } 263 264 override SecureVector!ubyte verifyMr(const(ubyte)*, size_t) { throw new InvalidState("Message recovery not supported"); } 265 override bool verify(const(ubyte)* msg, size_t msg_len, 266 const(ubyte)* sig, size_t sig_len) 267 { 268 if (sig_len != m_order.bytes()*2) 269 return false; 270 271 BigInt e = decodeLittleEndian(msg, msg_len); 272 273 BigInt s = BigInt(sig, sig_len / 2); 274 BigInt r = BigInt(sig + sig_len / 2, sig_len / 2); 275 276 if (r <= 0 || r >= (*m_order) || s <= 0 || s >= (*m_order)) 277 return false; 278 279 e %= (*m_order); 280 if (e == 0) 281 e = 1; 282 283 BigInt v = inverseMod(&e, m_order); 284 285 BigInt z1 = (s*v) % (*m_order); 286 BigInt z2 = (-r*v) % (*m_order); 287 288 PointGFp R = PointGFp.multiExponentiate(*m_base_point, &z1, 289 *m_public_point, &z2); 290 291 if (R.isZero()) 292 return false; 293 294 return (R.getAffineX() == r); 295 } 296 // const ~this() { destroy(cast(GOST3410VerificationOperation)this); } 297 private: 298 const ECPublicKey m_ec_publickey; 299 const PointGFp* m_base_point; 300 const PointGFp* m_public_point; 301 const BigInt* m_order; 302 } 303 304 305 private: 306 307 BigInt decodeLittleEndian(const(ubyte)* msg, size_t msg_len) 308 { 309 SecureVector!ubyte msg_le = SecureVector!ubyte(msg[0 .. msg_len]); 310 311 for (size_t i = 0; i != msg_le.length / 2; ++i) 312 swap(msg_le[i], msg_le[msg_le.length-1-i]); 313 314 return BigInt(msg_le.ptr, msg_le.length); 315 } 316 317 318 static if (BOTAN_TEST): 319 320 import botan.test; 321 import botan.pubkey.test; 322 import botan.rng.auto_rng; 323 import botan.pubkey.pubkey; 324 import botan.asn1.oids; 325 import botan.codec.hex; 326 import core.atomic; 327 import memutils.hashmap; 328 329 private shared size_t total_tests; 330 331 size_t testPkKeygen(RandomNumberGenerator rng) 332 { 333 size_t fails; 334 string[] gost_list = ["gost_256A", "secp112r1", "secp128r1", "secp160r1", 335 "secp192r1", "secp224r1", "secp256r1", "secp384r1", "secp521r1"]; 336 337 foreach (gost; gost_list) { 338 atomicOp!"+="(total_tests, 1); 339 auto ec = ECGroup(OIDS.lookup(gost)); 340 auto key = GOST3410PrivateKey(rng, ec); 341 key.checkKey(rng, true); 342 fails += validateSaveAndLoad(key, rng); 343 } 344 345 return fails; 346 } 347 348 size_t gostVerify(string group_id, 349 string x, 350 string hash, 351 string msg, 352 string signature) 353 { 354 atomicOp!"+="(total_tests, 1); 355 356 ECGroup group = ECGroup(OIDS.lookup(group_id)); 357 auto x_dec = hexDecode(x); 358 PointGFp public_point = OS2ECP(x_dec, group.getCurve()); 359 360 auto gost = GOST3410PublicKey(group, public_point); 361 362 const string padding = "EMSA1(" ~ hash ~ ")"; 363 364 PKVerifier v = PKVerifier(gost, padding); 365 366 if (!v.verifyMessage(hexDecode(msg), hexDecode(signature))) 367 return 1; 368 369 return 0; 370 } 371 372 static if (BOTAN_HAS_TESTS && !SKIP_GOST_TEST) unittest 373 { 374 logDebug("Testing gost_3410.d ..."); 375 size_t fails = 0; 376 377 Unique!AutoSeededRNG rng = new AutoSeededRNG; 378 379 fails += testPkKeygen(*rng); 380 381 File ecdsa_sig = File("test_data/pubkey/gost_3410.vec", "r"); 382 383 fails += runTestsBb(ecdsa_sig, "GOST-34.10 Signature", "Signature", true, 384 (ref HashMap!(string, string) m) { 385 return gostVerify(m["Group"], m["Pubkey"], m["Hash"], m["Msg"], m["Signature"]); 386 }); 387 388 testReport("gost_3410", total_tests, fails); 389 } 390