1 // Copyright 2017 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 #![forbid(
16     anonymous_parameters,
17     box_pointers,
18     missing_copy_implementations,
19     missing_debug_implementations,
20     missing_docs,
21     trivial_casts,
22     trivial_numeric_casts,
23     unsafe_code,
24     unstable_features,
25     unused_extern_crates,
26     unused_import_braces,
27     unused_qualifications,
28     unused_results,
29     variant_size_differences,
30     warnings
31 )]
32 
33 #[cfg(feature = "alloc")]
34 use ring::{
35     error,
36     io::der,
37     rand,
38     signature::{self, KeyPair},
39     test, test_file,
40 };
41 use std::convert::TryFrom;
42 
43 #[cfg(all(target_arch = "wasm32", feature = "wasm32_c"))]
44 use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
45 
46 #[cfg(all(target_arch = "wasm32", feature = "wasm32_c"))]
47 wasm_bindgen_test_configure!(run_in_browser);
48 
49 #[cfg(feature = "alloc")]
50 #[test]
51 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm32_c"), wasm_bindgen_test)]
rsa_from_pkcs8_test()52 fn rsa_from_pkcs8_test() {
53     test::run(
54         test_file!("rsa_from_pkcs8_tests.txt"),
55         |section, test_case| {
56             assert_eq!(section, "");
57 
58             let input = test_case.consume_bytes("Input");
59             let error = test_case.consume_optional_string("Error");
60 
61             match (signature::RsaKeyPair::from_pkcs8(&input), error) {
62                 (Ok(_), None) => (),
63                 (Err(e), None) => panic!("Failed with error \"{}\", but expected to succeed", e),
64                 (Ok(_), Some(e)) => panic!("Succeeded, but expected error \"{}\"", e),
65                 (Err(actual), Some(expected)) => assert_eq!(format!("{}", actual), expected),
66             };
67 
68             Ok(())
69         },
70     );
71 }
72 
73 #[cfg(feature = "alloc")]
74 #[test]
75 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm32_c"), wasm_bindgen_test)]
test_signature_rsa_pkcs1_sign()76 fn test_signature_rsa_pkcs1_sign() {
77     let rng = rand::SystemRandom::new();
78     test::run(
79         test_file!("rsa_pkcs1_sign_tests.txt"),
80         |section, test_case| {
81             assert_eq!(section, "");
82 
83             let digest_name = test_case.consume_string("Digest");
84             let alg = match digest_name.as_ref() {
85                 "SHA256" => &signature::RSA_PKCS1_SHA256,
86                 "SHA384" => &signature::RSA_PKCS1_SHA384,
87                 "SHA512" => &signature::RSA_PKCS1_SHA512,
88                 _ => panic!("Unsupported digest: {}", digest_name),
89             };
90 
91             let private_key = test_case.consume_bytes("Key");
92             let msg = test_case.consume_bytes("Msg");
93             let expected = test_case.consume_bytes("Sig");
94             let result = test_case.consume_string("Result");
95 
96             let key_pair = signature::RsaKeyPair::from_der(&private_key);
97             if result == "Fail-Invalid-Key" {
98                 assert!(key_pair.is_err());
99                 return Ok(());
100             }
101             let key_pair = key_pair.unwrap();
102 
103             // XXX: This test is too slow on Android ARM Travis CI builds.
104             // TODO: re-enable these tests on Android ARM.
105             let mut actual = vec![0u8; key_pair.public_modulus_len()];
106             key_pair
107                 .sign(alg, &rng, &msg, actual.as_mut_slice())
108                 .unwrap();
109             assert_eq!(actual.as_slice() == &expected[..], result == "Pass");
110             Ok(())
111         },
112     );
113 }
114 
115 #[cfg(feature = "alloc")]
116 #[test]
117 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm32_c"), wasm_bindgen_test)]
test_signature_rsa_pss_sign()118 fn test_signature_rsa_pss_sign() {
119     test::run(
120         test_file!("rsa_pss_sign_tests.txt"),
121         |section, test_case| {
122             assert_eq!(section, "");
123 
124             let digest_name = test_case.consume_string("Digest");
125             let alg = match digest_name.as_ref() {
126                 "SHA256" => &signature::RSA_PSS_SHA256,
127                 "SHA384" => &signature::RSA_PSS_SHA384,
128                 "SHA512" => &signature::RSA_PSS_SHA512,
129                 _ => panic!("Unsupported digest: {}", digest_name),
130             };
131 
132             let result = test_case.consume_string("Result");
133             let private_key = test_case.consume_bytes("Key");
134             let key_pair = signature::RsaKeyPair::from_der(&private_key);
135             if key_pair.is_err() && result == "Fail-Invalid-Key" {
136                 return Ok(());
137             }
138             let key_pair = key_pair.unwrap();
139             let msg = test_case.consume_bytes("Msg");
140             let salt = test_case.consume_bytes("Salt");
141             let expected = test_case.consume_bytes("Sig");
142 
143             let rng = test::rand::FixedSliceRandom { bytes: &salt };
144 
145             let mut actual = vec![0u8; key_pair.public_modulus_len()];
146             key_pair.sign(alg, &rng, &msg, actual.as_mut_slice())?;
147             assert_eq!(actual.as_slice() == &expected[..], result == "Pass");
148             Ok(())
149         },
150     );
151 }
152 
153 #[cfg(feature = "alloc")]
154 #[test]
155 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm32_c"), wasm_bindgen_test)]
test_signature_rsa_pkcs1_verify()156 fn test_signature_rsa_pkcs1_verify() {
157     let sha1_params = &[
158         (
159             &signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY,
160             1024,
161         ),
162         (
163             &signature::RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY,
164             2048,
165         ),
166     ];
167     let sha256_params = &[
168         (
169             &signature::RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY,
170             1024,
171         ),
172         (&signature::RSA_PKCS1_2048_8192_SHA256, 2048),
173     ];
174     let sha384_params = &[
175         (&signature::RSA_PKCS1_2048_8192_SHA384, 2048),
176         (&signature::RSA_PKCS1_3072_8192_SHA384, 3072),
177     ];
178     let sha512_params = &[
179         (
180             &signature::RSA_PKCS1_1024_8192_SHA512_FOR_LEGACY_USE_ONLY,
181             1024,
182         ),
183         (&signature::RSA_PKCS1_2048_8192_SHA512, 2048),
184     ];
185     test::run(
186         test_file!("rsa_pkcs1_verify_tests.txt"),
187         |section, test_case| {
188             assert_eq!(section, "");
189 
190             let digest_name = test_case.consume_string("Digest");
191             let params: &[_] = match digest_name.as_ref() {
192                 "SHA1" => sha1_params,
193                 "SHA256" => sha256_params,
194                 "SHA384" => sha384_params,
195                 "SHA512" => sha512_params,
196                 _ => panic!("Unsupported digest: {}", digest_name),
197             };
198 
199             let public_key = test_case.consume_bytes("Key");
200 
201             // Sanity check that we correctly DER-encoded the originally-
202             // provided separate (n, e) components. When we add test vectors
203             // for improperly-encoded signatures, we'll have to revisit this.
204             let key_bits = untrusted::Input::from(&public_key)
205                 .read_all(error::Unspecified, |input| {
206                     der::nested(input, der::Tag::Sequence, error::Unspecified, |input| {
207                         let n_bytes =
208                             der::positive_integer(input)?.big_endian_without_leading_zero();
209                         let _e = der::positive_integer(input)?;
210 
211                         // Because `n_bytes` has the leading zeros stripped and is big-endian, there
212                         // must be less than 8 leading zero bits.
213                         let n_leading_zeros = usize::try_from(n_bytes[0].leading_zeros()).unwrap();
214                         assert!(n_leading_zeros < 8);
215                         Ok((n_bytes.len() * 8) - n_leading_zeros)
216                     })
217                 })
218                 .expect("invalid DER");
219 
220             let msg = test_case.consume_bytes("Msg");
221             let sig = test_case.consume_bytes("Sig");
222             let is_valid = test_case.consume_string("Result") == "P";
223             for &(alg, min_bits) in params {
224                 let width_ok = key_bits >= min_bits;
225                 let actual_result =
226                     signature::UnparsedPublicKey::new(alg, &public_key).verify(&msg, &sig);
227                 assert_eq!(actual_result.is_ok(), is_valid && width_ok);
228             }
229 
230             Ok(())
231         },
232     );
233 }
234 
235 #[cfg(feature = "alloc")]
236 #[test]
237 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm32_c"), wasm_bindgen_test)]
test_signature_rsa_pss_verify()238 fn test_signature_rsa_pss_verify() {
239     test::run(
240         test_file!("rsa_pss_verify_tests.txt"),
241         |section, test_case| {
242             assert_eq!(section, "");
243 
244             let digest_name = test_case.consume_string("Digest");
245             let alg = match digest_name.as_ref() {
246                 "SHA256" => &signature::RSA_PSS_2048_8192_SHA256,
247                 "SHA384" => &signature::RSA_PSS_2048_8192_SHA384,
248                 "SHA512" => &signature::RSA_PSS_2048_8192_SHA512,
249                 _ => panic!("Unsupported digest: {}", digest_name),
250             };
251 
252             let public_key = test_case.consume_bytes("Key");
253 
254             // Sanity check that we correctly DER-encoded the originally-
255             // provided separate (n, e) components. When we add test vectors
256             // for improperly-encoded signatures, we'll have to revisit this.
257             assert!(untrusted::Input::from(&public_key)
258                 .read_all(error::Unspecified, |input| der::nested(
259                     input,
260                     der::Tag::Sequence,
261                     error::Unspecified,
262                     |input| {
263                         let _ = der::positive_integer(input)?;
264                         let _ = der::positive_integer(input)?;
265                         Ok(())
266                     }
267                 ))
268                 .is_ok());
269 
270             let msg = test_case.consume_bytes("Msg");
271             let sig = test_case.consume_bytes("Sig");
272             let is_valid = test_case.consume_string("Result") == "P";
273 
274             let actual_result =
275                 signature::UnparsedPublicKey::new(alg, &public_key).verify(&msg, &sig);
276             assert_eq!(actual_result.is_ok(), is_valid);
277 
278             Ok(())
279         },
280     );
281 }
282 
283 // Test for `primitive::verify()`. Read public key parts from a file
284 // and use them to verify a signature.
285 #[cfg(feature = "alloc")]
286 #[test]
287 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm32_c"), wasm_bindgen_test)]
test_signature_rsa_primitive_verification()288 fn test_signature_rsa_primitive_verification() {
289     test::run(
290         test_file!("rsa_primitive_verify_tests.txt"),
291         |section, test_case| {
292             assert_eq!(section, "");
293             let n = test_case.consume_bytes("n");
294             let e = test_case.consume_bytes("e");
295             let msg = test_case.consume_bytes("Msg");
296             let sig = test_case.consume_bytes("Sig");
297             let expected = test_case.consume_string("Result");
298             let public_key = signature::RsaPublicKeyComponents { n: &n, e: &e };
299             let result = public_key.verify(&signature::RSA_PKCS1_2048_8192_SHA256, &msg, &sig);
300             assert_eq!(result.is_ok(), expected == "Pass");
301             Ok(())
302         },
303     )
304 }
305 
306 #[cfg(feature = "alloc")]
307 #[test]
308 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm32_c"), wasm_bindgen_test)]
rsa_test_public_key_coverage()309 fn rsa_test_public_key_coverage() {
310     const PRIVATE_KEY: &[u8] = include_bytes!("rsa_test_private_key_2048.p8");
311     const PUBLIC_KEY: &[u8] = include_bytes!("rsa_test_public_key_2048.der");
312     const PUBLIC_KEY_DEBUG: &str = include_str!("rsa_test_public_key_2048_debug.txt");
313 
314     let key_pair = signature::RsaKeyPair::from_pkcs8(PRIVATE_KEY).unwrap();
315 
316     // Test `AsRef<[u8]>`
317     assert_eq!(key_pair.public_key().as_ref(), PUBLIC_KEY);
318 
319     // Test `Clone`.
320     let _ = key_pair.public_key().clone();
321 
322     // Test `exponent()`.
323     assert_eq!(
324         &[0x01, 0x00, 0x01],
325         key_pair
326             .public_key()
327             .exponent()
328             .big_endian_without_leading_zero()
329     );
330 
331     // Test `Debug`
332     assert_eq!(PUBLIC_KEY_DEBUG, format!("{:?}", key_pair.public_key()));
333     assert_eq!(
334         format!("RsaKeyPair {{ public_key: {:?} }}", key_pair.public_key()),
335         format!("{:?}", key_pair)
336     );
337 }
338