Skip to main content

defuse_webauthn/
ed25519.rs

1use crate::Algorithm;
2
3/// [COSE EdDSA (-8) algorithm](https://www.iana.org/assignments/cose/cose.xhtml#algorithms):
4/// ed25519 curve
5pub struct Ed25519;
6
7impl Algorithm for Ed25519 {
8    type Curve = defuse_crypto::ed25519::Ed25519;
9
10    fn maybe_prehash(msg: impl AsRef<[u8]>) -> impl AsRef<[u8]> {
11        // ed25519 does the hashing inside
12        msg
13    }
14}
15
16#[cfg(test)]
17mod tests {
18    use defuse_crypto::{
19        Signer,
20        ed25519::{Ed25519PublicKey, Ed25519Signature, ed25519_dalek},
21    };
22    use hex_literal::hex;
23    use rand::rng;
24    use rstest::rstest;
25
26    use crate::{
27        IgnoreUserVerification, RequireUserVerification, Webauthn, WebauthnAssertion,
28        mock::MockWebauthnSigner,
29    };
30
31    use super::*;
32
33    #[rstest]
34    #[case(
35        hex!("e2e9cb7ac57cb46d4da1ce1d1cc2c33bdfe17407c517916b522724a8ea2c6c50"),
36        hex!("06f269191431372337a0c606a15822e349bd0d5ec317704f97bef1a4ed6f5e1d"),
37        WebauthnAssertion {
38            authenticator_data: hex!("49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97631d00000000").to_vec(),
39            client_data_json: r#"{"type":"webauthn.get","challenge":"BvJpGRQxNyM3oMYGoVgi40m9DV7DF3BPl77xpO1vXh0","origin":"http://localhost:5173","crossOrigin":false}"#.to_string(),
40        },
41        hex!("7cd68c54af557c3d5d7bb6810d90a3efd0eb09e11d13feae3df589d0a54e5629c56dd4e4f6ce48766fccd305135edcbfa1928b0e3131930825c464a68c7d6d0b"),
42    )]
43    fn verify_ok(
44        #[case] public_key: impl Into<Ed25519PublicKey>,
45        #[case] challenge: impl AsRef<[u8]>,
46        #[case] payload: WebauthnAssertion,
47        #[case] signature: impl Into<Ed25519Signature>,
48    ) {
49        assert!(
50            Webauthn::<Ed25519, RequireUserVerification>::verify(
51                &public_key.into().try_into().unwrap(),
52                challenge,
53                &payload,
54                &signature.into().into(),
55            ),
56            "signature is invalid",
57        );
58    }
59
60    #[rstest]
61    #[case("")]
62    #[case("test")]
63    #[case(hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"))]
64    #[tokio::test]
65    async fn sign_verify_ok(#[case] challenge: impl AsRef<[u8]>) {
66        let challenge = challenge.as_ref();
67
68        let signer = ed25519_dalek::SigningKey::generate(&mut rng());
69        let mock = MockWebauthnSigner::<Ed25519, IgnoreUserVerification, _>::new(signer);
70        let (assertion, signature) = mock.sign(challenge).await.unwrap();
71
72        assert!(
73            Webauthn::<Ed25519, IgnoreUserVerification>::verify(
74                &mock.signer().public_key(),
75                challenge,
76                &assertion,
77                &signature
78            ),
79            "signer produced invalid signature"
80        );
81    }
82}