1 /** 2 * DSA 3 * 4 * Copyright: 5 * (C) 1999-2010 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.dsa; 12 13 import botan.constants; 14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_DSA): 15 16 public import botan.pubkey.algo.dl_algo; 17 public import botan.pubkey.pubkey; 18 import botan.pubkey.pk_ops; 19 import botan.math.numbertheory.reducer; 20 import botan.math.numbertheory.pow_mod; 21 import botan.math.numbertheory.numthry; 22 import botan.pubkey.algo.keypair; 23 import std.concurrency; 24 import core.thread; 25 import memutils.helpers : Embed; 26 import std.algorithm : max; 27 28 struct DSAOptions { 29 enum algoName = "DSA"; 30 enum format = DLGroup.ANSI_X9_57; 31 enum msgParts = 2; 32 33 /* 34 * Check Private DSA Parameters 35 */ 36 static bool checkKey(in DLSchemePrivateKey privkey, RandomNumberGenerator rng, bool strong) 37 { 38 if (!privkey.checkKeyImpl(rng, strong) || privkey.m_x >= privkey.groupQ()) 39 return false; 40 41 if (!strong) 42 return true; 43 44 return signatureConsistencyCheck(rng, privkey, "EMSA1(SHA-1)"); 45 } 46 } 47 48 /** 49 * DSA Public Key 50 */ 51 struct DSAPublicKey 52 { 53 public: 54 alias Options = DSAOptions; 55 __gshared immutable string algoName = Options.algoName; 56 57 58 this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 59 { 60 m_owned = true; 61 m_pub = new DLSchemePublicKey(Options(), alg_id, key_bits); 62 } 63 64 /* 65 * DSAPublicKey Constructor 66 */ 67 this(DLGroup grp, BigInt y1) 68 { 69 m_owned = true; 70 m_pub = new DLSchemePublicKey(Options(), grp.move, y1.move); 71 } 72 73 this(PublicKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; } 74 this(PrivateKey pkey) { m_pub = cast(DLSchemePublicKey) pkey; } 75 76 mixin Embed!(m_pub, m_owned); 77 78 bool m_owned; 79 DLSchemePublicKey m_pub; 80 } 81 82 /** 83 * DSA Private Key 84 */ 85 struct DSAPrivateKey 86 { 87 public: 88 alias Options = DSAOptions; 89 __gshared immutable string algoName = Options.algoName; 90 91 /* 92 * Create a DSA private key 93 */ 94 this(RandomNumberGenerator rng, DLGroup dl_group, BigInt x_arg = 0) 95 { 96 bool x_arg_0; 97 if (x_arg == 0) { 98 x_arg_0 = true; 99 auto bi = BigInt(2); 100 x_arg = BigInt.randomInteger(rng, bi, dl_group.getQ() - 1); 101 } 102 BigInt y1 = powerMod(&dl_group.getG(), &x_arg, &dl_group.getP()); 103 104 m_owned = true; 105 m_priv = new DLSchemePrivateKey(Options(), dl_group.move, y1.move, x_arg.move); 106 107 if (x_arg_0) 108 m_priv.genCheck(rng); 109 else 110 m_priv.loadCheck(rng); 111 } 112 113 this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits, RandomNumberGenerator rng) 114 { 115 m_owned = true; 116 m_priv = new DLSchemePrivateKey(Options(), alg_id, key_bits); 117 m_priv.loadCheck(rng); 118 } 119 120 this(PrivateKey pkey) { m_priv = cast(DLSchemePrivateKey) pkey; } 121 122 mixin Embed!(m_priv, m_owned); 123 124 bool m_owned; 125 DLSchemePrivateKey m_priv; 126 } 127 128 /** 129 * Object that can create a DSA signature 130 */ 131 final class DSASignatureOperation : Signature 132 { 133 public: 134 this(in PrivateKey pkey) { 135 this(cast(DLSchemePrivateKey) pkey); 136 } 137 138 this(in DSAPrivateKey pkey) { 139 this(pkey.m_priv); 140 } 141 142 this(in DLSchemePrivateKey dsa) 143 { 144 assert(dsa.algoName == DSAPublicKey.algoName); 145 m_q = &dsa.groupQ(); 146 m_x = &dsa.getX(); 147 m_g = &dsa.groupG(); 148 m_p = &dsa.groupP(); 149 m_mod_q = ModularReducer(dsa.groupQ()); 150 } 151 152 override size_t messageParts() const { return 2; } 153 override size_t messagePartSize() const { return m_q.bytes(); } 154 override size_t maxInputBits() const { return m_q.bits(); } 155 156 override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, RandomNumberGenerator rng) 157 { 158 //import core.memory : GC; GC.disable(); scope(exit) GC.enable(); 159 rng.addEntropy(msg, msg_len); 160 161 BigInt i = BigInt(msg, msg_len); 162 BigInt r = BigInt(0), s = BigInt(0); 163 164 while (r == 0 || s == 0) 165 { 166 BigInt k; 167 do 168 k.randomize(rng, m_q.bits()); 169 while (k >= *m_q); 170 171 import core.sync.mutex, core.sync.condition; 172 Mutex mutex = ThreadMem.alloc!Mutex(); 173 scope(exit) { 174 ThreadMem.free(mutex); 175 } 176 177 BigInt res; 178 //logDebug("Expected: ", max(m_g.bytes() + m_g.bytes() % 128, m_x.bytes() + m_x.bytes % 128)); 179 res.reserve(4096); 180 181 struct Handler { 182 shared(Mutex) mtx; 183 shared(ModularReducer*) mod_q; 184 shared(const BigInt*) g; 185 shared(const BigInt*) p; 186 shared(BigInt*) k2; 187 shared(BigInt*) res2; 188 void run() { 189 try { 190 import botan.libstate.libstate : modexpInit; 191 modexpInit(); // enable quick path for powermod 192 BigInt* ret = cast(BigInt*) res2; 193 { import memutils.utils; 194 auto powermod_g_p = scoped!FixedBasePowerModImpl(cast(const(BigInt)*)g, cast(const(BigInt)*)p); 195 BigInt _res = (cast(ModularReducer)*mod_q).reduce(powermod_g_p(cast(BigInt*)k2)); 196 //logDebug("Got: ", _res.bytes()); 197 synchronized(cast()mtx) ret.load(&_res); 198 } 199 } 200 catch (Exception e) { logDebug("Error: ", e.toString()); } 201 } 202 } 203 204 auto handler = Handler(cast(shared) mutex, cast(shared)&m_mod_q, cast(shared)m_g, cast(shared)m_p, cast(shared)&k, cast(shared)&res); 205 Unique!Thread thr = new Thread(&handler.run); 206 thr.start(); 207 s = inverseMod(&k, m_q); 208 thr.join(); 209 synchronized(mutex) r = res.dup; 210 BigInt s_arg = mulAdd(m_x, &r, &i); 211 s = m_mod_q.multiply(&s, &s_arg); 212 } 213 214 SecureVector!ubyte output = SecureVector!ubyte(2*m_q.bytes()); 215 r.binaryEncode(&output[output.length / 2 - r.bytes()]); 216 s.binaryEncode(&output[output.length - s.bytes()]); 217 return output.move; 218 } 219 private: 220 const BigInt* m_q; 221 const BigInt* m_x; 222 const BigInt* m_g; 223 const BigInt* m_p; 224 ModularReducer m_mod_q; 225 } 226 227 /** 228 * Object that can verify a DSA signature 229 */ 230 final class DSAVerificationOperation : Verification 231 { 232 public: 233 this(in PublicKey pkey) { 234 this(cast(DLSchemePublicKey) pkey); 235 } 236 237 this(in DSAPublicKey pkey) { 238 this(pkey.m_pub); 239 } 240 241 this(in DLSchemePublicKey dsa) 242 { 243 assert(dsa.algoName == DSAPublicKey.algoName); 244 m_dsa = dsa; 245 m_q = &m_dsa.groupQ(); 246 m_y = &m_dsa.getY(); 247 m_g = &m_dsa.groupG(); 248 m_p = &m_dsa.groupP(); 249 m_powermod_y_p = FixedBasePowerMod(m_y, m_p); 250 m_mod_p = ModularReducer(*m_p); 251 m_mod_q = ModularReducer(*m_q); 252 } 253 254 override size_t messageParts() const { return 2; } 255 override size_t messagePartSize() const { return m_q.bytes(); } 256 override size_t maxInputBits() const { return m_q.bits(); } 257 258 override bool withRecovery() const { return false; } 259 260 override SecureVector!ubyte verifyMr(const(ubyte)*, size_t) { throw new InvalidState("Message recovery not supported"); } 261 override bool verify(const(ubyte)* msg, size_t msg_len, const(ubyte)* sig, size_t sig_len) 262 { 263 //import core.memory : GC; GC.disable(); scope(exit) GC.enable(); 264 const BigInt* q = &m_mod_q.getModulus(); 265 266 if (sig_len != 2*q.bytes() || msg_len > q.bytes()) 267 return false; 268 269 BigInt r = BigInt(sig, q.bytes()); 270 BigInt s = BigInt(sig + q.bytes(), q.bytes()); 271 BigInt i = BigInt(msg, msg_len); 272 if (r <= 0 || r >= *q || s <= 0 || s >= *q) 273 return false; 274 275 s = inverseMod(&s, q); 276 277 BigInt s_i; 278 s_i.reserve(max(m_g.bytes() + m_g.bytes() % 128, m_y.bytes() + m_y.bytes() % 128)); 279 import core.sync.mutex, core.sync.condition; 280 Mutex mutex = ThreadMem.alloc!Mutex(); 281 scope(exit) { 282 ThreadMem.free(mutex); 283 } 284 285 struct Handler { 286 shared(Mutex) mtx; 287 shared(ModularReducer*) mod_q; 288 shared(const BigInt*)g2; 289 shared(const BigInt*)p2; 290 shared(BigInt*) s2; 291 shared(BigInt*) i2; 292 shared(BigInt*) s_i2; 293 void run() { 294 try { 295 import botan.libstate.libstate : modexpInit, globalState; 296 modexpInit(); // enable quick path for powermod 297 globalState(); 298 BigInt* ret = cast(BigInt*) s_i2; 299 { 300 import memutils.utils; 301 auto powermod_g_p = FixedBasePowerMod(cast(const(BigInt)*)g2, cast(const(BigInt)*)p2); 302 auto mult = (cast(ModularReducer*)mod_q).multiply(cast(const(BigInt)*)s2, cast(const(BigInt)*)i2); 303 BigInt _res = (cast(FixedBasePowerModImpl)powermod_g_p)(cast(BigInt*)&mult); 304 synchronized(cast()mtx) ret.load(&_res); 305 } 306 } catch (Exception e) { 307 logDebug("Got error: ", e.toString); 308 } 309 } 310 } 311 312 auto handler = Handler(cast(shared) mutex, cast(shared)&m_mod_q, cast(shared)m_g, cast(shared)m_p, cast(shared)&s, cast(shared)&i, cast(shared)&s_i); 313 Unique!Thread thr = new Thread(&handler.run); 314 thr.start(); 315 BigInt s_r_0 = (cast(ModularReducer)m_mod_q).multiply(&s, &r); 316 FixedBasePowerModImpl powermod_y_p = cast(FixedBasePowerModImpl)m_powermod_y_p; 317 BigInt s_r = powermod_y_p(&s_r_0); 318 thr.join(); 319 synchronized(mutex) s = m_mod_p.multiply(cast(const(BigInt)*)&s_i, cast(const(BigInt)*)&s_r); 320 auto r2 = m_mod_q.reduce(s.move); 321 return (r2 == r); 322 } 323 324 private: 325 const DLSchemePublicKey m_dsa; 326 const BigInt* m_q; 327 const BigInt* m_y; 328 const BigInt* m_g; 329 const BigInt* m_p; 330 331 FixedBasePowerMod m_powermod_y_p; 332 ModularReducer m_mod_p, m_mod_q; 333 } 334 335 336 static if (BOTAN_TEST): 337 338 import botan.test; 339 import botan.pubkey.test; 340 import botan.rng.auto_rng; 341 import botan.pubkey.pubkey; 342 import botan.codec.hex; 343 import memutils.hashmap; 344 345 import core.atomic; 346 private shared size_t total_tests; 347 348 size_t testPkKeygen(RandomNumberGenerator rng) { 349 size_t fails; 350 string[] dsa_list = ["dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"]; 351 foreach (dsa; dsa_list) { 352 atomicOp!"+="(total_tests, 1); 353 auto key = DSAPrivateKey(rng, DLGroup(dsa)); 354 key.checkKey(rng, true); 355 fails += validateSaveAndLoad(key, rng); 356 } 357 358 return fails; 359 } 360 361 size_t dsaSigKat(string p, 362 string q, 363 string g, 364 string x, 365 string hash, 366 string msg, 367 string nonce, 368 string signature) 369 { 370 atomicOp!"+="(total_tests, 1); 371 372 Unique!AutoSeededRNG rng = new AutoSeededRNG; 373 374 BigInt p_bn = BigInt(p); 375 BigInt q_bn = BigInt(q); 376 BigInt g_bn = BigInt(g); 377 BigInt x_bn = BigInt(x); 378 379 DLGroup group = DLGroup(p_bn, q_bn, g_bn); 380 auto privkey = DSAPrivateKey(*rng, group.move(), x_bn.move()); 381 382 auto pubkey = DSAPublicKey(privkey); 383 384 const string padding = "EMSA1(" ~ hash ~ ")"; 385 PKVerifier verify = PKVerifier(*pubkey, padding); 386 PKSigner sign = PKSigner(*privkey, padding); 387 return validateSignature(verify, sign, "DSA/" ~ hash, msg, *rng, nonce, signature); 388 } 389 390 static if (BOTAN_HAS_TESTS && !SKIP_DSA_TEST) unittest 391 { 392 logDebug("Testing dsa.d ..."); 393 size_t fails; 394 395 Unique!AutoSeededRNG rng = new AutoSeededRNG; 396 397 File dsa_sig = File("test_data/pubkey/dsa.vec", "r"); 398 399 fails += runTestsBb(dsa_sig, "DSA Signature", "Signature", true, 400 (ref HashMap!(string, string) m) 401 { 402 return dsaSigKat(m["P"], m["Q"], m["G"], m["X"], m["Hash"], m["Msg"], m["Nonce"], m["Signature"]); 403 }); 404 405 fails += testPkKeygen(*rng); 406 407 testReport("dsa", total_tests, fails); 408 } 409