315 lines
12 KiB
Rust
315 lines
12 KiB
Rust
// Copyright 2017 Brian Smith.
|
|
//
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
// copyright notice and this permission notice appear in all copies.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
|
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
extern crate ring;
|
|
extern crate untrusted;
|
|
|
|
use ring::{der, error, signature, test};
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
use ring::rand;
|
|
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_from_pkcs8_rsa_encryption_2048() {
|
|
const INPUT: &'static [u8] =
|
|
include_bytes!("pkcs8_test_rsaEncryption_2048_e65537.pk8");
|
|
assert!(signature::RSAKeyPair::from_pkcs8(
|
|
untrusted::Input::from(INPUT)).is_ok());
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_from_pkcs8_rsa_encryption_3072() {
|
|
const INPUT: &'static [u8] =
|
|
include_bytes!("pkcs8_test_rsaEncryption_3072_e65537.pk8");
|
|
assert!(signature::RSAKeyPair::from_pkcs8(
|
|
untrusted::Input::from(INPUT)).is_ok());
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_from_pkcs8_rsa_encryption_invalid_e3() {
|
|
const INPUT: &'static [u8] =
|
|
include_bytes!("pkcs8_test_rsaEncryption_2048_e3.pk8");
|
|
assert!(signature::RSAKeyPair::from_pkcs8(
|
|
untrusted::Input::from(INPUT)).is_err());
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_from_pkcs8_rsa_encryption_2048_truncated() {
|
|
const INPUT: &'static [u8] =
|
|
include_bytes!("pkcs8_test_rsaEncryption_2048_e65537.pk8");
|
|
assert!(signature::RSAKeyPair::from_pkcs8(
|
|
untrusted::Input::from(&INPUT[..(INPUT.len() - 1)])).is_err());
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_from_pkcs8_ecc() {
|
|
// The input is a valid P-256 private key, which isn't a valid RSA key.
|
|
const INPUT: &'static [u8] =
|
|
include_bytes!("pkcs8_test_ecPublicKey_p256.pk8");
|
|
assert!(signature::RSAKeyPair::from_pkcs8(
|
|
untrusted::Input::from(INPUT)).is_err());
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_from_pkcs8_rsa_encryption_ecc() {
|
|
// The input's algorithm ID is rsaEncryption, but it contains a P-256
|
|
// ECPrivateKey.
|
|
const INPUT: &'static [u8] =
|
|
include_bytes!("pkcs8_test_rsaEncryption_ecc.pk8");
|
|
assert!(signature::RSAKeyPair::from_pkcs8(
|
|
untrusted::Input::from(INPUT)).is_err());
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_from_pkcs8_ecc_rsa_private_key() {
|
|
// The input contains an RSAPrivateKey, but marked as an ecPublicKey w/
|
|
// P-256.
|
|
const INPUT: &'static [u8] =
|
|
include_bytes!("pkcs8_test_ecPublicKey_p256_RSAPrivateKey.pk8");
|
|
assert!(signature::RSAKeyPair::from_pkcs8(
|
|
untrusted::Input::from(INPUT)).is_err());
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_signature_rsa_pkcs1_sign() {
|
|
let rng = rand::SystemRandom::new();
|
|
test::from_file("tests/rsa_pkcs1_sign_tests.txt", |section, test_case| {
|
|
assert_eq!(section, "");
|
|
|
|
let digest_name = test_case.consume_string("Digest");
|
|
let alg = match digest_name.as_ref() {
|
|
"SHA256" => &signature::RSA_PKCS1_SHA256,
|
|
"SHA384" => &signature::RSA_PKCS1_SHA384,
|
|
"SHA512" => &signature::RSA_PKCS1_SHA512,
|
|
_ => { panic!("Unsupported digest: {}", digest_name) }
|
|
};
|
|
|
|
let private_key = test_case.consume_bytes("Key");
|
|
let msg = test_case.consume_bytes("Msg");
|
|
let expected = test_case.consume_bytes("Sig");
|
|
let result = test_case.consume_string("Result");
|
|
|
|
let private_key = untrusted::Input::from(&private_key);
|
|
let key_pair = signature::RSAKeyPair::from_der(private_key);
|
|
if result == "Fail-Invalid-Key" {
|
|
assert!(key_pair.is_err());
|
|
return Ok(());
|
|
}
|
|
let key_pair = key_pair.unwrap();
|
|
let key_pair = std::sync::Arc::new(key_pair);
|
|
|
|
// XXX: This test is too slow on Android ARM Travis CI builds.
|
|
// TODO: re-enable these tests on Android ARM.
|
|
let mut signing_state =
|
|
signature::RSASigningState::new(key_pair).unwrap();
|
|
let mut actual: std::vec::Vec<u8> =
|
|
vec![0; signing_state.key_pair().public_modulus_len()];
|
|
signing_state.sign(alg, &rng, &msg, actual.as_mut_slice()).unwrap();
|
|
assert_eq!(actual.as_slice() == &expected[..], result == "Pass");
|
|
Ok(())
|
|
});
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_signature_rsa_pss_sign() {
|
|
// Outputs the same value whenever a certain length is requested (the same
|
|
// as the length of the salt). Otherwise, the rng is used.
|
|
struct DeterministicSalt<'a> {
|
|
salt: &'a [u8],
|
|
rng: &'a rand::SecureRandom
|
|
}
|
|
impl<'a> rand::SecureRandom for DeterministicSalt<'a> {
|
|
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
|
|
let dest_len = dest.len();
|
|
if dest_len != self.salt.len() {
|
|
try!(self.rng.fill(dest));
|
|
} else {
|
|
dest.copy_from_slice(&self.salt);
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
let rng = rand::SystemRandom::new();
|
|
|
|
test::from_file("tests/rsa_pss_sign_tests.txt", |section, test_case| {
|
|
assert_eq!(section, "");
|
|
|
|
let digest_name = test_case.consume_string("Digest");
|
|
let alg = match digest_name.as_ref() {
|
|
"SHA256" => &signature::RSA_PSS_SHA256,
|
|
"SHA384" => &signature::RSA_PSS_SHA384,
|
|
"SHA512" => &signature::RSA_PSS_SHA512,
|
|
_ => { panic!("Unsupported digest: {}", digest_name) }
|
|
};
|
|
|
|
let result = test_case.consume_string("Result");
|
|
let private_key = test_case.consume_bytes("Key");
|
|
let private_key = untrusted::Input::from(&private_key);
|
|
let key_pair = signature::RSAKeyPair::from_der(private_key);
|
|
if key_pair.is_err() && result == "Fail-Invalid-Key" {
|
|
return Ok(());
|
|
}
|
|
let key_pair = key_pair.unwrap();
|
|
let key_pair = std::sync::Arc::new(key_pair);
|
|
let msg = test_case.consume_bytes("Msg");
|
|
let salt = test_case.consume_bytes("Salt");
|
|
let expected = test_case.consume_bytes("Sig");
|
|
|
|
let new_rng = DeterministicSalt { salt: &salt, rng: &rng };
|
|
|
|
let mut signing_state =
|
|
signature::RSASigningState::new(key_pair).unwrap();
|
|
let mut actual: std::vec::Vec<u8> =
|
|
vec![0; signing_state.key_pair().public_modulus_len()];
|
|
try!(signing_state.sign(alg, &new_rng, &msg, actual.as_mut_slice()));
|
|
assert_eq!(actual.as_slice() == &expected[..], result == "Pass");
|
|
Ok(())
|
|
});
|
|
}
|
|
|
|
#[cfg(feature = "rsa_signing")]
|
|
#[test]
|
|
fn test_rsa_key_pair_sync_and_send() {
|
|
const PRIVATE_KEY_DER: &'static [u8] =
|
|
include_bytes!("../src/rsa/signature_rsa_example_private_key.der");
|
|
let key_bytes_der = untrusted::Input::from(PRIVATE_KEY_DER);
|
|
let key_pair = signature::RSAKeyPair::from_der(key_bytes_der).unwrap();
|
|
let key_pair = std::sync::Arc::new(key_pair);
|
|
|
|
let _: &Send = &key_pair;
|
|
let _: &Sync = &key_pair;
|
|
|
|
let signing_state = signature::RSASigningState::new(key_pair).unwrap();
|
|
let _: &Send = &signing_state;
|
|
// TODO: Test that signing_state is NOT Sync; i.e.
|
|
// `let _: &Sync = &signing_state;` must fail
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn test_signature_rsa_pkcs1_verify() {
|
|
test::from_file("tests/rsa_pkcs1_verify_tests.txt", |section, test_case| {
|
|
assert_eq!(section, "");
|
|
|
|
let digest_name = test_case.consume_string("Digest");
|
|
let alg = match digest_name.as_ref() {
|
|
"SHA1" => &signature::RSA_PKCS1_2048_8192_SHA1,
|
|
"SHA256" => &signature::RSA_PKCS1_2048_8192_SHA256,
|
|
"SHA384" => &signature::RSA_PKCS1_2048_8192_SHA384,
|
|
"SHA512" => &signature::RSA_PKCS1_2048_8192_SHA512,
|
|
_ => { panic!("Unsupported digest: {}", digest_name) }
|
|
};
|
|
|
|
let public_key = test_case.consume_bytes("Key");
|
|
let public_key = untrusted::Input::from(&public_key);
|
|
|
|
// Sanity check that we correctly DER-encoded the originally-
|
|
// provided separate (n, e) components. When we add test vectors
|
|
// for improperly-encoded signatures, we'll have to revisit this.
|
|
assert!(public_key.read_all(error::Unspecified, |input| {
|
|
der::nested(input, der::Tag::Sequence, error::Unspecified, |input| {
|
|
let _ = try!(der::positive_integer(input));
|
|
let _ = try!(der::positive_integer(input));
|
|
Ok(())
|
|
})
|
|
}).is_ok());
|
|
|
|
let msg = test_case.consume_bytes("Msg");
|
|
let msg = untrusted::Input::from(&msg);
|
|
|
|
let sig = test_case.consume_bytes("Sig");
|
|
let sig = untrusted::Input::from(&sig);
|
|
|
|
let expected_result = test_case.consume_string("Result");
|
|
|
|
let actual_result = signature::verify(alg, public_key, msg, sig);
|
|
assert_eq!(actual_result.is_ok(), expected_result == "P");
|
|
|
|
Ok(())
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_signature_rsa_pss_verify() {
|
|
test::from_file("tests/rsa_pss_verify_tests.txt", |section, test_case| {
|
|
assert_eq!(section, "");
|
|
|
|
let digest_name = test_case.consume_string("Digest");
|
|
let alg = match digest_name.as_ref() {
|
|
"SHA256" => &signature::RSA_PSS_2048_8192_SHA256,
|
|
"SHA384" => &signature::RSA_PSS_2048_8192_SHA384,
|
|
"SHA512" => &signature::RSA_PSS_2048_8192_SHA512,
|
|
_ => { panic!("Unsupported digest: {}", digest_name) }
|
|
};
|
|
|
|
let public_key = test_case.consume_bytes("Key");
|
|
let public_key = untrusted::Input::from(&public_key);
|
|
|
|
// Sanity check that we correctly DER-encoded the originally-
|
|
// provided separate (n, e) components. When we add test vectors
|
|
// for improperly-encoded signatures, we'll have to revisit this.
|
|
assert!(public_key.read_all(error::Unspecified, |input| {
|
|
der::nested(input, der::Tag::Sequence, error::Unspecified, |input| {
|
|
let _ = try!(der::positive_integer(input));
|
|
let _ = try!(der::positive_integer(input));
|
|
Ok(())
|
|
})
|
|
}).is_ok());
|
|
|
|
let msg = test_case.consume_bytes("Msg");
|
|
let msg = untrusted::Input::from(&msg);
|
|
|
|
let sig = test_case.consume_bytes("Sig");
|
|
let sig = untrusted::Input::from(&sig);
|
|
|
|
let expected_result = test_case.consume_string("Result");
|
|
|
|
let actual_result = signature::verify(alg, public_key, msg, sig);
|
|
assert_eq!(actual_result.is_ok(), expected_result == "P");
|
|
|
|
Ok(())
|
|
});
|
|
}
|
|
|
|
// Test for `primitive::verify()`. Read public key parts from a file
|
|
// and use them to verify a signature.
|
|
#[test]
|
|
fn test_signature_rsa_primitive_verification() {
|
|
test::from_file("tests/rsa_primitive_verify_tests.txt",
|
|
|section, test_case| {
|
|
assert_eq!(section, "");
|
|
let n = test_case.consume_bytes("n");
|
|
let e = test_case.consume_bytes("e");
|
|
let msg = test_case.consume_bytes("Msg");
|
|
let sig = test_case.consume_bytes("Sig");
|
|
let expected = test_case.consume_string("Result");
|
|
let result = signature::primitive::verify_rsa(
|
|
&signature::RSA_PKCS1_2048_8192_SHA256,
|
|
(untrusted::Input::from(&n), untrusted::Input::from(&e)),
|
|
untrusted::Input::from(&msg), untrusted::Input::from(&sig));
|
|
assert_eq!(result.is_ok(), expected == "Pass");
|
|
Ok(())
|
|
})
|
|
}
|