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 }