From c5a25ce356a8cbe42ddcc6ec6bab380007790b44 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 21 Oct 2024 12:54:33 -0500 Subject: [PATCH] passkey: update passkey challenge format Passkeys now sign the same signing message as other formats resulting in a slight tweak to the authenticator data shape. --- .../sui-sdk-types/src/types/crypto/passkey.rs | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/crates/sui-sdk-types/src/types/crypto/passkey.rs b/crates/sui-sdk-types/src/types/crypto/passkey.rs index 15d1c082d..a292af039 100644 --- a/crates/sui-sdk-types/src/types/crypto/passkey.rs +++ b/crates/sui-sdk-types/src/types/crypto/passkey.rs @@ -1,5 +1,3 @@ -use crate::types::Digest; - use super::Secp256r1PublicKey; use super::Secp256r1Signature; @@ -14,11 +12,8 @@ pub struct PasskeyAuthenticator { /// Initialized from `user_signature` in `RawPasskeyAuthenticator`. signature: Secp256r1Signature, - /// Valid intent parsed from the first 3 bytes of `client_data_json.challenge`. - intent: [u8; 3], - - /// Valid tx_digest parsed from the last 32 bytes of `client_data_json.challenge`. - digest: Digest, + /// Parsed challenge bytes from `client_data_json.challenge`. + challenge: Vec, /// `authenticatorData` is a bytearray that encodes /// [Authenticator Data](https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data) @@ -147,29 +142,20 @@ mod serialization { origin: _, } = serde_json::from_str(&client_data_json).map_err(serde::de::Error::custom)?; - // challenge is 3 byte intent | 32 byte hash - let mut challenge_buf = [0; 3 + Digest::LENGTH]; - // decode unpadded url endoded base64 data per spec: // https://w3c.github.io/webauthn/#base64url-encoding - ::decode( - challenge, - &mut challenge_buf, - ) - .map_err(|e| { - serde::de::Error::custom(format!( + let challenge = + ::decode_vec(&challenge) + .map_err(|e| { + serde::de::Error::custom(format!( "unable to decode base64urlunpadded into 3-byte intent and 32-byte digest: {e}" )) - })?; - - let intent = challenge_buf[..3].try_into().unwrap(); - let digest = challenge_buf[3..].try_into().unwrap(); + })?; Ok(Self { public_key, signature, - intent, - digest: Digest::new(digest), + challenge, authenticator_data, client_data_json, }) @@ -299,18 +285,14 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { ( any::(), any::(), - any::(), - any::<[u8; 3]>(), + vec(any::(), 32), vec(any::(), 0..32), ) .prop_map( - |(public_key, signature, digest, intent, authenticator_data)| { - let mut challenge_buf = Vec::new(); - challenge_buf.extend_from_slice(&intent); - challenge_buf.extend_from_slice(digest.inner()); + |(public_key, signature, challenge_bytes, authenticator_data)| { let challenge = ::encode_string( - &challenge_buf, + &challenge_bytes, ); let client_data_json = serde_json::to_string(&CollectedClientData { ty: ClientDataType::Get, @@ -322,8 +304,7 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { Self { public_key, signature, - intent, - digest, + challenge: challenge_bytes, authenticator_data, client_data_json, } @@ -332,3 +313,16 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { .boxed() } } + +#[cfg(test)] +mod tests { + use crate::types::UserSignature; + + #[test] + fn base64_encoded_passkey_user_signature() { + let b64 = "BiVYDmenOnqS+thmz5m5SrZnWaKXZLVxgh+rri6LHXs25B0AAAAAnQF7InR5cGUiOiJ3ZWJhdXRobi5nZXQiLCAiY2hhbGxlbmdlIjoiQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTE3MyIsImNyb3NzT3JpZ2luIjpmYWxzZSwgInVua25vd24iOiAidW5rbm93biJ9YgJMwqcOmZI7F/N+K5SMe4DRYCb4/cDWW68SFneSHoD2GxKKhksbpZ5rZpdrjSYABTCsFQQBpLORzTvbj4edWKd/AsEBeovrGvHR9Ku7critg6k7qvfFlPUngujXfEzXd8Eg"; + + let sig = UserSignature::from_base64(b64).unwrap(); + assert!(matches!(sig, UserSignature::Passkey(_))); + } +}