From 8fa216d8d732dd45b7b690963109a6f5e8fd0908 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 25 Apr 2024 00:56:14 +0200 Subject: [PATCH] extract baid64 into a dedicated crate and repo --- Cargo.lock | 13 +++ Cargo.toml | 1 + src/baid64.rs | 249 ------------------------------------------------ src/bip340.rs | 2 +- src/ed25519.rs | 2 +- src/identity.rs | 2 +- src/lib.rs | 2 - src/public.rs | 3 +- src/runtime.rs | 3 +- src/secret.rs | 2 +- 10 files changed, 21 insertions(+), 258 deletions(-) delete mode 100644 src/baid64.rs diff --git a/Cargo.lock b/Cargo.lock index 1017644..7cd74fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "baid64" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8b80494235048845f856b267a4a1d97df59fd14ed7ca92652f834ce93becc6" +dependencies = [ + "amplify", + "base64", + "mnemonic", + "sha2", +] + [[package]] name = "base64" version = "0.22.0" @@ -558,6 +570,7 @@ version = "0.1.0" dependencies = [ "aes", "amplify", + "baid64", "base64", "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 514c37c..251b4a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ required-features = ["cli"] [dependencies] amplify = "4.6.0" +baid64 = "0.1.0" secp256k1 = { version = "0.29.0", features = ["rand", "global-context", "rand-std"] } ec25519 = "0.1.0" rand = "0.8.5" diff --git a/src/baid64.rs b/src/baid64.rs deleted file mode 100644 index 61b87e1..0000000 --- a/src/baid64.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Base64-encoded identifiers -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2024 by -// Dr Maxim Orlovsky -// -// Copyright (C) 2024 Cyphernet. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::fmt::{self, Display, Formatter}; - -use base64::Engine; -use sha2::Digest; - -pub const HRI_MAX_LEN: usize = 16; - -pub const BAID64_ALPHABET: &str = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$"; - -fn check(hri: &'static str, payload: [u8; LEN]) -> [u8; 4] { - let key = sha2::Sha256::digest(hri.as_bytes()); - let mut sha = sha2::Sha256::new_with_prefix(key); - sha.update(&payload); - let sha = sha.finalize(); - [sha[0], sha[1], sha[1], sha[2]] -} - -pub trait DisplayBaid64 { - const HRI: &'static str; - const CHUNKING: bool; - const PREFIX: bool; - const EMBED_CHECKSUM: bool; - const MNEMONIC: bool; - - fn to_baid64_payload(&self) -> [u8; LEN]; - fn to_baid64_string(&self) -> String { self.display_baid64().to_string() } - fn to_baid64_mnemonic(&self) -> String { self.display_baid64().mnemonic } - fn display_baid64(&self) -> Baid64Display { - Baid64Display::with( - Self::HRI, - self.to_baid64_payload(), - Self::CHUNKING, - Self::PREFIX, - Self::MNEMONIC, - Self::EMBED_CHECKSUM, - ) - } - fn fmt_baid64(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(&self.display_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} - expected {1:#x} while found - /// {2:#x}. - InvalidChecksum(String, u32, u32), - - /// invalid length of mnemonic in {0}. - InvalidMnemonicLen(String), - - #[from] - #[display(inner)] - InvalidMnemonic(mnemonic::Error), - - #[from] - #[display(inner)] - Base64(base64::DecodeError), -} - -pub trait FromBaid64Str: DisplayBaid64 + From<[u8; LEN]> { - fn from_baid64_str(mut s: &str) -> Result { - let orig = s; - - use base64::alphabet::Alphabet; - use base64::engine::general_purpose::NO_PAD; - use base64::engine::GeneralPurpose; - - let mut checksum = None; - - 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::::with_capacity(4); - mnemonic::decode(sfx, &mut mnemo)?; - if mnemo.len() != 4 { - return Err(Baid64ParseError::InvalidMnemonicLen(orig.to_string())); - } - checksum = Some([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 && data.len() != LEN + 4 { - return Err(Baid64ParseError::InvalidLen(orig.to_owned())); - } - let mut payload = [0u8; LEN]; - payload.copy_from_slice(&data[..LEN]); - if data.len() == LEN + 4 { - checksum = Some([data[LEN], data[LEN + 1], data[LEN + 2], data[LEN + 3]]); - } - - let ck = check(Self::HRI, payload); - if matches!(checksum, Some(c) if c != ck) { - return Err(Baid64ParseError::InvalidChecksum( - orig.to_owned(), - u32::from_le_bytes(ck), - u32::from_le_bytes(checksum.unwrap()), - )); - } - - Ok(Self::from(payload)) - } -} - -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -pub struct Baid64Display { - hri: &'static str, - chunking: bool, - mnemonic: String, - prefix: bool, - suffix: bool, - embed_checksum: bool, - checksum: [u8; 4], - payload: [u8; LEN], -} - -impl Baid64Display { - pub fn with( - hri: &'static str, - payload: [u8; LEN], - chunking: bool, - prefix: bool, - suffix: bool, - embed_checksum: bool, - ) -> Self { - 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 checksum = check(hri, payload); - let mnemonic = mnemonic::to_string(checksum); - - Self { - hri, - chunking, - mnemonic, - prefix, - suffix, - embed_checksum, - checksum, - payload, - } - } - - pub fn new(hri: &'static str, payload: [u8; LEN]) -> Self { - Self::with(hri, payload, false, false, false, false) - } - pub const fn use_hri(mut self) -> Self { - self.prefix = true; - self - } - pub const fn use_chunking(mut self) -> Self { - self.chunking = true; - self - } - pub const fn use_mnemonic(mut self) -> Self { - self.suffix = true; - self - } - pub const fn embed_checksum(mut self) -> Self { - self.embed_checksum = true; - self - } - - pub const fn human_identifier(&self) -> &'static str { self.hri } - - pub fn mnemonic(&self) -> &str { self.mnemonic.as_str() } - pub const fn checksum(&self) -> [u8; 4] { self.checksum } -} - -impl Display for Baid64Display { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use base64::alphabet::Alphabet; - use base64::engine::general_purpose::NO_PAD; - use base64::engine::GeneralPurpose; - - if (self.prefix && !f.sign_minus()) || (!self.prefix && f.sign_minus()) { - write!(f, "{}:", self.hri)?; - } - - let alphabet = Alphabet::new(BAID64_ALPHABET).expect("invalid Baid64 alphabet"); - let engine = GeneralPurpose::new(&alphabet, NO_PAD); - - let mut payload = self.payload.to_vec(); - if self.embed_checksum { - payload.extend(self.checksum); - } - let s = engine.encode(payload); - - if self.chunking { - let bytes = s.as_bytes(); - f.write_str(&String::from_utf8_lossy(&bytes[..8]))?; - for chunk in bytes[8..].chunks(7) { - write!(f, "-{}", &String::from_utf8_lossy(chunk))?; - } - } else { - f.write_str(&s)?; - } - - if (self.suffix && !f.alternate()) || (!self.suffix && f.alternate()) { - write!(f, "#{}", self.mnemonic)?; - } - - Ok(()) - } -} diff --git a/src/bip340.rs b/src/bip340.rs index 8082961..c9cd77c 100644 --- a/src/bip340.rs +++ b/src/bip340.rs @@ -25,10 +25,10 @@ use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use std::str::FromStr; +use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; use secp256k1::schnorr::Signature; use secp256k1::{Keypair, Message, SecretKey, XOnlyPublicKey, SECP256K1}; -use crate::baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; use crate::{Algo, Chain, InvalidPubkey, InvalidSig, SsiPub, SsiSig}; #[derive(Clone, Eq, PartialEq)] diff --git a/src/ed25519.rs b/src/ed25519.rs index 4995382..0877968 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -26,9 +26,9 @@ use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::str::FromStr; +use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; use ec25519::{KeyPair, Noise, PublicKey, SecretKey, Seed, Signature}; -use crate::baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; use crate::{Algo, Chain, InvalidPubkey, InvalidSig, SsiPub, SsiSig}; #[derive(Clone, Eq, PartialEq)] diff --git a/src/identity.rs b/src/identity.rs index 550ab1b..6ca769e 100644 --- a/src/identity.rs +++ b/src/identity.rs @@ -23,12 +23,12 @@ use std::collections::BTreeSet; use std::fmt::{self, Display, Formatter}; use std::str::{FromStr, Utf8Error}; +use baid64::Baid64ParseError; use chrono::{DateTime, Utc}; use fluent_uri::Uri; use percent_encoding::{percent_decode_str, utf8_percent_encode, AsciiSet, CONTROLS}; use sha2::{Digest, Sha256}; -use crate::baid64::Baid64ParseError; use crate::{InvalidSig, SsiPub, SsiSecret, SsiSig}; #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] diff --git a/src/lib.rs b/src/lib.rs index 05f7abb..80b678d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,6 @@ #[macro_use] extern crate amplify; -pub mod baid64; - mod identity; mod secret; mod public; diff --git a/src/public.rs b/src/public.rs index 5f7f1e2..f38f437 100644 --- a/src/public.rs +++ b/src/public.rs @@ -24,8 +24,7 @@ use std::hash::Hash; use std::str::FromStr; use amplify::{hex, Bytes, Bytes32, Display}; - -use crate::baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; +use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str}; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, Default)] #[non_exhaustive] diff --git a/src/runtime.rs b/src/runtime.rs index 8770f51..c7be22a 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -27,7 +27,8 @@ use std::io::{self, BufRead, Write}; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; -use crate::baid64::Baid64ParseError; +use baid64::Baid64ParseError; + use crate::{Fingerprint, SecretParseError, Ssi, SsiPair, SsiParseError, SsiQuery, SsiSecret}; #[derive(Debug, Display, Error, From)] diff --git a/src/secret.rs b/src/secret.rs index 7a37091..a570f3a 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -27,10 +27,10 @@ use aes::cipher::generic_array::GenericArray; use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit}; use aes::{Aes256, Block}; use amplify::Bytes32; +use baid64::Baid64ParseError; use chrono::{DateTime, Utc}; use sha2::{Digest, Sha256}; -use crate::baid64::Baid64ParseError; use crate::{Algo, Bip340Secret, Chain, Ed25519Secret, Fingerprint, Ssi, SsiCert, SsiPub, SsiSig}; #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]