Skip to content

Commit

Permalink
Ed25519 PKCS#8 testing: Pass all test vectors to both variants.
Browse files Browse the repository at this point in the history
  • Loading branch information
briansmith committed Feb 21, 2022
1 parent d03059e commit 32b2c6c
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 69 deletions.
14 changes: 11 additions & 3 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,15 @@ impl TestCase {
/// even number of hex digits, or as a double-quoted UTF-8 string. The
/// empty (zero-length) value is represented as "".
pub fn consume_bytes(&mut self, key: &str) -> Vec<u8> {
let s = self.consume_string(key);
if s.starts_with('\"') {
self.consume_optional_bytes(key)
.unwrap_or_else(|| panic!("No attribute named \"{}\"", key))
}

/// Like `consume_bytes()` except it returns `None` if the test case
/// doesn't have the attribute.
pub fn consume_optional_bytes(&mut self, key: &str) -> Option<Vec<u8>> {
let s = self.consume_optional_string(key)?;
let result = if s.starts_with('\"') {
// The value is a quoted UTF-8 string.

let mut bytes = Vec::with_capacity(s.as_bytes().len() - 2);
Expand Down Expand Up @@ -243,7 +250,8 @@ impl TestCase {
panic!("{} in {}", err_str, s);
}
}
}
};
Some(result)
}

/// Returns the value of an attribute that is an integer, in decimal
Expand Down
58 changes: 46 additions & 12 deletions tests/ed25519_from_pkcs8_tests.txt
Original file line number Diff line number Diff line change
@@ -1,53 +1,87 @@
# v1. An Ed25519 private key. (This is from BoringSSL's tests.)
Input = 302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
Result-Checked = VersionNotSupported
Result-Maybe-Unchecked = OK
Public = d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a

# v1. The same as the above, but with an invalid NULL parameter. (This is from BoringSSL's tests.)
Input = 3030020100300706032b65700500042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
Result-Checked = WrongAlgorithm
Result-Maybe-Unchecked = WrongAlgorithm

# v1. Sample private key from draft-ietf-curdle-pkix-04.
Input = 302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842
Result-Checked = VersionNotSupported
Result-Maybe-Unchecked = OK
Public = 19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1

# v1. valid except it includes publicKey.
Input = 3053020100300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749a00a1230321001aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77
Result-Checked = VersionNotSupported
Result-Maybe-Unchecked = InvalidEncoding

# v2. The private key ends with a zero byte.
Input = 3053020101300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749a00a1230321001aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77
Result = OK
Result-Checked = OK
Result-Maybe-Unchecked = OK
Public = 1aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77

# v2. The private key's last byte, zero, is omitted.
Input = 3052020101300506032b65700421041fa22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749aa1230321001aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77
Result = InvalidEncoding
Result-Checked = InvalidEncoding
Result-Maybe-Unchecked = InvalidEncoding

# v2. The private key starts with a zero byte.
Input = 3053020101300506032b65700422042000b1a7c20b2b4ed9c78f3686db82f854734cdc95be51def304d98e0cd30bf490a12303210063457cd4dfdd0e98a53796265831d46ac6a5a685f2a54c9697a38b2c800d60ba
Result = OK
Result-Checked = OK
Result-Maybe-Unchecked = OK
Public = 63457cd4dfdd0e98a53796265831d46ac6a5a685f2a54c9697a38b2c800d60ba

# v2. The private key's first byte, zero, is omitted.
Input = 3052020101300506032b65700421041fb1a7c20b2b4ed9c78f3686db82f854734cdc95be51def304d98e0cd30bf490a12303210063457cd4dfdd0e98a53796265831d46ac6a5a685f2a54c9697a38b2c800d60ba
Result = InvalidEncoding
Result-Checked = InvalidEncoding
Result-Maybe-Unchecked = InvalidEncoding

# v2. The public key's first byte is zero.
Input = 3053020101300506032b6570042204202dc67de5186d9193021c0b104d9c6ef24bee2bd395ccb5ed5a2db5f37a2fc1f0a12303210000c17e4d8bbff27c1fb618c23fce988703c7efa3cd590aacac12d3f1e3c90c8c
Result = OK
Result-Checked = OK
Result-Maybe-Unchecked = OK
Public = 00c17e4d8bbff27c1fb618c23fce988703c7efa3cd590aacac12d3f1e3c90c8c

# v2. The public key's first byte, zero, is omitted.
Input = 3052020101300506032b6570042204202dc67de5186d9193021c0b104d9c6ef24bee2bd395ccb5ed5a2db5f37a2fc1f0a122032000c17e4d8bbff27c1fb618c23fce988703c7efa3cd590aacac12d3f1e3c90c8c
Result = InvalidEncoding
Result-Checked = InvalidEncoding
Result-Maybe-Unchecked = InvalidEncoding

# v2. The public key's last byte is zero.
Input = 3053020101300506032b657004220420b2579f555a2eabdabac8d46997b1c08fe8ce63858df124efc29c60dfbb86c349a1230321009d421270ce2fcc08672c41e427214876245c9b0f14ab671b8bb9d266a492e400
Result = OK
Result-Checked = OK
Result-Maybe-Unchecked = OK
Public = 9d421270ce2fcc08672c41e427214876245c9b0f14ab671b8bb9d266a492e400

