copy plugin sources

This commit is contained in:
Jonas Kruckenberg
2022-12-14 18:43:39 +01:00
parent c15990d4e2
commit eb3495fb72
145 changed files with 16069 additions and 22 deletions
+217
View File
@@ -0,0 +1,217 @@
// Copyright 2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use authenticator::{
authenticatorservice::AuthenticatorService, statecallback::StateCallback,
AuthenticatorTransports, KeyHandle, RegisterFlags, SignFlags, StatusUpdate,
};
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
use once_cell::sync::Lazy;
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::io;
use std::sync::mpsc::channel;
use std::{convert::Into, sync::Mutex};
static MANAGER: Lazy<Mutex<AuthenticatorService>> = Lazy::new(|| {
let manager = AuthenticatorService::new().expect("The auth service should initialize safely");
Mutex::new(manager)
});
pub fn init_usb() {
let mut manager = MANAGER.lock().unwrap();
// theres also "add_detected_transports()" in the docs?
manager.add_u2f_usb_hid_platform_transports();
}
#[derive(Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Registration {
pub key_handle: String,
pub pubkey: String,
pub register_data: String,
pub client_data: String,
}
pub fn register(application: String, timeout: u64, challenge: String) -> crate::Result<String> {
let (chall_bytes, app_bytes, client_data_string) =
format_client_data(application.as_str(), challenge.as_str());
// log the status rx?
let (status_tx, _status_rx) = channel::<StatusUpdate>();
let mut manager = MANAGER.lock().unwrap();
let (register_tx, register_rx) = channel();
let callback = StateCallback::new(Box::new(move |rv| {
register_tx.send(rv).unwrap();
}));
let res = manager.register(
RegisterFlags::empty(),
timeout,
chall_bytes,
app_bytes,
vec![],
status_tx,
callback,
);
match res {
Ok(_r) => {
let register_result = register_rx
.recv()
.expect("Problem receiving, unable to continue");
if let Err(e) = register_result {
return Err(e.into());
}
let (register_data, device_info) = register_result.unwrap(); // error already has been checked
// println!("Register result: {}", base64::encode(&register_data));
println!("Device info: {}", &device_info);
let (key_handle, public_key) =
_u2f_get_key_handle_and_public_key_from_register_response(&register_data).unwrap();
let key_handle_base64 = encode_config(&key_handle, URL_SAFE_NO_PAD);
let public_key_base64 = encode_config(&public_key, URL_SAFE_NO_PAD);
let register_data_base64 = encode_config(&register_data, URL_SAFE_NO_PAD);
println!("Key Handle: {}", &key_handle_base64);
println!("Public Key: {}", &public_key_base64);
// Ok(base64::encode(&register_data))
// Ok(key_handle_base64)
let res = serde_json::to_string(&Registration {
key_handle: key_handle_base64,
pubkey: public_key_base64,
register_data: register_data_base64,
client_data: client_data_string,
})?;
Ok(res)
}
Err(e) => Err(e.into()),
}
}
#[derive(Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Signature {
pub key_handle: String,
pub sign_data: String,
}
pub fn sign(
application: String,
timeout: u64,
challenge: String,
key_handle: String,
) -> crate::Result<String> {
let credential = match decode_config(&key_handle, URL_SAFE_NO_PAD) {
Ok(v) => v,
Err(e) => {
return Err(e.into());
}
};
let key_handle = KeyHandle {
credential,
transports: AuthenticatorTransports::empty(),
};
let (chall_bytes, app_bytes, _) = format_client_data(application.as_str(), challenge.as_str());
let (sign_tx, sign_rx) = channel();
let callback = StateCallback::new(Box::new(move |rv| {
sign_tx.send(rv).unwrap();
}));
// log the status rx?
let (status_tx, _status_rx) = channel::<StatusUpdate>();
let mut manager = MANAGER.lock().unwrap();
let res = manager.sign(
SignFlags::empty(),
timeout,
chall_bytes,
vec![app_bytes],
vec![key_handle],
status_tx,
callback,
);
match res {
Ok(_v) => {
let sign_result = sign_rx
.recv()
.expect("Problem receiving, unable to continue");
if let Err(e) = sign_result {
return Err(e.into());
}
let (_, handle_used, sign_data, device_info) = sign_result.unwrap();
let sig = encode_config(&sign_data, URL_SAFE_NO_PAD);
println!("Sign result: {}", sig);
println!(
"Key handle used: {}",
encode_config(&handle_used, URL_SAFE_NO_PAD)
);
println!("Device info: {}", &device_info);
println!("Done.");
let res = serde_json::to_string(&Signature {
sign_data: sig,
key_handle: encode_config(&handle_used, URL_SAFE_NO_PAD),
})?;
Ok(res)
}
Err(e) => Err(e.into()),
}
}
fn format_client_data(application: &str, challenge: &str) -> (Vec<u8>, Vec<u8>, String) {
let d = format!(
r#"{{"challenge": "{}", "version": "U2F_V2", "appId": "{}"}}"#,
challenge, application
);
let mut challenge = Sha256::new();
challenge.update(d.as_bytes());
let chall_bytes = challenge.finalize().to_vec();
let mut app = Sha256::new();
app.update(application.as_bytes());
let app_bytes = app.finalize().to_vec();
(chall_bytes, app_bytes, d)
}
fn _u2f_get_key_handle_and_public_key_from_register_response(
register_response: &[u8],
) -> io::Result<(Vec<u8>, Vec<u8>)> {
if register_response[0] != 0x05 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Reserved byte not set correctly",
));
}
// 1: reserved
// 65: public key
// 1: key handle length
// key handle
// x.509 cert
// sig
let key_handle_len = register_response[66] as usize;
let mut public_key = register_response.to_owned();
let mut key_handle = public_key.split_off(67);
let _attestation = key_handle.split_off(key_handle_len);
// remove fist (reserved) and last (handle len) bytes
let pk: Vec<u8> = public_key[1..public_key.len() - 1].to_vec();
Ok((key_handle, pk))
}
+22
View File
@@ -0,0 +1,22 @@
use serde::{Serialize, Serializer};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Base64Decode(#[from] base64::DecodeError),
#[error(transparent)]
JSON(#[from] serde_json::Error),
#[error(transparent)]
U2F(#[from] u2f::u2ferror::U2fError),
#[error(transparent)]
Auth(#[from] authenticator::errors::AuthenticatorError),
}
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
+89
View File
@@ -0,0 +1,89 @@
// Copyright 2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
mod auth;
mod error;
mod u2f;
use tauri::{plugin::Plugin, Invoke, Runtime};
pub use error::Error;
type Result<T> = std::result::Result<T, Error>;
#[tauri::command]
fn init() {
auth::init_usb();
}
#[tauri::command]
fn register(timeout: u64, challenge: String, application: String) -> crate::Result<String> {
auth::register(application, timeout, challenge)
}
#[tauri::command]
fn verify_registration(
challenge: String,
application: String,
register_data: String,
client_data: String,
) -> crate::Result<String> {
u2f::verify_registration(application, challenge, register_data, client_data)
}
#[tauri::command]
fn sign(
timeout: u64,
challenge: String,
application: String,
key_handle: String,
) -> crate::Result<String> {
auth::sign(application, timeout, challenge, key_handle)
}
#[tauri::command]
fn verify_signature(
challenge: String,
application: String,
sign_data: String,
client_data: String,
key_handle: String,
pubkey: String,
) -> crate::Result<u32> {
u2f::verify_signature(
application,
challenge,
sign_data,
client_data,
key_handle,
pubkey,
)
}
pub struct TauriAuthenticator<R: Runtime> {
invoke_handler: Box<dyn Fn(Invoke<R>) + Send + Sync>,
}
impl<R: Runtime> Default for TauriAuthenticator<R> {
fn default() -> Self {
Self {
invoke_handler: Box::new(tauri::generate_handler![
init,
register,
verify_registration,
sign,
verify_signature
]),
}
}
}
impl<R: Runtime> Plugin<R> for TauriAuthenticator<R> {
fn name(&self) -> &'static str {
"authenticator"
}
fn extend_api(&mut self, invoke: Invoke<R>) {
(self.invoke_handler)(invoke)
}
}
+105
View File
@@ -0,0 +1,105 @@
// Copyright 2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
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<u8>) -> Challenge {
let utc: DateTime<Utc> = Utc::now();
Challenge {
challenge: encode_config(&challenge_bytes, URL_SAFE_NO_PAD),
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<String>,
}
pub fn verify_registration(
app_id: String,
challenge: String,
register_data: String,
client_data: String,
) -> crate::Result<String> {
let challenge_bytes = decode_config(&challenge, URL_SAFE_NO_PAD)?;
let challenge = make_challenge(&app_id, challenge_bytes);
let client_data_bytes: Vec<u8> = client_data.as_bytes().into();
let client_data_base64 = encode_config(&client_data_bytes, URL_SAFE_NO_PAD);
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: encode_config(&v.key_handle, URL_SAFE_NO_PAD),
pubkey: encode_config(&v.pub_key, URL_SAFE_NO_PAD),
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<u32> {
let challenge_bytes = decode_config(&challenge, URL_SAFE_NO_PAD)?;
let chal = make_challenge(&app_id, challenge_bytes);
let client_data_bytes: Vec<u8> = client_data.as_bytes().into();
let client_data_base64 = encode_config(&client_data_bytes, URL_SAFE_NO_PAD);
let key_handle_bytes = decode_config(&key_handle, URL_SAFE_NO_PAD)?;
let pubkey_bytes = decode_config(&pub_key, URL_SAFE_NO_PAD)?;
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()),
}
}