Skip to content

Commit

Permalink
Add device signer (#46)
Browse files Browse the repository at this point in the history
* Add device signer

* pubkey for device signer not needed

* Added register to device signer

* Propagate sign error

* fix fmt/clippy

* fix tests

* remove pubkey trait

* fix test
  • Loading branch information
broody committed Mar 24, 2024
1 parent 48381d7 commit fd2336f
Show file tree
Hide file tree
Showing 12 changed files with 290 additions and 111 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ toml = "0.8"
u256-literal = "1"
url = "2"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4.42"
wasm-webauthn = { git = "https://github.com/broody/wasm-webauthn" }
webauthn-rs-proto = "0.4"
account-sdk = { path = "crates/account_sdk" }
tokio = { version = "1", features = ["macros", "time"] }
5 changes: 3 additions & 2 deletions crates/account_sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ thiserror.workspace = true
toml.workspace = true
u256-literal.workspace = true
url.workspace = true
wasm-bindgen-futures.workspace = true
wasm-bindgen.workspace = true
webauthn-rs-proto.workspace = true
wasm-webauthn.workspace = true

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio.workspace = true

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-test = "0.3.34"
js-sys = "0.3.69"
web-sys = "0.3.69"
wasm-bindgen-futures = "0.4.42"
8 changes: 1 addition & 7 deletions crates/account_sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ pub mod abigen;
pub mod felt_ser;
pub mod session_token;

#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
pub mod tests;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
pub fn alert(s: &str);
}
18 changes: 16 additions & 2 deletions crates/account_sdk/src/tests/webauthn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use crate::abigen::account::WebauthnPubKey;
use crate::abigen::account::WebauthnSignature;
use crate::{
tests::runners::katana_runner::KatanaRunner,
webauthn_signer::{cairo_args::VerifyWebauthnSignerArgs, P256r1Signer},
webauthn_signer::{
cairo_args::VerifyWebauthnSignerArgs, signers::p256r1::P256r1Signer, signers::Signer,
},
};

#[tokio::test]
Expand Down Expand Up @@ -63,7 +65,11 @@ async fn test_verify_webauthn_explicit() {

let challenge = felt!("0x0169af1f6f99d35e0b80e0140235ec4a2041048868071a8654576223934726f5");
let challenge_bytes = challenge.to_bytes_be().to_vec();
let response = data.signer.sign(&challenge_bytes);
let response = data
.signer
.sign(&challenge_bytes)
.await
.expect("signer error");

let args = VerifyWebauthnSignerArgs::from_response(origin, challenge_bytes, response.clone());

Expand Down Expand Up @@ -110,3 +116,11 @@ async fn test_verify_webauthn_execute() {
.await;
result.unwrap();
}

#[tokio::test]
async fn test_signer() {
let rp_id = "https://localhost:8080".to_string();
let signer = P256r1Signer::random(rp_id);
let calldata = signer.sign("842903840923".as_bytes()).await.unwrap();
dbg!(&calldata);
}
4 changes: 2 additions & 2 deletions crates/account_sdk/src/tests/webauthn/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
};
use crate::{
deploy_contract::single_owner_account, tests::runners::TestnetRunner,
webauthn_signer::P256r1Signer,
webauthn_signer::signers::p256r1::P256r1Signer,
};

