1 /** 2 * Public Key Cryptography Unit Testing 3 * 4 * Copyright: 5 * (C) 2009 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.test; 12 13 import botan.constants; 14 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_TEST): 15 16 import botan.test; 17 import botan.rng.test; 18 import botan.asn1.oids; 19 import botan.codec.hex; 20 import botan.pubkey.x509_key; 21 import botan.pubkey.pkcs8; 22 import botan.pubkey.pubkey; 23 import botan.rng.auto_rng; 24 import botan.utils.mem_ops; 25 import botan.filters.filters; 26 import botan.filters.hex_filt; 27 import botan.math.numbertheory.numthry; 28 29 void dumpData(const ref Vector!ubyte output, const ref Vector!ubyte expected) 30 { 31 Pipe pipe = Pipe(new HexEncoder); 32 33 pipe.processMsg(output.clone); 34 pipe.processMsg(expected.clone); 35 logTrace("Got: " ~ pipe.toString(0)); 36 logTrace("Exp: " ~ pipe.toString(1)); 37 } 38 39 size_t validateSaveAndLoad(const PrivateKey priv_key, RandomNumberGenerator rng) 40 { 41 string name = priv_key.algoName(); 42 43 size_t fails = 0; 44 string pub_pem = x509_key.PEM_encode(priv_key); 45 46 try 47 { 48 DataSourceMemory input_pub = DataSourceMemory(pub_pem); 49 Unique!PublicKey restored_pub = x509_key.loadKey(cast(DataSource)input_pub); 50 51 if (!restored_pub) 52 { 53 logError("Could not recover " ~ name ~ " public key"); 54 ++fails; 55 } 56 else if (restored_pub.checkKey(rng, true) == false) 57 { 58 logError("Restored pubkey failed self tests " ~ name); 59 ++fails; 60 } 61 } 62 catch(Exception e) 63 { 64 logError("Exception during load of " ~ name ~ " key: " ~ e.msg); 65 logTrace("PEM for pubkey was: " ~ pub_pem); 66 ++fails; 67 } 68 69 string priv_pem = pkcs8.PEM_encode(priv_key); 70 try { 71 auto input_priv = DataSourceMemory(priv_pem); 72 Unique!PrivateKey restored_priv = pkcs8.loadKey(cast(DataSource)input_priv, rng); 73 74 if (!restored_priv) 75 { 76 logError("Could not recover " ~ name ~ " private key"); 77 ++fails; 78 } 79 else if (restored_priv.checkKey(rng, true) == false) 80 { 81 logError("Restored privkey failed self tests " ~ name); 82 ++fails; 83 } 84 } 85 catch(Exception e) 86 { 87 logError("Exception during load of " ~ name ~ " key: " ~ e.msg); 88 logTrace("PEM for pubkey was: " ~ priv_pem); 89 ++fails; 90 } 91 return fails; 92 } 93 94 ubyte nonzeroByte(RandomNumberGenerator rng) 95 { 96 ubyte b = 0; 97 while(b == 0) 98 b = rng.nextByte(); 99 return b; 100 } 101 102 string PKTEST(string expr, string msg) 103 { 104 return ` 105 { 106 const bool test_result = ` ~ expr ~ `; 107 if (!test_result) 108 { 109 logError("Test " ~ ` ~ expr ~ ` ~ " failed: ` ~ msg ~ `"); 110 ++fails; 111 } 112 } 113 `; 114 } 115 116 size_t validateEncryption(PKEncryptor e, PKDecryptor d, 117 string algo, string input, 118 string random, string exp) 119 { 120 Vector!ubyte message = hexDecode(input); 121 Vector!ubyte expected = hexDecode(exp); 122 auto rng = scoped!FixedOutputRNG(hexDecode(random)); 123 124 size_t fails = 0; 125 126 const Vector!ubyte ctext = e.encrypt(message, rng); 127 if (ctext != expected) 128 { 129 logError("FAILED (encrypt): " ~ algo); 130 dumpData(ctext, expected); 131 ++fails; 132 } 133 134 Vector!ubyte decrypted = unlock(d.decrypt(ctext)); 135 136 if (decrypted != message) 137 { 138 logError("FAILED (decrypt): " ~ algo); 139 dumpData(decrypted, message); 140 ++fails; 141 } 142 143 if (algo.canFind("/Raw") == -1) 144 { 145 Unique!AutoSeededRNG arng = new AutoSeededRNG; 146 147 for(size_t i = 0; i != ctext.length; ++i) 148 { 149 Vector!ubyte bad_ctext = ctext.clone; 150 151 bad_ctext[i] ^= nonzeroByte(*arng); 152 153 assert(bad_ctext != ctext, "Made them different"); 154 155 auto bad_ptext = unlock(d.decrypt(bad_ctext)); 156 logError(algo ~ " failed - decrypted bad data"); 157 logTrace(hexEncode(bad_ctext) ~ " . " ~ hexEncode(bad_ptext)); 158 logTrace(hexEncode(ctext) ~ " . " ~ hexEncode(decrypted)); 159 ++fails; 160 } 161 } 162 163 return fails; 164 } 165 166 size_t validateSignature(ref PKVerifier v, ref PKSigner s, string algo, 167 string input, 168 RandomNumberGenerator rng, 169 string exp) 170 { 171 return validateSignature(v, s, algo, input, rng, rng, exp); 172 } 173 174 size_t validateSignature(ref PKVerifier v, ref PKSigner s, string algo, 175 string input, 176 RandomNumberGenerator signer_rng, 177 RandomNumberGenerator test_rng, 178 string exp) 179 { 180 Vector!ubyte message = hexDecode(input); 181 Vector!ubyte expected = hexDecode(exp); 182 Vector!ubyte sig = s.signMessage(message, signer_rng); 183 size_t fails = 0; 184 185 if (sig != expected) 186 { 187 logError("FAILED (sign): " ~ algo); 188 dumpData(sig, expected); 189 ++fails; 190 } 191 192 mixin( PKTEST(` v.verifyMessage(message, sig) `, "Correct signature is valid") ); 193 194 clearMem(sig.ptr, sig.length); 195 196 mixin( PKTEST(` !v.verifyMessage(message, sig) `, "All-zero signature is invalid") ); 197 198 for(size_t i = 0; i != 3; ++i) 199 { 200 auto bad_sig = sig.clone; 201 202 const size_t idx = (test_rng.nextByte() * 256 + test_rng.nextByte()) % sig.length; 203 bad_sig[idx] ^= nonzeroByte(test_rng); 204 205 mixin( PKTEST(` !v.verifyMessage(message, bad_sig) `, "Incorrect signature is invalid") ); 206 } 207 return fails; 208 } 209 210 size_t validateSignature(ref PKVerifier v, ref PKSigner s, string algo, 211 string input, 212 RandomNumberGenerator rng, 213 string random, 214 string exp) 215 { 216 auto fixed_rng = scoped!FixedOutputRNG(random); 217 218 return validateSignature(v, s, algo, input, fixed_rng, rng, exp); 219 } 220 221 size_t validateKas(PKKeyAgreement kas, string algo, 222 const Vector!ubyte pubkey, string output, 223 size_t keylen) 224 { 225 Vector!ubyte expected = hexDecode(output); 226 227 Vector!ubyte got = unlock(kas.deriveKey(keylen, pubkey).bitsOf()); 228 229 size_t fails = 0; 230 231 if (got != expected) 232 { 233 logError("FAILED: " ~ algo); 234 dumpData(got, expected); 235 ++fails; 236 } 237 238 return fails; 239 }