// Copyright 2021 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use chrono::prelude::*; use serde::Serialize; use std::convert::Into; use u2f::messages::*; use u2f::protocol::*; use u2f::register::*; static VERSION: &str = "U2F_V2"; pub fn make_challenge(app_id: &str, challenge_bytes: Vec) -> Challenge { let utc: DateTime = Utc::now(); Challenge { challenge: URL_SAFE_NO_PAD.encode(challenge_bytes), timestamp: format!("{utc:?}"), app_id: app_id.to_string(), } } #[derive(Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct RegistrationVerification { pub key_handle: String, pub pubkey: String, pub device_name: Option, } pub fn verify_registration( app_id: String, challenge: String, register_data: String, client_data: String, ) -> crate::Result { let challenge_bytes = URL_SAFE_NO_PAD.decode(challenge)?; let challenge = make_challenge(&app_id, challenge_bytes); let client_data_bytes: Vec = client_data.as_bytes().into(); let client_data_base64 = URL_SAFE_NO_PAD.encode(client_data_bytes); let client = U2f::new(app_id); match client.register_response( challenge, RegisterResponse { registration_data: register_data, client_data: client_data_base64, version: VERSION.to_string(), }, ) { Ok(v) => { let rv = RegistrationVerification { key_handle: URL_SAFE_NO_PAD.encode(&v.key_handle), pubkey: URL_SAFE_NO_PAD.encode(&v.pub_key), device_name: v.device_name, }; Ok(serde_json::to_string(&rv)?) } Err(e) => Err(e.into()), } } #[derive(Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct SignatureVerification { pub counter: u8, } pub fn verify_signature( app_id: String, challenge: String, sign_data: String, client_data: String, key_handle: String, pub_key: String, ) -> crate::Result { let challenge_bytes = URL_SAFE_NO_PAD.decode(challenge)?; let chal = make_challenge(&app_id, challenge_bytes); let client_data_bytes: Vec = client_data.as_bytes().into(); let client_data_base64 = URL_SAFE_NO_PAD.encode(client_data_bytes); let key_handle_bytes = URL_SAFE_NO_PAD.decode(&key_handle)?; let pubkey_bytes = URL_SAFE_NO_PAD.decode(pub_key)?; let client = U2f::new(app_id); let mut _counter: u32 = 0; match client.sign_response( chal, Registration { // here only needs pubkey and keyhandle key_handle: key_handle_bytes, pub_key: pubkey_bytes, attestation_cert: None, device_name: None, }, SignResponse { // here needs client data and sig data and key_handle signature_data: sign_data, client_data: client_data_base64, key_handle, }, _counter, ) { Ok(v) => Ok(v), Err(e) => Err(e.into()), } }