Skip to content

Commit

Permalink
passkey: update passkey challenge format
Browse files Browse the repository at this point in the history
Passkeys now sign the same signing message as other formats resulting in
a slight tweak to the authenticator data shape.
  • Loading branch information
bmwill committed Oct 21, 2024
1 parent 5703b4d commit c5a25ce
Showing 1 changed file with 25 additions and 31 deletions.
56 changes: 25 additions & 31 deletions crates/sui-sdk-types/src/types/crypto/passkey.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::types::Digest;

use super::Secp256r1PublicKey;
use super::Secp256r1Signature;

Expand All @@ -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<u8>,

/// `authenticatorData` is a bytearray that encodes
/// [Authenticator Data](https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data)
Expand Down Expand Up @@ -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
<base64ct::Base64UrlUnpadded as base64ct::Encoding>::decode(
challenge,
&mut challenge_buf,
)
.map_err(|e| {
serde::de::Error::custom(format!(
let challenge =
<base64ct::Base64UrlUnpadded as base64ct::Encoding>::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,
})
Expand Down Expand Up @@ -299,18 +285,14 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator {
(
any::<Secp256r1PublicKey>(),
any::<Secp256r1Signature>(),
any::<Digest>(),
any::<[u8; 3]>(),
vec(any::<u8>(), 32),
vec(any::<u8>(), 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 =
<base64ct::Base64UrlUnpadded as base64ct::Encoding>::encode_string(
&challenge_buf,
&challenge_bytes,
);
let client_data_json = serde_json::to_string(&CollectedClientData {
ty: ClientDataType::Get,
Expand All @@ -322,8 +304,7 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator {
Self {
public_key,
signature,
intent,
digest,
challenge: challenge_bytes,
authenticator_data,
client_data_json,
}
Expand All @@ -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(_)));
}
}

0 comments on commit c5a25ce

Please sign in to comment.