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