mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-05-29 13:31:27 +02:00
copy plugin sources
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "tauri-bindgen-host-macro"
|
||||
authors.workspace = true
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tauri.workspace = true
|
||||
log.workspace = true
|
||||
thiserror.workspace = true
|
||||
authenticator = "0.3.1"
|
||||
once_cell = "1.9"
|
||||
sha2 = "0.10"
|
||||
base64 = { version = "^0.13" }
|
||||
u2f = "0.2"
|
||||
chrono = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8"
|
||||
rusty-fork = "0.3"
|
||||
@@ -0,0 +1,45 @@
|
||||
var d=Object.defineProperty;var e=(c,a)=>{for(var b in a)d(c,b,{get:a[b],enumerable:!0});};
|
||||
|
||||
var f={};e(f,{convertFileSrc:()=>w,invoke:()=>c,transformCallback:()=>s});function u(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function s(e,r=!1){let n=u(),t=`_${n}`;return Object.defineProperty(window,t,{value:o=>(r&&Reflect.deleteProperty(window,t),e==null?void 0:e(o)),writable:!1,configurable:!0}),n}async function c(e,r={}){return new Promise((n,t)=>{let o=s(i=>{n(i),Reflect.deleteProperty(window,`_${a}`);},!0),a=s(i=>{t(i),Reflect.deleteProperty(window,`_${o}`);},!0);window.__TAURI_IPC__({cmd:e,callback:o,error:a,...r});})}function w(e,r="asset"){let n=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?`https://${r}.localhost/${n}`:`${r}://localhost/${n}`}
|
||||
|
||||
class Authenticator {
|
||||
async init() {
|
||||
return await c("plugin:authenticator|init");
|
||||
}
|
||||
async register(challenge, application) {
|
||||
return await c("plugin:authenticator|register", {
|
||||
timeout: 10000,
|
||||
challenge,
|
||||
application,
|
||||
});
|
||||
}
|
||||
async verifyRegistration(challenge, application, registerData, clientData) {
|
||||
return await c("plugin:authenticator|verify_registration", {
|
||||
challenge,
|
||||
application,
|
||||
registerData,
|
||||
clientData,
|
||||
});
|
||||
}
|
||||
async sign(challenge, application, keyHandle) {
|
||||
return await c("plugin:authenticator|sign", {
|
||||
timeout: 10000,
|
||||
challenge,
|
||||
application,
|
||||
keyHandle,
|
||||
});
|
||||
}
|
||||
async verifySignature(challenge, application, signData, clientData, keyHandle, pubkey) {
|
||||
return await c("plugin:authenticator|verify_signature", {
|
||||
challenge,
|
||||
application,
|
||||
signData,
|
||||
clientData,
|
||||
keyHandle,
|
||||
pubkey,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { Authenticator };
|
||||
//# sourceMappingURL=index.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.min.js","sources":["../../../../node_modules/.pnpm/@tauri-apps+api@1.2.0/node_modules/@tauri-apps/api/chunk-FEIY7W7S.js","../../../../node_modules/.pnpm/@tauri-apps+api@1.2.0/node_modules/@tauri-apps/api/chunk-RCPA6UVN.js","../index.ts"],"sourcesContent":["var d=Object.defineProperty;var e=(c,a)=>{for(var b in a)d(c,b,{get:a[b],enumerable:!0})};export{e as a};\n","import{a as d}from\"./chunk-FEIY7W7S.js\";var f={};d(f,{convertFileSrc:()=>w,invoke:()=>c,transformCallback:()=>s});function u(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function s(e,r=!1){let n=u(),t=`_${n}`;return Object.defineProperty(window,t,{value:o=>(r&&Reflect.deleteProperty(window,t),e==null?void 0:e(o)),writable:!1,configurable:!0}),n}async function c(e,r={}){return new Promise((n,t)=>{let o=s(i=>{n(i),Reflect.deleteProperty(window,`_${a}`)},!0),a=s(i=>{t(i),Reflect.deleteProperty(window,`_${o}`)},!0);window.__TAURI_IPC__({cmd:e,callback:o,error:a,...r})})}function w(e,r=\"asset\"){let n=encodeURIComponent(e);return navigator.userAgent.includes(\"Windows\")?`https://${r}.localhost/${n}`:`${r}://localhost/${n}`}export{s as a,c as b,w as c,f as d};\n",null],"names":["d","invoke"],"mappings":"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"}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { invoke } from '@tauri-apps/api/tauri';
|
||||
|
||||
class Authenticator {
|
||||
async init() {
|
||||
return await invoke("plugin:authenticator|init");
|
||||
}
|
||||
async register(challenge, application) {
|
||||
return await invoke("plugin:authenticator|register", {
|
||||
timeout: 10000,
|
||||
challenge,
|
||||
application,
|
||||
});
|
||||
}
|
||||
async verifyRegistration(challenge, application, registerData, clientData) {
|
||||
return await invoke("plugin:authenticator|verify_registration", {
|
||||
challenge,
|
||||
application,
|
||||
registerData,
|
||||
clientData,
|
||||
});
|
||||
}
|
||||
async sign(challenge, application, keyHandle) {
|
||||
return await invoke("plugin:authenticator|sign", {
|
||||
timeout: 10000,
|
||||
challenge,
|
||||
application,
|
||||
keyHandle,
|
||||
});
|
||||
}
|
||||
async verifySignature(challenge, application, signData, clientData, keyHandle, pubkey) {
|
||||
return await invoke("plugin:authenticator|verify_signature", {
|
||||
challenge,
|
||||
application,
|
||||
signData,
|
||||
clientData,
|
||||
keyHandle,
|
||||
pubkey,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { Authenticator };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.mjs","sources":["../index.ts"],"sourcesContent":[null],"names":[],"mappings":";;MAEa,aAAa,CAAA;AACxB,IAAA,MAAM,IAAI,GAAA;AACR,QAAA,OAAO,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;KAClD;AAED,IAAA,MAAM,QAAQ,CAAC,SAAiB,EAAE,WAAmB,EAAA;AACnD,QAAA,OAAO,MAAM,MAAM,CAAC,+BAA+B,EAAE;AACnD,YAAA,OAAO,EAAE,KAAK;YACd,SAAS;YACT,WAAW;AACZ,SAAA,CAAC,CAAC;KACJ;IAED,MAAM,kBAAkB,CACtB,SAAiB,EACjB,WAAmB,EACnB,YAAoB,EACpB,UAAkB,EAAA;AAElB,QAAA,OAAO,MAAM,MAAM,CAAC,0CAA0C,EAAE;YAC9D,SAAS;YACT,WAAW;YACX,YAAY;YACZ,UAAU;AACX,SAAA,CAAC,CAAC;KACJ;AAED,IAAA,MAAM,IAAI,CACR,SAAiB,EACjB,WAAmB,EACnB,SAAiB,EAAA;AAEjB,QAAA,OAAO,MAAM,MAAM,CAAC,2BAA2B,EAAE;AAC/C,YAAA,OAAO,EAAE,KAAK;YACd,SAAS;YACT,WAAW;YACX,SAAS;AACV,SAAA,CAAC,CAAC;KACJ;AAED,IAAA,MAAM,eAAe,CACnB,SAAiB,EACjB,WAAmB,EACnB,QAAgB,EAChB,UAAkB,EAClB,SAAiB,EACjB,MAAc,EAAA;AAEd,QAAA,OAAO,MAAM,MAAM,CAAC,uCAAuC,EAAE;YAC3D,SAAS;YACT,WAAW;YACX,QAAQ;YACR,UAAU;YACV,SAAS;YACT,MAAM;AACP,SAAA,CAAC,CAAC;KACJ;AACF;;;;"}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
|
||||
export class Authenticator {
|
||||
async init(): Promise<void> {
|
||||
return await invoke("plugin:authenticator|init");
|
||||
}
|
||||
|
||||
async register(challenge: string, application: string): Promise<string> {
|
||||
return await invoke("plugin:authenticator|register", {
|
||||
timeout: 10000,
|
||||
challenge,
|
||||
application,
|
||||
});
|
||||
}
|
||||
|
||||
async verifyRegistration(
|
||||
challenge: string,
|
||||
application: string,
|
||||
registerData: string,
|
||||
clientData: string
|
||||
): Promise<string> {
|
||||
return await invoke("plugin:authenticator|verify_registration", {
|
||||
challenge,
|
||||
application,
|
||||
registerData,
|
||||
clientData,
|
||||
});
|
||||
}
|
||||
|
||||
async sign(
|
||||
challenge: string,
|
||||
application: string,
|
||||
keyHandle: string
|
||||
): Promise<string> {
|
||||
return await invoke("plugin:authenticator|sign", {
|
||||
timeout: 10000,
|
||||
challenge,
|
||||
application,
|
||||
keyHandle,
|
||||
});
|
||||
}
|
||||
|
||||
async verifySignature(
|
||||
challenge: string,
|
||||
application: string,
|
||||
signData: string,
|
||||
clientData: string,
|
||||
keyHandle: string,
|
||||
pubkey: string
|
||||
): Promise<number> {
|
||||
return await invoke("plugin:authenticator|verify_signature", {
|
||||
challenge,
|
||||
application,
|
||||
signData,
|
||||
clientData,
|
||||
keyHandle,
|
||||
pubkey,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "tauri-plugin-authenticator-api",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"type": "module",
|
||||
"browser": "dist/index.min.js",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
"import": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"browser": "./dist/index.min.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/*.map",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"devDependencies": {
|
||||
"tslib": "^2.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
import { createConfig } from "../../../shared/rollup.config.mjs";
|
||||
|
||||
export default createConfig({
|
||||
pkg: JSON.parse(
|
||||
readFileSync(new URL("./package.json", import.meta.url), "utf8")
|
||||
),
|
||||
external: [/^@tauri-apps\/api/],
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
../../../shared/tsconfig.json
|
||||
@@ -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(®ister_data));
|
||||
println!("Device info: {}", &device_info);
|
||||
|
||||
let (key_handle, public_key) =
|
||||
_u2f_get_key_handle_and_public_key_from_register_response(®ister_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(®ister_data, URL_SAFE_NO_PAD);
|
||||
println!("Key Handle: {}", &key_handle_base64);
|
||||
println!("Public Key: {}", &public_key_base64);
|
||||
|
||||
// Ok(base64::encode(®ister_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))
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user