1 /** 2 * Rabin-Williams 3 * 4 * Copyright: 5 * (C) 1999-2007 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.rw; 12 13 import botan.constants; 14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_RW): 15 16 public import botan.pubkey.pubkey; 17 import botan.pubkey.algo.if_algo; 18 import botan.pubkey.pk_ops; 19 import botan.math.numbertheory.reducer; 20 import botan.pubkey.blinding; 21 import botan.math.numbertheory.numthry; 22 import botan.pubkey.algo.keypair; 23 import botan.utils.parsing; 24 import botan.utils.types; 25 import memutils.helpers; 26 import std.algorithm; 27 import std.concurrency; 28 import core.thread; 29 30 struct RWOptions { 31 enum algoName = "RW"; 32 33 /* 34 * Check Private Rabin-Williams Parameters 35 */ 36 static bool checkKey(in IFSchemePrivateKey privkey, RandomNumberGenerator rng, bool strong) 37 { 38 if (!privkey.checkKeyImpl(rng, strong)) 39 return false; 40 41 if (!strong) 42 return true; 43 44 if ((privkey.getE() * privkey.getD()) % (lcm(privkey.getP() - 1, privkey.getQ() - 1) / 2) != 1) 45 return false; 46 47 return signatureConsistencyCheck(rng, privkey, "EMSA2(SHA-1)"); 48 } 49 } 50 /** 51 * Rabin-Williams Public Key 52 */ 53 struct RWPublicKey 54 { 55 public: 56 alias Options = RWOptions; 57 __gshared immutable string algoName = Options.algoName; 58 59 this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 60 { 61 m_owned = true; 62 m_pub = new IFSchemePublicKey(Options(), alg_id, key_bits); 63 } 64 65 this(BigInt mod, BigInt exponent) 66 { 67 m_owned = true; 68 m_pub = new IFSchemePublicKey(Options(), mod.move(), exponent.move()); 69 } 70 71 this(PrivateKey pkey) { m_pub = cast(IFSchemePublicKey) pkey; } 72 this(PublicKey pkey) { m_pub = cast(IFSchemePublicKey) pkey; } 73 74 mixin Embed!(m_pub, m_owned); 75 76 bool m_owned; 77 IFSchemePublicKey m_pub; 78 } 79 80 /** 81 * Rabin-Williams Private Key 82 */ 83 struct RWPrivateKey 84 { 85 public: 86 alias Options = RWOptions; 87 __gshared immutable string algoName = Options.algoName; 88 89 this(in AlgorithmIdentifier alg_id, 90 const ref SecureVector!ubyte key_bits, 91 RandomNumberGenerator rng) 92 { 93 m_owned = true; 94 m_priv = new IFSchemePrivateKey(Options(), rng, alg_id, key_bits); 95 } 96 97 this(RandomNumberGenerator rng, 98 BigInt p, BigInt q, 99 BigInt e, BigInt d = BigInt(0), 100 BigInt n = BigInt(0)) 101 { 102 m_owned = true; 103 m_priv = new IFSchemePrivateKey(Options(), rng, p.move(), q.move(), e.move(), d.move(), n.move()); 104 } 105 106 /* 107 * Create a Rabin-Williams private key 108 */ 109 this(RandomNumberGenerator rng, size_t bits, size_t exp = 2) 110 { 111 if (bits < 1024) 112 throw new InvalidArgument(algoName ~ ": Can't make a key that is only " ~ 113 to!string(bits) ~ " bits long"); 114 if (exp < 2 || exp % 2 == 1) 115 throw new InvalidArgument(algoName ~ ": Invalid encryption exponent"); 116 117 BigInt p, q, e, d, n, d1, d2; 118 119 e = exp; 120 121 do 122 { 123 p = randomPrime(rng, (bits + 1) / 2, e / 2, 3, 4); 124 q = randomPrime(rng, bits - p.bits(), e / 2, ((p % 8 == 3) ? 7 : 3), 8); 125 n = p * q; 126 } while (n.bits() != bits); 127 128 d = inverseMod(e, lcm(p - 1, q - 1) >> 1); 129 130 m_owned = true; 131 m_priv = new IFSchemePrivateKey(Options(), rng, p.move(), q.move(), e.move(), d.move(), n.move()); 132 133 genCheck(rng); 134 } 135 136 mixin Embed!(m_priv, m_owned); 137 138 this(PrivateKey pkey) { m_priv = cast(IFSchemePrivateKey) pkey; } 139 bool m_owned; 140 IFSchemePrivateKey m_priv; 141 } 142 143 /** 144 * Rabin-Williams Signature Operation 145 */ 146 final class RWSignatureOperation : Signature 147 { 148 public: 149 this(in RWPrivateKey pkey) { 150 this(pkey.m_priv); 151 } 152 153 this(in PrivateKey pkey) { 154 this(cast(IFSchemePrivateKey) pkey); 155 } 156 157 this(in IFSchemePrivateKey rw) 158 { 159 assert(rw.algoName == RWPublicKey.algoName); 160 m_n = &rw.getN(); 161 m_e = &rw.getE(); 162 m_q = &rw.getQ(); 163 m_c = &rw.getC(); 164 m_d1 = &rw.getD1(); 165 m_p = &rw.getP(); 166 m_powermod_d2_q = FixedExponentPowerMod(rw.getD2(), rw.getQ()); 167 m_mod_p = ModularReducer(rw.getP()); 168 m_blinder = Blinder.init; 169 } 170 override size_t messageParts() const { return 1; } 171 override size_t messagePartSize() const { return 0; } 172 override size_t maxInputBits() const { return (m_n.bits() - 1); } 173 174 override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len, RandomNumberGenerator rng) 175 { 176 import core.memory : GC; GC.disable(); scope(exit) GC.enable(); 177 import core.thread; 178 rng.addEntropy(msg, msg_len); 179 180 if (!m_blinder.initialized()) { // initialize here because we need rng 181 BigInt k = BigInt(rng, std.algorithm.min(160, m_n.bits() - 1)); 182 auto e = powerMod(k, *m_e, *m_n); 183 m_blinder = Blinder(e, inverseMod(k, *m_n), *m_n); 184 } 185 186 BigInt i = BigInt(msg, msg_len); 187 188 if (i >= *m_n || i % 16 != 12) 189 throw new InvalidArgument("Rabin-Williams: invalid input"); 190 191 if (jacobi(i, *m_n) != 1) 192 i >>= 1; 193 194 i = m_blinder.blind(i); 195 196 BigInt j1; 197 j1.reserve(max(m_e.bytes() + m_e.bytes() % 128, m_q.bytes() + m_q.bytes() % 128, m_n.bytes() + m_n.bytes() % 128)); 198 import core.sync.mutex, core.sync.condition; 199 Mutex mutex = ThreadMem.alloc!Mutex(); 200 scope(exit) { 201 ThreadMem.free(mutex); 202 } 203 204 struct Handler { 205 shared(Mutex) mtx; 206 shared(const BigInt*) d1; 207 shared(const BigInt*) p; 208 shared(BigInt*) i2; 209 shared(BigInt*) j1_2; 210 void run() { 211 try { 212 import botan.libstate.libstate : modexpInit; 213 modexpInit(); // enable quick path for powermod 214 BigInt* ret = cast(BigInt*)j1_2; 215 216 { 217 import memutils.utils; 218 FixedExponentPowerModImpl powermod_d1_p = ThreadMem.alloc!FixedExponentPowerModImpl(*cast(const BigInt*)d1, *cast(const BigInt*)p); 219 scope(exit) ThreadMem.free(powermod_d1_p); 220 BigInt _res = powermod_d1_p(*cast(BigInt*)i2); 221 synchronized(cast()mtx) ret.load( _res ); 222 } 223 } catch (Exception e) { logDebug("Error: ", e.toString()); } 224 } 225 } 226 227 auto handler = Handler(cast(shared)mutex, cast(shared)m_d1, cast(shared)m_p, cast(shared)&i, cast(shared)&j1); 228 Unique!Thread thr = new Thread(&handler.run); 229 thr.start(); 230 const BigInt j2 = (*m_powermod_d2_q)(i); 231 thr.join(); 232 BigInt j3; 233 synchronized(mutex) j3 = m_mod_p.reduce(subMul(j1, j2, *m_c)); 234 BigInt r = m_blinder.unblind(mulAdd(j3, *m_q, j2)); 235 BigInt cmp2 = *m_n - r; 236 BigInt min_val = r.move(); 237 if (cmp2 < min_val) 238 min_val = cmp2.move(); 239 auto ret = BigInt.encode1363(min_val, m_n.bytes()); 240 return ret; 241 } 242 private: 243 const BigInt* m_n; 244 const BigInt* m_e; 245 const BigInt* m_q; 246 const BigInt* m_c; 247 const BigInt* m_d1; 248 const BigInt* m_p; 249 250 FixedExponentPowerMod m_powermod_d1_p, m_powermod_d2_q; 251 ModularReducer m_mod_p; 252 Blinder m_blinder; 253 } 254 255 /** 256 * Rabin-Williams Verification Operation 257 */ 258 final class RWVerificationOperation : Verification 259 { 260 public: 261 this(in PublicKey pkey) { 262 this(cast(IFSchemePublicKey) pkey); 263 } 264 265 this(in RWPublicKey pkey) { 266 this(pkey.m_pub); 267 } 268 269 this(in IFSchemePublicKey rw) 270 { 271 assert(rw.algoName == RWPublicKey.algoName); 272 m_n = &rw.getN(); 273 m_powermod_e_n = FixedExponentPowerMod(rw.getE(), rw.getN()); 274 } 275 override size_t messageParts() const { return 1; } 276 override size_t messagePartSize() const { return 0; } 277 override size_t maxInputBits() const { return (m_n.bits() - 1); } 278 override bool withRecovery() const { return true; } 279 280 override bool verify(const(ubyte)*, size_t, const(ubyte)*, size_t) 281 { 282 throw new InvalidState("Message recovery required"); 283 } 284 285 override SecureVector!ubyte verifyMr(const(ubyte)* msg, size_t msg_len) 286 { 287 BigInt m = BigInt(msg, msg_len); 288 289 if ((m > (*m_n >> 1)) || m.isNegative()) 290 throw new InvalidArgument("RW signature verification: m > n / 2 || m < 0"); 291 292 BigInt r = (*m_powermod_e_n)(m); 293 if (r % 16 == 12) 294 return BigInt.encodeLocked(r); 295 if (r % 8 == 6) 296 return BigInt.encodeLocked(r*2); 297 298 r = (*m_n) - r; 299 if (r % 16 == 12) 300 return BigInt.encodeLocked(r); 301 if (r % 8 == 6) 302 return BigInt.encodeLocked(r*2); 303 304 throw new InvalidArgument("RW signature verification: Invalid signature"); 305 } 306 307 private: 308 const BigInt* m_n; 309 FixedExponentPowerMod m_powermod_e_n; 310 } 311 312 313 static if (BOTAN_TEST): 314 import botan.test; 315 import botan.pubkey.test; 316 import botan.rng.auto_rng; 317 import botan.codec.hex; 318 import core.atomic; 319 import memutils.hashmap; 320 321 shared size_t total_tests; 322 __gshared immutable string padding = "EMSA2(SHA-1)"; 323 324 size_t testPkKeygen(RandomNumberGenerator rng) 325 { 326 atomicOp!"+="(total_tests, 1); 327 size_t fails; 328 auto rw1024 = RWPrivateKey(rng, 1024); 329 rw1024.checkKey(rng, true); 330 fails += validateSaveAndLoad(rw1024, rng); 331 return fails; 332 } 333 size_t rwSigKat(string e, 334 string p, 335 string q, 336 string msg, 337 string signature) 338 { 339 atomicOp!"+="(total_tests, 1); 340 Unique!AutoSeededRNG rng = new AutoSeededRNG; 341 342 auto privkey = RWPrivateKey(*rng, BigInt(p), BigInt(q), BigInt(e)); 343 344 auto pubkey = RWPublicKey(privkey); 345 346 PKVerifier verify = PKVerifier(pubkey, padding); 347 PKSigner sign = PKSigner(privkey, padding); 348 349 return validateSignature(verify, sign, "RW/" ~ padding, msg, *rng, signature); 350 } 351 352 size_t rwSigVerify(string e, 353 string n, 354 string msg, 355 string signature) 356 { 357 atomicOp!"+="(total_tests, 1); 358 359 BigInt e_bn = BigInt(e); 360 BigInt n_bn = BigInt(n); 361 362 auto key = RWPublicKey(n_bn.move(), e_bn.move()); 363 364 PKVerifier verify = PKVerifier(key, padding); 365 366 if (!verify.verifyMessage(hexDecode(msg), hexDecode(signature))) 367 return 1; 368 return 0; 369 } 370 371 static if (BOTAN_HAS_TESTS && !SKIP_RW_TEST) unittest 372 { 373 import core.thread : Thread; 374 //Thread.sleep(10.seconds); 375 logDebug("Testing rw.d ..."); 376 size_t fails = 0; 377 378 Unique!AutoSeededRNG rng = new AutoSeededRNG; 379 380 fails += testPkKeygen(*rng); 381 382 File rw_sig = File("../test_data/pubkey/rw_sig.vec", "r"); 383 File rw_verify = File("../test_data/pubkey/rw_verify.vec", "r"); 384 385 fails += runTestsBb(rw_sig, "RW Signature", "Signature", true, 386 (ref HashMap!(string, string) m) { 387 return rwSigKat(m["E"], m["P"], m["Q"], m["Msg"], m["Signature"]); 388 }); 389 390 fails += runTestsBb(rw_verify, "RW Verify", "Signature", true, 391 (ref HashMap!(string, string) m) { 392 return rwSigVerify(m["E"], m["N"], m["Msg"], m["Signature"]); 393 }); 394 395 testReport("rw", total_tests, fails); 396 }