feat: v0.1.0
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
.idea/
|
||||||
# ---> Rust
|
# ---> Rust
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
|
|||||||
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "pinentry-util"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Hatter Jiang"]
|
||||||
|
repository = "https://git.hatter.ink/hatter/pinentry-util"
|
||||||
|
description = "pinentry util"
|
||||||
|
license = "MIT"
|
||||||
|
keywords = ["pinentry"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pinentry = "0.6.0"
|
||||||
|
rpassword = "7.3.1"
|
||||||
|
secrecy = "0.10.3"
|
||||||
|
zeroize = "1.8.1"
|
||||||
100
src/lib.rs
Normal file
100
src/lib.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
use pinentry::PassphraseInput;
|
||||||
|
use secrecy::ExposeSecret;
|
||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::{env, fs};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
const PIN_ENTRY_ENV: &str = "PIN_ENTRY_CMD";
|
||||||
|
const PIN_ENTRY_1: &str = "/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac";
|
||||||
|
const PIN_ENTRY_DEFAULT: &str = "pinentry";
|
||||||
|
|
||||||
|
pub struct Pin {
|
||||||
|
pin: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Pin {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.pin.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pin {
|
||||||
|
fn from(pin: String) -> Self {
|
||||||
|
Self { pin }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Pin {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Pin").field("pin", &"******").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PinError {
|
||||||
|
Cancel,
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PinError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
PinError::Cancel => f.write_str("PinError:Cancel"),
|
||||||
|
PinError::Other(other) => f.write_str(&format!("PinError:Other({})", other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PinError {
|
||||||
|
fn from(err: String) -> Self {
|
||||||
|
if err.contains("Operation cancelled") {
|
||||||
|
return Self::Cancel;
|
||||||
|
}
|
||||||
|
Self::Other(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_pin_default() -> Result<Pin, PinError> {
|
||||||
|
read_pin(None::<&str>, None::<&str>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_pin<S, T>(description: Option<S>, prompt: Option<T>) -> Result<Pin, PinError>
|
||||||
|
where
|
||||||
|
S: AsRef<str>,
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
let pin_entry = get_pin_entry();
|
||||||
|
let description = description
|
||||||
|
.as_ref()
|
||||||
|
.map(|a| a.as_ref())
|
||||||
|
.unwrap_or("Please input PIN");
|
||||||
|
let prompt = prompt.as_ref().map(|a| a.as_ref()).unwrap_or("PIN: ");
|
||||||
|
|
||||||
|
if let Some(mut input) = PassphraseInput::with_binary(pin_entry) {
|
||||||
|
let secret = input
|
||||||
|
.with_description(&format!("{}.", description))
|
||||||
|
.with_prompt(prompt)
|
||||||
|
.interact();
|
||||||
|
|
||||||
|
match secret {
|
||||||
|
Ok(secret_string) => Ok(Pin::from(secret_string.expose_secret().to_string())),
|
||||||
|
Err(e) => Err(PinError::from(format!("{}", e))),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match rpassword::prompt_password(format!("{}: ", description)) {
|
||||||
|
Ok(pin) => Ok(Pin::from(pin)),
|
||||||
|
Err(e) => Err(PinError::from(format!("{}", e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pin_entry() -> String {
|
||||||
|
if let Ok(pin_entry) = env::var(PIN_ENTRY_ENV) {
|
||||||
|
return pin_entry;
|
||||||
|
}
|
||||||
|
if let Ok(m) = fs::metadata(PIN_ENTRY_1) {
|
||||||
|
if m.is_file() {
|
||||||
|
return PIN_ENTRY_1.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PIN_ENTRY_DEFAULT.to_string()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user