use super::super::deployment_test::{declare, deploy};
Expand Down Expand Up @@ -112,7 +112,7 @@ where
}
pub async fn webauthn_executor(
&self,
) -> CartridgeAccount<WebauthnAccount<&JsonRpcClient<HttpTransport>>> {
) -> CartridgeAccount<WebauthnAccount<&JsonRpcClient<HttpTransport>, P256r1Signer>> {
CartridgeAccount::new(
self.address,
WebauthnAccount::new(
Expand Down
33 changes: 17 additions & 16 deletions crates/account_sdk/src/webauthn_signer/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,28 @@ use std::sync::Arc;

use crate::felt_ser::to_felts;

use super::{cairo_args::VerifyWebauthnSignerArgs, P256r1Signer};
use super::{cairo_args::VerifyWebauthnSignerArgs, signers::device::DeviceError};
use crate::abigen::account::WebauthnSignature;
use crate::webauthn_signer::signers::Signer;

pub struct WebauthnAccount<P>
pub struct WebauthnAccount<P, S>
where
P: Provider + Send,
S: Signer + Send,
{
provider: P,
// Later the struct will be generic over the signer type
// and will support "external" signers
signer: P256r1Signer,
signer: S,
address: FieldElement,
chain_id: FieldElement,
block_id: BlockId,
origin: String,
}
impl<P> WebauthnAccount<P>
impl<P, S> WebauthnAccount<P, S>
where
P: Provider + Send,
S: Signer + Send,
{
pub fn new(
provider: P,
signer: P256r1Signer,
address: FieldElement,
chain_id: FieldElement,
) -> Self {
pub fn new(provider: P, signer: S, address: FieldElement, chain_id: FieldElement) -> Self {
Self {
provider,
signer,
Expand Down Expand Up @@ -74,9 +70,10 @@ impl<'a> From<&'a Call> for SerializableCall<'a> {
}
}

impl<P> ExecutionEncoder for WebauthnAccount<P>
impl<P, S> ExecutionEncoder for WebauthnAccount<P, S>
where
P: Provider + Send,
S: Signer + Send,
{
fn encode_calls(&self, calls: &[Call]) -> Vec<FieldElement> {
to_felts(&calls.iter().map(SerializableCall::from).collect::<Vec<_>>())
Expand All @@ -87,13 +84,16 @@ where
pub enum SignError {
#[error("Signer error: {0}")]
Signer(EcdsaSignError),
#[error("Device error: {0}")]
Device(DeviceError),
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<P> Account for WebauthnAccount<P>
impl<P, S> Account for WebauthnAccount<P, S>
where
P: Provider + Send + Sync,
S: Signer + Send + Sync,
{
type SignError = SignError;

Expand All @@ -112,7 +112,7 @@ where
) -> Result<Vec<FieldElement>, Self::SignError> {
let tx_hash = execution.transaction_hash(self.chain_id, self.address, query_only, self);
let challenge = tx_hash.to_bytes_be().to_vec();
let assertion = self.signer.sign(&challenge);
let assertion = self.signer.sign(&challenge).await?;

let args =
VerifyWebauthnSignerArgs::from_response(self.origin.clone(), challenge, assertion);
Expand Down Expand Up @@ -164,9 +164,10 @@ where
}
}

impl<P> ConnectedAccount for WebauthnAccount<P>
impl<P, S> ConnectedAccount for WebauthnAccount<P, S>
where
P: Provider + Send + Sync,
S: Signer + Send + Sync,
{
type Provider = P;

Expand Down
81 changes: 1 addition & 80 deletions crates/account_sdk/src/webauthn_signer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,12 @@
use p256::{
ecdsa::{signature::Signer, Signature, SigningKey, VerifyingKey},
elliptic_curve::sec1::Coordinates,
};
use rand_core::OsRng;
use starknet::{core::types::FieldElement, macros::felt};

use crate::webauthn_signer::credential::{AuthenticatorData, CliendData};

use self::credential::AuthenticatorAssertionResponse;

pub mod account;
pub mod cairo_args;
pub mod credential;
pub mod signers;

pub type U256 = (FieldElement, FieldElement);
pub type Secp256r1Point = (U256, U256);

// "Webauthn v1"
pub const WEBAUTHN_SIGNATURE_TYPE: FieldElement = felt!("0x576562617574686e207631");

#[derive(Debug, Clone)]
pub struct P256r1Signer {
pub signing_key: SigningKey,
rp_id: String,
}

impl P256r1Signer {
pub fn random(rp_id: String) -> Self {
let signing_key = SigningKey::random(&mut OsRng);
Self::new(signing_key, rp_id)
}
pub fn new(signing_key: SigningKey, rp_id: String) -> Self {
Self { signing_key, rp_id }
}
pub fn public_key_bytes(&self) -> ([u8; 32], [u8; 32]) {
P256VerifyingKeyConverter::new(*self.signing_key.verifying_key()).to_bytes()
}
pub fn sign(&self, challenge: &[u8]) -> AuthenticatorAssertionResponse {
use sha2::{digest::Update, Digest, Sha256};

let authenticator_data = AuthenticatorData {
rp_id_hash: [0; 32],
flags: 0b00000101,
sign_count: 0,
};
let client_data_json = CliendData::new(challenge, self.rp_id.clone()).to_json();
let client_data_hash = Sha256::new().chain(client_data_json.clone()).finalize();

let mut to_sign = Into::<Vec<u8>>::into(authenticator_data.clone());
to_sign.append(&mut client_data_hash.to_vec());
let signature: Signature = self.signing_key.try_sign(&to_sign).unwrap();
let signature = signature.to_bytes().to_vec();

AuthenticatorAssertionResponse {
authenticator_data,
client_data_json,
signature,
user_handle: None,
}
}
}

pub struct P256VerifyingKeyConverter {
pub verifying_key: VerifyingKey,
}

impl P256VerifyingKeyConverter {
pub fn new(verifying_key: VerifyingKey) -> Self {
Self { verifying_key }
}
pub fn to_bytes(&self) -> ([u8; 32], [u8; 32]) {
let encoded = &self.verifying_key.to_encoded_point(false);
let (x, y) = match encoded.coordinates() {
Coordinates::Uncompressed { x, y } => (x, y),
_ => panic!("unexpected compression"),
};
(
x.as_slice().try_into().unwrap(),
y.as_slice().try_into().unwrap(),
)
}
}

#[test]
fn test_signer() {
let rp_id = "https://localhost:8080".to_string();
let signer = P256r1Signer::random(rp_id);
let calldata = signer.sign("842903840923".as_bytes());
dbg!(&calldata);
}
Loading

0 comments on commit fd2336f

Please sign in to comment.