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.dup);
34 pipe.processMsg(expected.dup);
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.dup;
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.dup;
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 }