Skip to content

Commit

Permalink
completing signing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Apr 24, 2024
1 parent 37e1e58 commit 6161544
Show file tree
Hide file tree
Showing 10 changed files with 1,012 additions and 306 deletions.
117 changes: 116 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ required-features = ["cli"]

[dependencies]
amplify = "4.6.0"
secp256k1 = { version = "0.29.0", features = ["rand", "global-context"] }
secp256k1 = { version = "0.29.0", features = ["rand", "global-context", "rand-std"] }
rand = "0.8.5"
chrono = "0.4.38"
clap = { version = "4.5.4", features = ["derive"], optional = true }
shellexpand = { version = "3.1.0", optional = true }
sha2 = "0.10.8"
fluent-uri = "0.1.4"
percent-encoding = "2.3.1"

rpassword = { version = "7.3.1", optional = true }
aes = { version = "0.8.4", optional = true }
crossbeam-channel = { version = "0.5.12", optional = true }
Expand Down
96 changes: 84 additions & 12 deletions src/baid64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,26 @@ use base64::Engine;
use sha2::Digest;

pub const HRI_MAX_LEN: usize = 16;
const LEN: usize = 32;

pub const BAID64_ALPHABET: &str =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$";

pub trait ToBaid64 {
fn check<const LEN: usize>(hri: &'static str, payload: [u8; LEN]) -> u32 {
let key = sha2::Sha256::digest(hri.as_bytes());
let mut sha = sha2::Sha256::new_with_prefix(key);
sha.update(&payload);
let sha = sha.finalize();
u32::from_le_bytes([sha[0], sha[1], sha[1], sha[2]])
}

pub trait ToBaid64<const LEN: usize = 32> {
const HRI: &'static str;
const CHUNKING: bool;
const PREFIX: bool;
const MNEMONIC: bool;

fn to_baid64_payload(&self) -> [u8; 32];
fn to_baid64(&self) -> Baid64 {
fn to_baid64_payload(&self) -> [u8; LEN];
fn to_baid64(&self) -> Baid64<LEN> {
Baid64::with(
Self::HRI,
self.to_baid64_payload(),
Expand All @@ -50,8 +57,77 @@ pub trait ToBaid64 {
fn fmt_baid64(&self, f: &mut Formatter) -> fmt::Result { Display::fmt(&self.to_baid64(), f) }
}

#[derive(Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum Baid64ParseError {
/// invalid human-readable prefix in {0} ({1} is expected).
InvalidHri(String, &'static str),

/// invalid length of identifier {0}.
InvalidLen(String),

/// invalid checksum value in {0}.
InvalidChecksum(String),

#[from]
#[display(inner)]
InvalidMnemonic(mnemonic::Error),

#[from]
#[display(inner)]
Base64(base64::DecodeError),
}

pub trait FromBaid64Str<const LEN: usize = 32>: ToBaid64<LEN> + From<[u8; LEN]> {
fn from_baid64_str(mut s: &str) -> Result<Self, Baid64ParseError> {
let orig = s;

use base64::alphabet::Alphabet;
use base64::engine::general_purpose::NO_PAD;
use base64::engine::GeneralPurpose;

let mut checksum = 0u32;

if let Some((hri, rest)) = s.rsplit_once(':') {
if hri != Self::HRI {
return Err(Baid64ParseError::InvalidHri(orig.to_owned(), Self::HRI));
}
s = rest;
}

if let Some((rest, sfx)) = s.split_once('#') {
let mut mnemo = Vec::<u8>::with_capacity(4);
mnemonic::decode(sfx, &mut mnemo)?;
checksum = u32::from_le_bytes([mnemo[0], mnemo[1], mnemo[2], mnemo[3]]);
s = rest;
}

let s = if s.contains('-') {
s.replace('-', "")
} else {
s.to_owned()
};

let alphabet = Alphabet::new(BAID64_ALPHABET).expect("invalid Baid64 alphabet");
let engine = GeneralPurpose::new(&alphabet, NO_PAD);
let data = engine.decode(s)?;

if data.len() != LEN {
return Err(Baid64ParseError::InvalidLen(orig.to_owned()));
}
let mut payload = [0u8; LEN];
payload.copy_from_slice(&data);

if checksum != check(Self::HRI, payload) {
return Err(Baid64ParseError::InvalidChecksum(orig.to_owned()));
}

Ok(Self::from(payload))
}
}

#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct Baid64 {
pub struct Baid64<const LEN: usize = 32> {
hri: &'static str,
chunking: bool,
mnemonic: String,
Expand All @@ -61,7 +137,7 @@ pub struct Baid64 {
payload: [u8; LEN],
}

impl Baid64 {
impl<const LEN: usize> Baid64<LEN> {
pub fn with(
hri: &'static str,
payload: [u8; LEN],
Expand All @@ -72,11 +148,7 @@ impl Baid64 {
debug_assert!(hri.len() <= HRI_MAX_LEN, "HRI is too long");
debug_assert!(LEN > HRI_MAX_LEN, "Baid64 id must be at least 9 bytes");

let key = sha2::Sha256::digest(hri.as_bytes());
let mut sha = sha2::Sha256::new_with_prefix(key);
sha.update(&payload);
let sha = sha.finalize();
let checksum = u32::from_le_bytes([sha[0], sha[1], sha[1], sha[2]]);
let checksum = check(hri, payload);
let mnemonic = mnemonic::to_string(checksum.to_le_bytes());

Self {
Expand Down Expand Up @@ -106,7 +178,7 @@ impl Baid64 {
pub const fn checksum(&self) -> u32 { self.checksum }
}

impl Display for Baid64 {
impl<const LEN: usize> Display for Baid64<LEN> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
use base64::alphabet::Alphabet;
use base64::engine::general_purpose::NO_PAD;
Expand Down
Loading

0 comments on commit 6161544

Please sign in to comment.