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 FixedBasePowerModImpl powermod_g_p = ThreadMem.alloc!FixedBasePowerModImpl((*cast(const BigInt*)g), (*cast(const BigInt*)p)); 195 scope(exit) ThreadMem.free(powermod_g_p); 196 BigInt _res = (cast(ModularReducer*)mod_q).reduce(powermod_g_p(*cast(BigInt*)k2)); 197 //logDebug("Got: ", _res.bytes()); 198 synchronized(cast()mtx) ret.load(_res); 199 } 200 } 201 catch (Exception e) { logDebug("Error: ", e.toString()); } 202 } 203 } 204 205 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); 206 Unique!Thread thr = new Thread(&handler.run); 207 thr.start(); 208 s = inverseMod(k, *m_q); 209 thr.join(); 210 synchronized(mutex) r = res.dup; 211 BigInt s_arg = mulAdd(*m_x, r, i); 212 s = m_mod_q.multiply(s, s_arg); 213 } 214 215 SecureVector!ubyte output = SecureVector!ubyte(2*m_q.bytes()); 216 r.binaryEncode(&output[output.length / 2 - r.bytes()]); 217 s.binaryEncode(&output[output.length - s.bytes()]); 218 return output.move; 219 } 220 private: 221 const BigInt* m_q; 222 const BigInt* m_x; 223 const BigInt* m_g; 224 const BigInt* m_p; 225 ModularReducer m_mod_q; 226 } 227 228 /** 229 * Object that can verify a DSA signature 230 */ 231 final class DSAVerificationOperation : Verification 232 { 233 public: 234 this(in PublicKey pkey) { 235 this(cast(DLSchemePublicKey) pkey); 236 } 237 238 this(in DSAPublicKey pkey) { 239 this(pkey.m_pub); 240 } 241 242 this(in DLSchemePublicKey dsa) 243 { 244 assert(dsa.algoName == DSAPublicKey.algoName); 245 m_q = &dsa.groupQ(); 246 m_y = &dsa.getY(); 247 m_g = &dsa.groupG(); 248 m_p = &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 FixedBasePowerModImpl powermod_g_p = ThreadMem.alloc!FixedBasePowerModImpl(*cast(const BigInt*)g2, *cast(const BigInt*)p2); 302 scope(exit) ThreadMem.free(powermod_g_p); 303 auto mult = (*cast(ModularReducer*)mod_q).multiply(*cast(BigInt*)s2, *cast(BigInt*)i2); 304 BigInt _res = powermod_g_p(mult); 305 synchronized(cast()mtx) ret.load(_res); 306 } 307 } catch (Exception e) { 308 logDebug("Got error: ", e.toString); 309 } 310 } 311 } 312 313 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); 314 Unique!Thread thr = new Thread(&handler.run); 315 thr.start(); 316 auto mult = m_mod_q.multiply(s, r); 317 BigInt s_r = (*m_powermod_y_p)(mult.move); 318 thr.join(); 319 synchronized(mutex) s = m_mod_p.multiply(s_i, s_r); 320 auto r2 = m_mod_q.reduce(s.move); 321 return (r2 == r); 322 } 323 324 private: 325 const BigInt* m_q; 326 const BigInt* m_y; 327 const BigInt* m_g; 328 const BigInt* m_p; 329 330 FixedBasePowerMod m_powermod_y_p; 331 ModularReducer m_mod_p, m_mod_q; 332 } 333 334 335 static if (BOTAN_TEST): 336 337 import botan.test; 338 import botan.pubkey.test; 339 import botan.rng.auto_rng; 340 import botan.pubkey.pubkey; 341 import botan.codec.hex; 342 import memutils.hashmap; 343 344 import core.atomic; 345 private shared size_t total_tests; 346 347 size_t testPkKeygen(RandomNumberGenerator rng) { 348 size_t fails; 349 string[] dsa_list = ["dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"]; 350 foreach (dsa; dsa_list) { 351 atomicOp!"+="(total_tests, 1); 352 auto key = DSAPrivateKey(rng, DLGroup(dsa)); 353 key.checkKey(rng, true); 354 fails += validateSaveAndLoad(key, rng); 355 } 356 357 return fails; 358 } 359 360 size_t dsaSigKat(string p, 361 string q, 362 string g, 363 string x, 364 string hash, 365 string msg, 366 string nonce, 367 string signature) 368 { 369 atomicOp!"+="(total_tests, 1); 370 371 Unique!AutoSeededRNG rng = new AutoSeededRNG; 372 373 BigInt p_bn = BigInt(p); 374 BigInt q_bn = BigInt(q); 375 BigInt g_bn = BigInt(g); 376 BigInt x_bn = BigInt(x); 377 378 DLGroup group = DLGroup(p_bn, q_bn, g_bn); 379 auto privkey = DSAPrivateKey(*rng, group.move(), x_bn.move()); 380 381 auto pubkey = DSAPublicKey(*privkey); 382 383 const string padding = "EMSA1(" ~ hash ~ ")"; 384 PKVerifier verify = PKVerifier(*pubkey, padding); 385 PKSigner sign = PKSigner(*privkey, padding); 386 return validateSignature(verify, sign, "DSA/" ~ hash, msg, *rng, nonce, signature); 387 } 388 389 static if (BOTAN_HAS_TESTS && !SKIP_DSA_TEST) unittest 390 { 391 logDebug("Testing dsa.d ..."); 392 size_t fails; 393 394 Unique!AutoSeededRNG rng = new AutoSeededRNG; 395 396 File dsa_sig = File("../test_data/pubkey/dsa.vec", "r"); 397 398 fails += runTestsBb(dsa_sig, "DSA Signature", "Signature", true, 399 (ref HashMap!(string, string) m) 400 { 401 return dsaSigKat(m["P"], m["Q"], m["G"], m["X"], m["Hash"], m["Msg"], m["Nonce"], m["Signature"]); 402 }); 403 404 fails += testPkKeygen(*rng); 405 406 testReport("dsa", total_tests, fails); 407 } 408