# v2. The public key's last byte, zero, is omitted (valid ASN.1 DER).
Input = 3052020101300506032b657004220420b2579f555a2eabdabac8d46997b1c08fe8ce63858df124efc29c60dfbb86c349a1220320009d421270ce2fcc08672c41e427214876245c9b0f14ab671b8bb9d266a492e4
Result = InvalidEncoding
Result-Checked = InvalidEncoding
Result-Maybe-Unchecked = InvalidEncoding

# v2. The public key's last byte, zero, has been truncated (invalid ASN.1 DER).
Input = 3053020101300506032b657004220420b2579f555a2eabdabac8d46997b1c08fe8ce63858df124efc29c60dfbb86c349a1230321009d421270ce2fcc08672c41e427214876245c9b0f14ab671b8bb9d266a492e4
Result = InvalidEncoding
Result-Checked = InvalidEncoding
Result-Maybe-Unchecked = InvalidEncoding

# v2. The public key's high bit has been flipped. Ed25519 public keys don't
# have their high bit masked, so this is wrong.
Input = 3053020101300506032b6570042204202dc67de5186d9193021c0b104d9c6ef24bee2bd395ccb5ed5a2db5f37a2fc1f0a12303210000c17e4d8bbff27c1fb618c23fce988703c7efa3cd590aacac12d3f1e3c90c0c
Result = InconsistentComponents
Result-Checked = InconsistentComponents
Result-Maybe-Unchecked = InconsistentComponents

# v2. Valid except the public key field is missing.
Input = 302e020101300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749a00
Result = PublicKeyIsMissing
Result-Checked = PublicKeyIsMissing
Result-Maybe-Unchecked = PublicKeyIsMissing

# v2. Valid except the public key is encoded as [0] instead of [1]; i.e. the
# attributes are invalid and the public key is missing.
Input = 3053020101300506032b657004220420a22efdb713f0e1600d2a5ce948e321ca3a18137c47f15091a12c7126c1749a00a0230321001aeb8e3ee5ba5afd91113466d19f4ea77fa0feffbd8c5adcb499927f12535f77
Result = PublicKeyIsMissing
Result-Checked = PublicKeyIsMissing
Result-Maybe-Unchecked = PublicKeyIsMissing
17 changes: 0 additions & 17 deletions tests/ed25519_from_pkcs8_unchecked_tests.txt

This file was deleted.

87 changes: 50 additions & 37 deletions tests/ed25519_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,60 +112,73 @@ fn test_ed25519_from_seed_and_public_key_misuse() {
assert!(Ed25519KeyPair::from_seed_and_public_key(PUBLIC_KEY, PRIVATE_KEY).is_err());
}

enum FromPkcs8Variant {
Checked,
MaybeUnchecked,
}

#[test]
fn test_ed25519_from_pkcs8_unchecked() {
test_ed25519_from_pkcs8_(
test_file!("ed25519_from_pkcs8_unchecked_tests.txt"),
FromPkcs8Variant::MaybeUnchecked,
Ed25519KeyPair::from_pkcs8_maybe_unchecked,
)
}

#[test]
fn test_ed25519_from_pkcs8() {
test_ed25519_from_pkcs8_(
test_file!("ed25519_from_pkcs8_tests.txt"),
Ed25519KeyPair::from_pkcs8,
)
test_ed25519_from_pkcs8_(FromPkcs8Variant::Checked, Ed25519KeyPair::from_pkcs8)
}

fn test_ed25519_from_pkcs8_(
test_file: test::File,
variant: FromPkcs8Variant,
f: impl Fn(&[u8]) -> Result<Ed25519KeyPair, error::KeyRejected>,
) {
// Just test that we can parse the input.
test::run(test_file, |section, test_case| {
assert_eq!(section, "");
let input = test_case.consume_bytes("Input");
let expected_error = {
let expected_result = test_case.consume_string("Result");
if expected_result == "OK" {
None
} else {
Some(expected_result)
}
};
let expected_public = if expected_error.is_none() {
Some(test_case.consume_bytes("Public"))
} else {
None
};

match f(&input) {
Ok(keypair) => {
assert_eq!(expected_error, None);
assert_eq!(
expected_public.as_deref(),
Some(keypair.public_key().as_ref())
);
}
Err(actual_error) => {
assert_eq!(expected_error, Some(format!("{}", actual_error)));
assert_eq!(expected_public, None);
test::run(
test_file!("ed25519_from_pkcs8_tests.txt"),
|section, test_case| {
assert_eq!(section, "");
let input = test_case.consume_bytes("Input");
let expected_error = {
let expected_checked = test_case.consume_string("Result-Checked");
let expected_maybe_unchecked = test_case.consume_string("Result-Maybe-Unchecked");
let expected_result = match variant {
FromPkcs8Variant::Checked => expected_checked,
FromPkcs8Variant::MaybeUnchecked => expected_maybe_unchecked,
};
if expected_result == "OK" {
None
} else {
Some(expected_result)
}
};
let expected_public = {
let expected_if_no_error = test_case.consume_optional_bytes("Public");
if expected_error.is_none() {
Some(expected_if_no_error.unwrap())
} else {
None
}
};

match f(&input) {
Ok(keypair) => {
assert_eq!(expected_error, None);
assert_eq!(
expected_public.as_deref(),
Some(keypair.public_key().as_ref())
);
}
Err(actual_error) => {
assert_eq!(expected_error, Some(format!("{}", actual_error)));
assert_eq!(expected_public, None);
}
}
}

Ok(())
});
Ok(())
},
);
}

#[test]
Expand Down

0 comments on commit 32b2c6c

Please sign in to comment.