Add support for ed25519 keys (#335)
This commit is contained in:
@@ -27,7 +27,8 @@ use sk_cbor as cbor;
|
||||
use sk_cbor::{cbor_array_vec, cbor_map, cbor_map_options, destructure_cbor_map};
|
||||
|
||||
// Used as the identifier for ECDSA in assertion signatures and COSE.
|
||||
const ES256_ALGORITHM: i64 = -7;
|
||||
pub const ES256_ALGORITHM: i64 = -7;
|
||||
pub const EDDSA_ALGORITHM: i64 = -8;
|
||||
|
||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -503,6 +504,8 @@ impl From<PackedAttestationStatement> for cbor::Value {
|
||||
#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
|
||||
pub enum SignatureAlgorithm {
|
||||
ES256 = ES256_ALGORITHM as isize,
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
EDDSA = EDDSA_ALGORITHM as isize,
|
||||
// This is the default for all numbers not covered above.
|
||||
// Unknown types should be ignored, instead of returning errors.
|
||||
Unknown = 0,
|
||||
@@ -518,6 +521,8 @@ impl From<i64> for SignatureAlgorithm {
|
||||
fn from(int: i64) -> Self {
|
||||
match int {
|
||||
ES256_ALGORITHM => SignatureAlgorithm::ES256,
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
EDDSA_ALGORITHM => SignatureAlgorithm::EDDSA,
|
||||
_ => SignatureAlgorithm::Unknown,
|
||||
}
|
||||
}
|
||||
@@ -720,6 +725,8 @@ pub struct CoseKey {
|
||||
x_bytes: [u8; ecdh::NBYTES],
|
||||
y_bytes: [u8; ecdh::NBYTES],
|
||||
algorithm: i64,
|
||||
key_type: i64,
|
||||
curve: i64,
|
||||
}
|
||||
|
||||
impl CoseKey {
|
||||
@@ -729,8 +736,12 @@ impl CoseKey {
|
||||
const ECDH_ALGORITHM: i64 = -25;
|
||||
// The parameter behind map key 1.
|
||||
const EC2_KEY_TYPE: i64 = 2;
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
const OKP_KEY_TYPE: i64 = 1;
|
||||
// The parameter behind map key -1.
|
||||
const P_256_CURVE: i64 = 1;
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
const ED25519_CURVE: i64 = 6;
|
||||
}
|
||||
|
||||
// This conversion accepts both ECDH and ECDSA.
|
||||
@@ -776,6 +787,8 @@ impl TryFrom<cbor::Value> for CoseKey {
|
||||
x_bytes: *array_ref![x_bytes.as_slice(), 0, ecdh::NBYTES],
|
||||
y_bytes: *array_ref![y_bytes.as_slice(), 0, ecdh::NBYTES],
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -786,12 +799,14 @@ impl From<CoseKey> for cbor::Value {
|
||||
x_bytes,
|
||||
y_bytes,
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
} = cose_key;
|
||||
|
||||
cbor_map! {
|
||||
1 => CoseKey::EC2_KEY_TYPE,
|
||||
1 => key_type,
|
||||
3 => algorithm,
|
||||
-1 => CoseKey::P_256_CURVE,
|
||||
-1 => curve,
|
||||
-2 => x_bytes,
|
||||
-3 => y_bytes,
|
||||
}
|
||||
@@ -807,6 +822,8 @@ impl From<ecdh::PubKey> for CoseKey {
|
||||
x_bytes,
|
||||
y_bytes,
|
||||
algorithm: CoseKey::ECDH_ALGORITHM,
|
||||
key_type: CoseKey::EC2_KEY_TYPE,
|
||||
curve: CoseKey::P_256_CURVE,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -820,6 +837,21 @@ impl From<ecdsa::PubKey> for CoseKey {
|
||||
x_bytes,
|
||||
y_bytes,
|
||||
algorithm: ES256_ALGORITHM,
|
||||
key_type: CoseKey::EC2_KEY_TYPE,
|
||||
curve: CoseKey::P_256_CURVE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
impl From<ed25519_dalek::PublicKey> for CoseKey {
|
||||
fn from(pk: ed25519_dalek::PublicKey) -> Self {
|
||||
CoseKey {
|
||||
x_bytes: pk.to_bytes(),
|
||||
y_bytes: [0u8; 32],
|
||||
key_type: CoseKey::OKP_KEY_TYPE,
|
||||
curve: CoseKey::ED25519_CURVE,
|
||||
algorithm: EDDSA_ALGORITHM,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -832,6 +864,8 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
|
||||
x_bytes,
|
||||
y_bytes,
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
} = cose_key;
|
||||
|
||||
// Since algorithm can be used for different COSE key types, we check
|
||||
@@ -841,6 +875,9 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
|
||||
if algorithm != CoseKey::ECDH_ALGORITHM && algorithm != ES256_ALGORITHM {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
if key_type != CoseKey::EC2_KEY_TYPE || curve != CoseKey::P_256_CURVE {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
ecdh::PubKey::from_coordinates(&x_bytes, &y_bytes)
|
||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
}
|
||||
@@ -854,9 +891,11 @@ impl TryFrom<CoseKey> for ecdsa::PubKey {
|
||||
x_bytes,
|
||||
y_bytes,
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
} = cose_key;
|
||||
|
||||
if algorithm != ES256_ALGORITHM {
|
||||
if algorithm != ES256_ALGORITHM || key_type != CoseKey::EC2_KEY_TYPE || curve != CoseKey::P_256_CURVE {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
ecdsa::PubKey::from_coordinates(&x_bytes, &y_bytes)
|
||||
@@ -904,7 +943,11 @@ impl TryFrom<CoseSignature> for ecdsa::Signature {
|
||||
match cose_signature.algorithm {
|
||||
SignatureAlgorithm::ES256 => ecdsa::Signature::from_bytes(&cose_signature.bytes)
|
||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
||||
SignatureAlgorithm::Unknown => Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
SignatureAlgorithm::EDDSA =>
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
||||
SignatureAlgorithm::Unknown =>
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1564,6 +1607,13 @@ mod test {
|
||||
let signature_algorithm = SignatureAlgorithm::from(alg_int);
|
||||
assert_eq!(signature_algorithm, SignatureAlgorithm::ES256);
|
||||
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
{
|
||||
let alg_int = SignatureAlgorithm::EDDSA as i64;
|
||||
let signature_algorithm = SignatureAlgorithm::from(alg_int);
|
||||
assert_eq!(signature_algorithm, SignatureAlgorithm::EDDSA);
|
||||
}
|
||||
|
||||
let unknown_alg_int = -1;
|
||||
let unknown_algorithm = SignatureAlgorithm::from(unknown_alg_int);
|
||||
assert_eq!(unknown_algorithm, SignatureAlgorithm::Unknown);
|
||||
@@ -1578,6 +1628,16 @@ mod test {
|
||||
let created_cbor: cbor::Value = signature_algorithm.unwrap().into();
|
||||
assert_eq!(created_cbor, cbor_signature_algorithm);
|
||||
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
{
|
||||
let cbor_signature_algorithm: cbor::Value = cbor_int!(EDDSA_ALGORITHM);
|
||||
let signature_algorithm = SignatureAlgorithm::try_from(cbor_signature_algorithm.clone());
|
||||
let expected_signature_algorithm = SignatureAlgorithm::EDDSA;
|
||||
assert_eq!(signature_algorithm, Ok(expected_signature_algorithm));
|
||||
let created_cbor: cbor::Value = signature_algorithm.unwrap().into();
|
||||
assert_eq!(created_cbor, cbor_signature_algorithm);
|
||||
}
|
||||
|
||||
let cbor_unknown_algorithm: cbor::Value = cbor_int!(-1);
|
||||
let unknown_algorithm = SignatureAlgorithm::try_from(cbor_unknown_algorithm);
|
||||
let expected_unknown_algorithm = SignatureAlgorithm::Unknown;
|
||||
@@ -1641,23 +1701,33 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_into_public_key_credential_parameter() {
|
||||
fn test_from_into_public_key_credential_parameter(alg_int: i64, signature_algorithm: SignatureAlgorithm) {
|
||||
let cbor_credential_parameter = cbor_map! {
|
||||
"alg" => ES256_ALGORITHM,
|
||||
"alg" => alg_int,
|
||||
"type" => "public-key",
|
||||
};
|
||||
let credential_parameter =
|
||||
PublicKeyCredentialParameter::try_from(cbor_credential_parameter.clone());
|
||||
let expected_credential_parameter = PublicKeyCredentialParameter {
|
||||
cred_type: PublicKeyCredentialType::PublicKey,
|
||||
alg: SignatureAlgorithm::ES256,
|
||||
alg: signature_algorithm,
|
||||
};
|
||||
assert_eq!(credential_parameter, Ok(expected_credential_parameter));
|
||||
let created_cbor: cbor::Value = credential_parameter.unwrap().into();
|
||||
assert_eq!(created_cbor, cbor_credential_parameter);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_into_ecdsa_public_key_credential_parameter() {
|
||||
test_from_into_public_key_credential_parameter(ES256_ALGORITHM, SignatureAlgorithm::ES256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "with_ed25519")]
|
||||
fn test_from_into_ed25519_public_key_credential_parameter() {
|
||||
test_from_into_public_key_credential_parameter(EDDSA_ALGORITHM, SignatureAlgorithm::EDDSA);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_into_public_key_credential_descriptor() {
|
||||
let cbor_credential_descriptor = cbor_map! {
|
||||
|
||||
Reference in New Issue
Block a user