diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9fd45e0..46c7d29 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,6 +1,7 @@ -name: Rust +name: Rust on: + workflow_dispatch: push: branches: [ "main" ] pull_request: @@ -11,12 +12,64 @@ env: jobs: build: + strategy: + matrix: + include: + # Linux (32-bit) + - target: i686-unknown-linux-gnu + platform: ubuntu-latest + rust: stable + deps: sudo apt update && sudo apt install gcc-multilib - runs-on: ubuntu-latest + # macOS (64-bit) + - target: x86_64-apple-darwin + platform: macos-latest + rust: stable + # Windows (64-bit) + - target: x86_64-pc-windows-msvc + platform: windows-latest + rust: stable + runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v4 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + - run: ${{ matrix.deps }} + - run: cargo build --target ${{ matrix.target }} --release --all-features + + wasm_builds: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.73.0 # MSRV + - stable + target: + - wasm32-wasi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + - run: cargo build --target ${{ matrix.target }} --features wasm + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.73.0 # MSRV + - stable + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + - run: cargo test --verbose --release + - run: cargo test --all-features # debug build + - run: cargo test --release --all-features --all-targets diff --git a/Cargo.toml b/Cargo.toml index 9ff51ff..c18c2be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ license = "Apache-2.0" [features] default = ["serde"] +wasm = ["getrandom/js"] # needed for CI testing on wasm32-unknown-unknown [dependencies] bcrypt-pbkdf = "0.10" @@ -21,20 +22,34 @@ k256 = "0.13" multibase = { version = "1.0", git = "https://github.com/cryptidtech/rust-multibase.git" } multicodec = { version = "1.0", git = "https://github.com/cryptidtech/rust-multicodec.git" } multihash = { version = "1.0", git = "https://github.com/cryptidtech/multihash.git" } -multisig = { version = "1.0", git = "https://github.com/cryptidtech/multisig.git" } +multisig = { version = "^1.0", git = "https://github.com/cryptidtech/multisig.git" } multitrait = { version = "1.0", git = "https://github.com/cryptidtech/multitrait.git" } multiutil = { version = "1.0", git = "https://github.com/cryptidtech/multiutil.git" } rand = "0.8" sec1 = "0.7" -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"], optional = true } -sodiumoxide = "0.2" -ssh-key = { version = "0.6", features = ["alloc", "crypto", "ed25519"]} -ssh-encoding = "0.2" +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +], optional = true } +chacha20 = "0.9" +poly1305 = "0.8" thiserror = "1.0" typenum = "1.17" -unsigned-varint = { version = "0.8", features = ["std"]} +unsigned-varint = { version = "0.8", features = ["std"] } vsss-rs = "3.4" zeroize = "1.7" +ssh-encoding = { version = "0.2" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +ssh-key = { version = "0.6", default-features = false, features = [ + "alloc", + "ecdsa", + "ed25519", +] } +getrandom = { version = "0.2", features = ["js"], optional = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +ssh-key = { version = "0.6", features = ["alloc", "crypto", "ed25519"] } [dev-dependencies] serde_test = "1.0" diff --git a/src/cipher.rs b/src/cipher.rs index 630f1e2..2e5447f 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -94,7 +94,8 @@ mod tests { .try_build() .unwrap(); - let nonce = hex::decode("00b61a43d4d1e8d7").unwrap(); + // ChaCha needs 12 bytes of nonce iaw RFC8439 + let nonce = hex::decode("00b61a43d4d1e8d700b61a43").unwrap(); // create a cipher multikey let ciphermk = Builder::new(Codec::Chacha20Poly1305) .with_nonce(&nonce) @@ -110,7 +111,7 @@ mod tests { .unwrap(); // generate a random secret key - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = mk::Builder::new_from_random_bytes(Codec::Ed25519Priv, &mut rng) .unwrap() .with_comment("test key") diff --git a/src/error.rs b/src/error.rs index b38eeff..82644e4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -103,13 +103,7 @@ pub enum AttributesError { pub enum ConversionsError { /// Ssh key error #[error(transparent)] - SshKey(#[from] ssh_key::Error), - /// Ssh key label error - #[error(transparent)] - SshKeyLabel(#[from] ssh_encoding::LabelError), - /// Ssh encoding error - #[error(transparent)] - SshEncoding(#[from] ssh_encoding::Error), + Ssh(#[from] SshErrors), /// Public key operation failure #[error("Public key error: {0}")] PublicKeyFailure(String), @@ -124,6 +118,49 @@ pub enum ConversionsError { UnsupportedCodec(multicodec::Codec), } +/// SSH Encoding Errors that cannot be handled by thiserror since they may not use the std feature +/// in the case of wasm32 target. +#[derive(Clone, Debug)] +pub enum SshErrors { + /// Error from [ssh_key::Error] + Key(ssh_key::Error), + /// Invalid label from [ssh_encoding::LabelError] + KeyLabel(ssh_encoding::LabelError), + /// Unexpected trailing data at end of message from [ssh_encoding::Error] + Encoding(ssh_encoding::Error), +} + +/// Impl Display for EncodingError +impl std::fmt::Display for SshErrors { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SshErrors::Key(err) => write!(f, "{}", err), + SshErrors::KeyLabel(err) => write!(f, "{}", err), + SshErrors::Encoding(err) => write!(f, "{}", err), + } + } +} + +impl std::error::Error for SshErrors {} + +impl From for SshErrors { + fn from(err: ssh_encoding::Error) -> Self { + SshErrors::Encoding(err) + } +} + +impl From for SshErrors { + fn from(err: ssh_key::Error) -> Self { + SshErrors::Key(err) + } +} + +impl From for SshErrors { + fn from(err: ssh_encoding::LabelError) -> Self { + SshErrors::KeyLabel(err) + } +} + /// Cipher errors created by this library #[derive(Clone, Debug, thiserror::Error)] #[non_exhaustive] diff --git a/src/lib.rs b/src/lib.rs index ee34dff..fc05d27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ // SPDX-License-Idnetifier: Apache-2.0 -//! multikey +//! Multikey Crate #![warn(missing_docs)] #![deny( trivial_casts, @@ -31,7 +31,7 @@ pub use views::{ /// Multikey type and functions pub mod mk; -pub use mk::{KEY_CODECS, KEY_SHARE_CODECS, Builder, EncodedMultikey, Multikey}; +pub use mk::{Builder, EncodedMultikey, Multikey, KEY_CODECS, KEY_SHARE_CODECS}; /// Nonce type pub mod nonce; diff --git a/src/mk.rs b/src/mk.rs index b2421e6..3da507e 100644 --- a/src/mk.rs +++ b/src/mk.rs @@ -12,7 +12,7 @@ use multitrait::{Null, TryDecodeFrom}; use multiutil::{BaseEncoded, CodecInfo, EncodingInfo, Varbytes, Varuint}; use rand::{CryptoRng, RngCore}; use ssh_key::{ - private::{EcdsaKeypair, Ed25519Keypair, KeypairData}, + private::{EcdsaKeypair, KeypairData}, public::{EcdsaPublicKey, KeyData}, EcdsaCurve, PrivateKey, PublicKey, }; @@ -32,7 +32,7 @@ pub const KEY_CODECS: [Codec; 4] = [ */ Codec::Secp256K1Priv, Codec::Bls12381G1Priv, - Codec::Bls12381G2Priv + Codec::Bls12381G2Priv, ]; /// the list of key share codecs supported @@ -40,12 +40,11 @@ pub const KEY_SHARE_CODECS: [Codec; 4] = [ Codec::Bls12381G1PubShare, Codec::Bls12381G1PrivShare, Codec::Bls12381G2PubShare, - Codec::Bls12381G2PrivShare - /* - Codec::LamportSha3256PrivShare, - Codec::LamportSha3384PrivShare, - Codec::LamportSha3512PrivShare, - */ + Codec::Bls12381G2PrivShare, /* + Codec::LamportSha3256PrivShare, + Codec::LamportSha3384PrivShare, + Codec::LamportSha3512PrivShare, + */ ]; /// the multicodec sigil for multikey @@ -93,7 +92,7 @@ impl EncodingInfo for Multikey { } impl From for Vec { - fn from(mk: Multikey) -> Vec { + fn from(mk: Multikey) -> Self { let mut v = Vec::default(); // add in the sigil v.append(&mut SIGIL.into()); @@ -402,17 +401,17 @@ impl Builder { rng: &mut (impl RngCore + CryptoRng), ) -> Result { let key_bytes = match codec { - Codec::Ed25519Priv => Ed25519Keypair::random(rng).private.to_bytes().to_vec(), + Codec::Ed25519Priv => ed25519_dalek::SigningKey::generate(rng).to_bytes().to_vec(), Codec::P256Priv => EcdsaKeypair::random(rng, EcdsaCurve::NistP256) - .map_err(ConversionsError::SshKey)? + .map_err(|e| ConversionsError::Ssh(e.into()))? .private_key_bytes() .to_vec(), Codec::P384Priv => EcdsaKeypair::random(rng, EcdsaCurve::NistP384) - .map_err(ConversionsError::SshKey)? + .map_err(|e| ConversionsError::Ssh(e.into()))? .private_key_bytes() .to_vec(), Codec::P521Priv => EcdsaKeypair::random(rng, EcdsaCurve::NistP521) - .map_err(ConversionsError::SshKey)? + .map_err(|e| ConversionsError::Ssh(e.into()))? .private_key_bytes() .to_vec(), Codec::Secp256K1Priv => k256::SecretKey::random(rng).to_bytes().to_vec(), @@ -805,6 +804,26 @@ impl Builder { } } + /// Create a new [Multikey] from a seed. + /// + /// Currently only supports [Codec::Ed25519Priv] seeds. + pub fn new_from_seed(codec: Codec, seed: &[u8]) -> Result { + match codec { + Codec::Ed25519Priv => { + let keypair = + ssh_key::private::Ed25519Keypair::from_seed(seed.try_into().map_err(|_| { + ConversionsError::UnsupportedAlgorithm( + "Ed25519 seed must be 32 bytes long".to_string(), + ) + })?); + let private_key = PrivateKey::try_from(KeypairData::Ed25519(keypair)) + .map_err(|e| ConversionsError::Ssh(e.into()))?; + Self::new_from_ssh_private_key(&private_key) + } + _ => Err(ConversionsError::UnsupportedCodec(codec).into()), + } + } + /// add an encoding pub fn with_base_encoding(mut self, base: Base) -> Self { self.base_encoding = Some(base); @@ -831,8 +850,7 @@ impl Builder { /// add in the threshold value pub fn with_threshold(self, threshold: usize) -> Self { - let v: Vec = Varuint(threshold).into(); - self.with_attribute(AttrId::Threshold, &v) + self.with_attribute(AttrId::Threshold, &Varuint(threshold).into()) } /// add in the limit value @@ -896,11 +914,12 @@ mod tests { use super::*; use crate::{cipher, kdf}; use multisig::EncodedMultisig; + use ssh_key::private::Ed25519Keypair; #[test] fn test_random() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_comment("test key") @@ -913,8 +932,16 @@ mod tests { }; println!("encoded pubkey: {}: {}", codec, epk); println!("encoded pubkey v: {}: {}", codec, hex::encode(vpk)); - println!("encoded privkey: {}: {}", codec, EncodedMultikey::from(mk.clone())); - println!("encoded privkey v: {}: {}", codec, hex::encode(Into::>::into(mk.clone()))); + println!( + "encoded privkey: {}: {}", + codec, + EncodedMultikey::from(mk.clone()) + ); + println!( + "encoded privkey v: {}: {}", + codec, + hex::encode(Into::>::into(mk.clone())) + ); let _v: Vec = mk.into(); } } @@ -922,7 +949,7 @@ mod tests { #[test] fn test_encoded_random() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_base_encoding(Base::Base32Lower) @@ -938,7 +965,7 @@ mod tests { #[test] fn test_random_public_ssh_key_roundtrip() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_comment("test key") @@ -958,7 +985,7 @@ mod tests { #[test] fn test_random_private_ssh_key_roundtrip() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_comment("test key") @@ -977,7 +1004,7 @@ mod tests { #[test] fn test_ssh_key_roundtrip() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let sk1 = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_comment("test key") @@ -1007,7 +1034,7 @@ mod tests { #[test] fn test_encryption_roundtrip() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk1 = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_comment("test key") @@ -1029,7 +1056,7 @@ mod tests { .try_build() .unwrap(); let ciphermk = cipher::Builder::new(Codec::Chacha20Poly1305) - .with_random_nonce(chacha20::NONCE_LENGTH, &mut rng) + .with_random_nonce(chacha20::nonce_length(), &mut rng) .try_build() .unwrap(); // get the kdf view on the cipher multikey so we can generate a @@ -1044,14 +1071,14 @@ mod tests { // key and the kdf and cipher attributes and data let cipher = mk1.cipher_view(&ciphermk).unwrap(); // encrypt the multikey using the cipher - let mk = cipher.encrypt().unwrap(); - mk + + cipher.encrypt().unwrap() }; let attr = mk2.attr_view().unwrap(); - assert_eq!(true, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk2.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // encrypted key @@ -1076,14 +1103,14 @@ mod tests { // get the cipher view let cipher = mk2.cipher_view(&ciphermk).unwrap(); // decrypt the multikey using the cipher - let mk = cipher.decrypt().unwrap(); - mk + + cipher.decrypt().unwrap() }; let attr = mk3.attr_view().unwrap(); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk3.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -1096,7 +1123,7 @@ mod tests { #[test] fn test_signing_detached_roundtrip() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_comment("test key") @@ -1112,7 +1139,7 @@ mod tests { assert!(kd.secret_bytes().is_ok()); let conv = mk.conv_view().unwrap(); let pk = EncodedMultikey::new(Base::Base16Lower, conv.to_public_key().unwrap()); - println!("{} pubkey: {}", codec, pk.to_string()); + println!("{} pubkey: {}", codec, pk); let msg = b"for great justice, move every zig!"; @@ -1123,7 +1150,7 @@ mod tests { signmk.sign(msg.as_slice(), false, None).unwrap() }; let sig = EncodedMultisig::new(Base::Base16Lower, signature.clone()); - println!("signaure: {}", sig.to_string()); + println!("signaure: {}", sig); let verifymk = mk.verify_view().unwrap(); assert!(verifymk.verify(&signature, Some(msg.as_slice())).is_ok()); @@ -1133,7 +1160,7 @@ mod tests { #[test] fn test_signing_merged_roundtrip() { for codec in KEY_CODECS { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = Builder::new_from_random_bytes(codec, &mut rng) .unwrap() .with_comment("test key") @@ -1148,8 +1175,9 @@ mod tests { assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); - let msg = hex::decode("8bb78be51ac7cc98f44e38947ff8a128764ec039b89687a790dfa8444ba97682") - .unwrap(); + let msg = + hex::decode("8bb78be51ac7cc98f44e38947ff8a128764ec039b89687a790dfa8444ba97682") + .unwrap(); let signmk = mk.sign_view().unwrap(); let signature = if codec == Codec::Bls12381G1Priv || codec == Codec::Bls12381G2Priv { @@ -1168,7 +1196,7 @@ mod tests { #[test] fn test_bls_key_combine() { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk1 = Builder::new_from_random_bytes(Codec::Bls12381G1Priv, &mut rng) .unwrap() .with_comment("test key") @@ -1212,7 +1240,7 @@ mod tests { #[test] fn test_bls_share_ssh_key_roundtrip() { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = Builder::new_from_random_bytes(Codec::Bls12381G1Priv, &mut rng) .unwrap() .with_comment("test key") @@ -1244,10 +1272,8 @@ mod tests { #[test] fn test_from_ssh_pubkey() { - let mut rng = rand::rngs::OsRng::default(); - let kp = KeypairData::Ed25519(Ed25519Keypair::random( - &mut rng, - )); + let mut rng = rand::rngs::OsRng; + let kp = KeypairData::Ed25519(Ed25519Keypair::random(&mut rng)); let sk = PrivateKey::new(kp, "test key").unwrap(); // build a multikey from the public key @@ -1259,9 +1285,9 @@ mod tests { let attr = mk.attr_view().unwrap(); assert_eq!(mk.codec, Codec::Ed25519Pub); assert_eq!(mk.comment, "test key".to_string()); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(true, attr.is_public_key()); - assert_eq!(false, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(attr.is_public_key()); + assert!(!attr.is_secret_key()); let kd = mk.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // public key @@ -1269,10 +1295,8 @@ mod tests { #[test] fn test_from_ssh_privkey() { - let mut rng = rand::rngs::OsRng::default(); - let kp = KeypairData::Ed25519(Ed25519Keypair::random( - &mut rng, - )); + let mut rng = rand::rngs::OsRng; + let kp = KeypairData::Ed25519(Ed25519Keypair::random(&mut rng)); let sk = PrivateKey::new(kp, "test key").unwrap(); let mk = Builder::new_from_ssh_private_key(&sk) @@ -1283,9 +1307,9 @@ mod tests { let attr = mk.attr_view().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Priv); assert_eq!(mk.comment, "test key".to_string()); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -1299,9 +1323,9 @@ mod tests { assert_eq!(mk.codec(), Codec::Ed25519Pub); assert_eq!(mk.encoding(), Base::Base16Lower); assert_eq!(mk.comment, "test key".to_string()); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(true, attr.is_public_key()); - assert_eq!(false, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(attr.is_public_key()); + assert!(!attr.is_secret_key()); let kd = mk.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // public key @@ -1315,9 +1339,9 @@ mod tests { assert_eq!(mk.codec(), Codec::Ed25519Priv); assert_eq!(mk.encoding(), Base::Base16Lower); assert_eq!(mk.comment, "test key".to_string()); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -1330,9 +1354,9 @@ mod tests { let attr = mk.attr_view().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Pub); assert_eq!(mk.comment, "test key".to_string()); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(true, attr.is_public_key()); - assert_eq!(false, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(attr.is_public_key()); + assert!(!attr.is_secret_key()); let kd = mk.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // public key @@ -1345,9 +1369,9 @@ mod tests { let attr = mk.attr_view().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Priv); assert_eq!(mk.comment, "test key".to_string()); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -1361,4 +1385,24 @@ mod tests { assert_eq!(mk1, mk2); assert!(mk2.is_null()); } + + #[test] + fn test_from_seed() { + let seed = hex::decode("f9ddcd5118319cc69e6985ef3f4ee3b6c591d46255e1ae5569c8662111b7d3c2") + .unwrap(); + let mk = Builder::new_from_seed(Codec::Ed25519Priv, seed.as_slice()) + .unwrap() + .with_comment("test key") + .try_build() + .unwrap(); + let attr = mk.attr_view().unwrap(); + assert_eq!(mk.codec(), Codec::Ed25519Priv); + assert_eq!(mk.comment, "test key".to_string()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); + let kd = mk.data_view().unwrap(); + assert!(kd.key_bytes().is_ok()); + assert!(kd.secret_bytes().is_ok()); + } } diff --git a/src/nonce.rs b/src/nonce.rs index a71f972..e471223 100644 --- a/src/nonce.rs +++ b/src/nonce.rs @@ -25,11 +25,6 @@ impl Nonce { pub fn len(&self) -> usize { self.nonce.len() } - - /// return if the nonce is empty - pub fn is_empty(&self) -> bool { - self.nonce.is_empty() - } } impl CodecInfo for Nonce { @@ -61,12 +56,12 @@ impl AsRef<[u8]> for Nonce { } impl From for Vec { - fn from(n: Nonce) -> Vec { + fn from(val: Nonce) -> Self { let mut v = Vec::default(); // add the sigil v.append(&mut SIGIL.into()); // add the nonce bytes - v.append(&mut Varbytes(n.nonce).into()); + v.append(&mut Varbytes(val.nonce.clone()).into()); v } } @@ -127,6 +122,7 @@ impl Builder { /// build from random source pub fn new_from_random_bytes(size: usize, rng: &mut (impl RngCore + CryptoRng)) -> Self { let mut bytes = vec![0; size]; + bytes.resize(size, 0u8); rng.fill_bytes(bytes.as_mut()); Self { bytes, @@ -151,8 +147,7 @@ impl Builder { /// build a base encoded vlad pub fn try_build_encoded(&self) -> Result { Ok(EncodedNonce::new( - self.base_encoding - .unwrap_or_else(Nonce::preferred_encoding), + self.base_encoding.unwrap_or_else(Nonce::preferred_encoding), self.try_build()?, )) } @@ -172,7 +167,7 @@ mod tests { #[test] fn test_random() { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let n = Builder::new_from_random_bytes(32, &mut rng) .try_build() .unwrap(); @@ -183,7 +178,7 @@ mod tests { #[test] fn test_binary_roundtrip() { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let n = Builder::new_from_random_bytes(32, &mut rng) .try_build() .unwrap(); @@ -193,7 +188,7 @@ mod tests { #[test] fn test_encoded_roundtrip() { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let n = Builder::new_from_random_bytes(32, &mut rng) .try_build_encoded() .unwrap(); @@ -205,7 +200,7 @@ mod tests { #[test] fn test_nonce_multisig_roundtrip() { - let mut rng = rand::rngs::OsRng::default(); + let mut rng = rand::rngs::OsRng; let mk = mk::Builder::new_from_random_bytes(Codec::Ed25519Priv, &mut rng) .unwrap() .with_comment("test key") diff --git a/src/serde/mod.rs b/src/serde/mod.rs index 2c8efc4..66e3d71 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -67,8 +67,8 @@ mod tests { // try to get the associated public key let mk = { let conv = sk.conv_view().unwrap(); - let mk = conv.to_public_key().unwrap(); - mk + + conv.to_public_key().unwrap() }; //let v: Vec = mk.clone().into(); @@ -76,22 +76,19 @@ mod tests { assert_tokens( &mk.compact(), - &[ - Token::BorrowedBytes(&[ - 0xba, 0x24, // Multikey sigil - 0xed, 0x01, // Ed25519 public key as varuint - 0x08, // comment length - 0x74, 0x65, 0x73, 0x74, 0x20, 0x6b, 0x65, 0x79, // comment - 0x01, // 1 attribute - 0x01, // key data attributes - 0x20, // 32 bytes in the public key - // public key bytes - 0x13, 0xe1, 0xe6, 0xe8, 0xc3, 0x53, 0x67, 0x2b, - 0x75, 0x9c, 0x93, 0xc3, 0x97, 0x95, 0x69, 0x27, - 0xe1, 0x50, 0x3c, 0x6e, 0xdd, 0x73, 0xf2, 0x40, - 0xcc, 0xff, 0x2b, 0x7d, 0xd0, 0x45, 0x58, 0xb6 - ]), - ], + &[Token::BorrowedBytes(&[ + 0xba, 0x24, // Multikey sigil + 0xed, 0x01, // Ed25519 public key as varuint + 0x08, // comment length + 0x74, 0x65, 0x73, 0x74, 0x20, 0x6b, 0x65, 0x79, // comment + 0x01, // 1 attribute + 0x01, // key data attributes + 0x20, // 32 bytes in the public key + // public key bytes + 0x13, 0xe1, 0xe6, 0xe8, 0xc3, 0x53, 0x67, 0x2b, 0x75, 0x9c, 0x93, 0xc3, 0x97, 0x95, + 0x69, 0x27, 0xe1, 0x50, 0x3c, 0x6e, 0xdd, 0x73, 0xf2, 0x40, 0xcc, 0xff, 0x2b, 0x7d, + 0xd0, 0x45, 0x58, 0xb6, + ])], ); } @@ -126,8 +123,8 @@ mod tests { let mk = { let conv = sk.conv_view().unwrap(); - let mk = conv.to_public_key().unwrap(); - mk + + conv.to_public_key().unwrap() }; assert_tokens( @@ -145,9 +142,7 @@ mod tests { Token::Seq { len: Some(1) }, Token::Tuple { len: 2 }, Token::Str("key-data"), // AttrId::KeyData - Token::Str( - "f2013e1e6e8c353672b759c93c397956927e1503c6edd73f240ccff2b7dd04558b6", - ), + Token::Str("f2013e1e6e8c353672b759c93c397956927e1503c6edd73f240ccff2b7dd04558b6"), Token::TupleEnd, Token::SeqEnd, Token::StructEnd, @@ -166,9 +161,9 @@ mod tests { .unwrap(); let attr = mk1.attr_view().unwrap(); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk1.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -183,7 +178,7 @@ mod tests { .try_build() .unwrap(); - let nonce = hex::decode("714e5abf0f7beae8").unwrap(); + let nonce = hex::decode("714e5abf0f7beae8aabbccdd").unwrap(); let ciphermk = cipher::Builder::new(Codec::Chacha20Poly1305) .with_nonce(&nonce) .try_build() @@ -198,57 +193,52 @@ mod tests { // get the cipher view let cipher = mk1.cipher_view(&ciphermk).unwrap(); // encrypt the multikey using the cipher - let mk = cipher.encrypt().unwrap(); - mk + cipher.encrypt().unwrap() }; /* - let v: Vec = mk2.clone().into(); - print!("mk2: "); - for b in &v { - print!("0x{:02x}, ", b); - } - println!(""); + let v: Vec = mk2.clone().into(); + print!("mk2: "); + for b in &v { + print!("0x{:02x}, ", b); + } + println!(""); */ assert_tokens( &mk2.compact(), - &[ - Token::BorrowedBytes(&[ - 0xba, 0x24, // Multikey sigil - 0x80, 0x26, // Ed25519 private codec as varuint - 0x08, // comment of 8 bytes - // comment - 0x74, 0x65, 0x73, 0x74, 0x20, 0x6b, 0x65, 0x79, - 0x08, // 8 attributes - // key is encrypted - 0x00, 0x01, 0x01, - // key data of 48 bytes - 0x01, 0x30, - 0xb7, 0xa9, 0x28, 0xdf, 0x65, 0x68, 0xbf, 0x6c, - 0xbe, 0xcc, 0x2e, 0x1e, 0x9a, 0xfe, 0xb8, 0x35, - 0xbe, 0x69, 0x08, 0x3e, 0x3f, 0xe2, 0x5f, 0x57, - 0x38, 0xad, 0x16, 0x57, 0x54, 0x35, 0xb4, 0xab, - 0x6a, 0x67, 0x9e, 0x08, 0x69, 0x6b, 0x1f, 0xc4, - 0x63, 0x7f, 0xbb, 0xad, 0x85, 0xd0, 0x52, 0x9a, - // cipher codec - 0x02, 0x02, 0xa5, 0x01, - // cipher key len (32) - 0x03, 0x01, 0x20, - // cipher nonce - 0x04, 0x08, 0x71, 0x4e, 0x5a, 0xbf, 0x0f, 0x7b, 0xea, 0xe8, - // kdf codec - 0x05, 0x03, 0x8d, 0xa0, 0x03, - // kdf salt - 0x06, 0x20, - 0x62, 0x1f, 0x20, 0xcf, 0xda, 0x14, 0x0b, 0xd8, - 0xbf, 0x83, 0xa8, 0x99, 0x16, 0x74, 0x28, 0x46, - 0x29, 0x29, 0xa4, 0x1e, 0x9b, 0x68, 0xa8, 0x46, - 0x7b, 0xfc, 0x24, 0x55, 0xe9, 0xf9, 0x84, 0x06, - // kdf rounds (10) - 0x07, 0x01, 0x0a, - ]), - ], + &[Token::BorrowedBytes(&[ + 0xba, 0x24, // Multikey sigil + 0x80, 0x26, // Ed25519 private codec as varuint + 0x08, // comment of 8 bytes + // comment + 0x74, 0x65, 0x73, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x08, // 8 bytes of attributes + // key is encrypted + 0x00, 0x01, 0x01, // 3 bytes + // key data of 32 bytes + 0x01, 0x20, // 0x20 = 32 byte key + 0xef, 0x7c, 0xf7, 0x8f, 0x3e, 0x0e, 0x58, 0x82, // 8 bytes + 0x32, 0xa4, 0x23, 0xdb, 0x1f, 0xdf, 0x02, 0xe2, // 16 bytes + 0x18, 0xc4, 0x94, 0x4c, 0x35, 0xba, 0x4e, 0xb4, // 24 bytes + 0x54, 0x96, 0xb5, 0x27, 0x42, 0x53, 0x9d, 0x78, // 32 bytes + // cipher codec + 0x02, 0x02, 0xa5, 0x01, // codec (Chacha20Poly1305) + // cipher key len (32) + 0x03, 0x01, 0x20, // 3 bytes codec + // 12 byte cipher nonce + 0x04, 0x0c, // 0x0c = 12 bytes of nonce + 0x71, 0x4e, 0x5a, 0xbf, 0x0f, // 6 bytes + 0x7b, 0xea, 0xe8, 0xaa, 0xbb, 0xcc, 0xdd, // 12 bytes + // kdf codec + 0x05, 0x03, 0x8d, 0xa0, 0x03, // kdf salt + 0x06, 0x20, // 2 bytes salt codec + 0x62, 0x1f, 0x20, 0xcf, 0xda, 0x14, 0x0b, 0xd8, // 8 bytes salt + 0xbf, 0x83, 0xa8, 0x99, 0x16, 0x74, 0x28, 0x46, // 16 + 0x29, 0x29, 0xa4, 0x1e, 0x9b, 0x68, 0xa8, 0x46, // 24 + 0x7b, 0xfc, 0x24, 0x55, 0xe9, 0xf9, 0x84, 0x06, // 32 + // kdf rounds (10) + 0x07, 0x01, 0x0a, // 3 bytes rounds + ])], ); } @@ -263,9 +253,9 @@ mod tests { .unwrap(); let attr = mk1.attr_view().unwrap(); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk1.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -279,7 +269,7 @@ mod tests { .with_rounds(10) .try_build() .unwrap(); - let nonce = hex::decode("714e5abf0f7beae8").unwrap(); + let nonce = hex::decode("714e5abf0f7beae8aabbccdd").unwrap(); let ciphermk = cipher::Builder::new(Codec::Chacha20Poly1305) .with_nonce(&nonce) .try_build() @@ -294,8 +284,7 @@ mod tests { // get the cipher view let cipher = mk1.cipher_view(&ciphermk).unwrap(); // encrypt the multikey using the cipher - let mk = cipher.encrypt().unwrap(); - mk + cipher.encrypt().unwrap() }; assert_tokens( @@ -317,7 +306,7 @@ mod tests { Token::TupleEnd, Token::Tuple { len: 2 }, Token::Str("key-data"), - Token::Str("f30b7a928df6568bf6cbecc2e1e9afeb835be69083e3fe25f5738ad16575435b4ab6a679e08696b1fc4637fbbad85d0529a"), + Token::Str("f20ef7cf78f3e0e588232a423db1fdf02e218c4944c35ba4eb45496b52742539d78"), Token::TupleEnd, Token::Tuple { len: 2 }, Token::Str("cipher-codec"), @@ -329,7 +318,7 @@ mod tests { Token::TupleEnd, Token::Tuple { len: 2 }, Token::Str("cipher-nonce"), - Token::Str("f08714e5abf0f7beae8"), + Token::Str("f0c714e5abf0f7beae8aabbccdd"), Token::TupleEnd, Token::Tuple { len: 2 }, Token::Str("kdf-codec"), @@ -360,9 +349,9 @@ mod tests { .unwrap(); let attr = mk1.attr_view().unwrap(); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk1.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -377,7 +366,7 @@ mod tests { .try_build() .unwrap(); - let nonce = hex::decode("714e5abf0f7beae8").unwrap(); + let nonce = hex::decode("714e5abf0f7beae8aabbccdd").unwrap(); let ciphermk = cipher::Builder::new(Codec::Chacha20Poly1305) .with_nonce(&nonce) .try_build() @@ -392,12 +381,11 @@ mod tests { // get the cipher view let cipher = mk1.cipher_view(&ciphermk).unwrap(); // encrypt the multikey using the cipher - let mk = cipher.encrypt().unwrap(); - mk + cipher.encrypt().unwrap() }; let s = serde_json::to_string(&mk2).unwrap(); - assert_eq!(s, "{\"codec\":\"ed25519-priv\",\"comment\":\"test key\",\"attributes\":[[\"key-is-encrypted\",\"f0101\"],[\"key-data\",\"f30b7a928df6568bf6cbecc2e1e9afeb835be69083e3fe25f5738ad16575435b4ab6a679e08696b1fc4637fbbad85d0529a\"],[\"cipher-codec\",\"f02a501\"],[\"cipher-key-len\",\"f0120\"],[\"cipher-nonce\",\"f08714e5abf0f7beae8\"],[\"kdf-codec\",\"f038da003\"],[\"kdf-salt\",\"f20621f20cfda140bd8bf83a899167428462929a41e9b68a8467bfc2455e9f98406\"],[\"kdf-rounds\",\"f010a\"]]}".to_string()); + assert_eq!(s, "{\"codec\":\"ed25519-priv\",\"comment\":\"test key\",\"attributes\":[[\"key-is-encrypted\",\"f0101\"],[\"key-data\",\"f20ef7cf78f3e0e588232a423db1fdf02e218c4944c35ba4eb45496b52742539d78\"],[\"cipher-codec\",\"f02a501\"],[\"cipher-key-len\",\"f0120\"],[\"cipher-nonce\",\"f0c714e5abf0f7beae8aabbccdd\"],[\"kdf-codec\",\"f038da003\"],[\"kdf-salt\",\"f20621f20cfda140bd8bf83a899167428462929a41e9b68a8467bfc2455e9f98406\"],[\"kdf-rounds\",\"f010a\"]]}".to_string()); let mk3: Multikey = serde_json::from_str(&s).unwrap(); assert_eq!(mk2, mk3); @@ -425,9 +413,9 @@ mod tests { let mk1 = emk.to_inner(); let attr = mk1.attr_view().unwrap(); - assert_eq!(false, attr.is_encrypted()); - assert_eq!(false, attr.is_public_key()); - assert_eq!(true, attr.is_secret_key()); + assert!(!attr.is_encrypted()); + assert!(!attr.is_public_key()); + assert!(attr.is_secret_key()); let kd = mk1.data_view().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -442,7 +430,7 @@ mod tests { .try_build() .unwrap(); - let nonce = hex::decode("714e5abf0f7beae8").unwrap(); + let nonce = hex::decode("714e5abf0f7beae8aabbccdd").unwrap(); let ciphermk = cipher::Builder::new(Codec::Chacha20Poly1305) .with_nonce(&nonce) .try_build() @@ -457,12 +445,12 @@ mod tests { // get the cipher view let cipher = mk1.cipher_view(&ciphermk).unwrap(); // encrypt the multikey using the cipher - let mk = cipher.encrypt().unwrap(); - mk + cipher.encrypt().unwrap() }; let s = serde_json::to_string(&mk2).unwrap(); - assert_eq!(s, "{\"codec\":\"bls12_381-g1-priv\",\"comment\":\"test key\",\"attributes\":[[\"key-is-encrypted\",\"f0101\"],[\"key-data\",\"f308298d80896ebda0577994e73041d2e3acd79967fd1516010a6cebedefa5c131e200bab3d620a17940f758191742f2deb\"],[\"cipher-codec\",\"f02a501\"],[\"cipher-key-len\",\"f0120\"],[\"cipher-nonce\",\"f08714e5abf0f7beae8\"],[\"kdf-codec\",\"f038da003\"],[\"kdf-salt\",\"f20621f20cfda140bd8bf83a899167428462929a41e9b68a8467bfc2455e9f98406\"],[\"kdf-rounds\",\"f010a\"]]}".to_string()); + println!("{}", s); + assert_eq!(s, "{\"codec\":\"bls12_381-g1-priv\",\"comment\":\"test key\",\"attributes\":[[\"key-is-encrypted\",\"f0101\"],[\"key-data\",\"f20da4d0758cd8d3debfbf143b6813c94ed6bd40a0ddb0971f3caf51daeec3a3acd\"],[\"cipher-codec\",\"f02a501\"],[\"cipher-key-len\",\"f0120\"],[\"cipher-nonce\",\"f0c714e5abf0f7beae8aabbccdd\"],[\"kdf-codec\",\"f038da003\"],[\"kdf-salt\",\"f20621f20cfda140bd8bf83a899167428462929a41e9b68a8467bfc2455e9f98406\"],[\"kdf-rounds\",\"f010a\"]]}".to_string()); let mk3: Multikey = serde_json::from_str(&s).unwrap(); assert_eq!(mk2, mk3); @@ -481,8 +469,7 @@ mod tests { // try to get the associated public key let pk = { let conv = sk.conv_view().unwrap(); - let pk = conv.to_public_key().unwrap(); - pk + conv.to_public_key().unwrap() }; // try to get the associated public key @@ -500,9 +487,10 @@ mod tests { assert_tokens( &n.compact(), - &[ - Token::BorrowedBytes(&[187, 36, 32, 118, 137, 82, 114, 197, 206, 92, 12, 114, 181, 236, 84, 148, 78, 173, 115, 148, 130, 248, 112, 72, 219, 191, 193, 59, 135, 48, 8, 179, 29, 89, 149]), - ], + &[Token::BorrowedBytes(&[ + 187, 36, 32, 118, 137, 82, 114, 197, 206, 92, 12, 114, 181, 236, 84, 148, 78, 173, + 115, 148, 130, 248, 112, 72, 219, 191, 193, 59, 135, 48, 8, 179, 29, 89, 149, + ])], ); } @@ -536,9 +524,7 @@ mod tests { len: 1, }, Token::Str("nonce"), - Token::Str( - "f2076895272c5ce5c0c72b5ec54944ead739482f87048dbbfc13b873008b31d5995", - ), + Token::Str("f2076895272c5ce5c0c72b5ec54944ead739482f87048dbbfc13b873008b31d5995"), Token::StructEnd, ], ); @@ -547,12 +533,7 @@ mod tests { #[test] fn test_null_multikey_serde_compact() { let mk = Multikey::null(); - assert_tokens( - &mk.compact(), - &[ - Token::BorrowedBytes(&[186, 36, 0, 0, 0]), - ] - ); + assert_tokens(&mk.compact(), &[Token::BorrowedBytes(&[186, 36, 0, 0, 0])]); } #[test] @@ -561,39 +542,32 @@ mod tests { assert_tokens( &mk.readable(), &[ - Token::Struct { name: "multikey", len: 3, }, + Token::Struct { + name: "multikey", + len: 3, + }, Token::Str("codec"), Token::Str("identity"), Token::Str("comment"), Token::Str(""), Token::Str("attributes"), - Token::Seq { len: Some(0), }, + Token::Seq { len: Some(0) }, Token::SeqEnd, Token::StructEnd, - ] + ], ); } #[test] fn test_encoded_null_multikey_serde_readable() { let mk: EncodedMultikey = Multikey::null().into(); - assert_tokens( - &mk.readable(), - &[ - Token::Str("fba24000000"), - ] - ); + assert_tokens(&mk.readable(), &[Token::Str("fba24000000")]); } #[test] fn test_null_nonce_serde_compact() { let n = nonce::Nonce::null(); - assert_tokens( - &n.compact(), - &[ - Token::BorrowedBytes(&[187, 36, 0]), - ] - ); + assert_tokens(&n.compact(), &[Token::BorrowedBytes(&[187, 36, 0])]); } #[test] @@ -602,22 +576,20 @@ mod tests { assert_tokens( &n.readable(), &[ - Token::Struct { name: "nonce", len: 1, }, + Token::Struct { + name: "nonce", + len: 1, + }, Token::Str("nonce"), Token::Str("f00"), Token::StructEnd, - ] + ], ); } #[test] fn test_encoded_null_nonce_serde_readable() { let n: nonce::EncodedNonce = nonce::Nonce::null().into(); - assert_tokens( - &n.readable(), - &[ - Token::Str("fbb2400"), - ] - ); + assert_tokens(&n.readable(), &[Token::Str("fbb2400")]); } } diff --git a/src/views/bls12381.rs b/src/views/bls12381.rs index fe31214..19d6b0c 100644 --- a/src/views/bls12381.rs +++ b/src/views/bls12381.rs @@ -167,11 +167,13 @@ impl<'a> AttrView for View<'a> { } fn is_secret_key(&self) -> bool { - matches!(self.mk.codec, + matches!( + self.mk.codec, Codec::Bls12381G1Priv - | Codec::Bls12381G2Priv - | Codec::Bls12381G1PrivShare - | Codec::Bls12381G2PrivShare) + | Codec::Bls12381G2Priv + | Codec::Bls12381G1PrivShare + | Codec::Bls12381G2PrivShare + ) } fn is_public_key(&self) -> bool { @@ -179,7 +181,10 @@ impl<'a> AttrView for View<'a> { } fn is_secret_key_share(&self) -> bool { - matches!(self.mk.codec, Codec::Bls12381G1PrivShare | Codec::Bls12381G2PrivShare) + matches!( + self.mk.codec, + Codec::Bls12381G1PrivShare | Codec::Bls12381G2PrivShare + ) } } @@ -323,7 +328,7 @@ impl<'a> FingerprintView for View<'a> { // get the key bytes let bytes = { let kd = self.mk.data_view()?; - + kd.key_bytes()? }; // hash the key bytes using the given codec @@ -338,7 +343,7 @@ impl<'a> ConvView for View<'a> { // get the secret key bytes let secret_bytes = { let kd = self.mk.data_view()?; - + kd.secret_bytes()? }; @@ -446,7 +451,7 @@ impl<'a> ConvView for View<'a> { let key_bytes = { let kd = pk.data_view()?; - + kd.key_bytes()? }; @@ -456,7 +461,7 @@ impl<'a> ConvView for View<'a> { Codec::Bls12381G1Pub => { key_bytes .encode(&mut buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G1 } Codec::Bls12381G1PubShare => { @@ -470,13 +475,13 @@ impl<'a> ConvView for View<'a> { .into(); key_share .encode(&mut buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G1_SHARE } Codec::Bls12381G2Pub => { key_bytes .encode(&mut buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G2 } Codec::Bls12381G2PubShare => { @@ -490,20 +495,20 @@ impl<'a> ConvView for View<'a> { .into(); key_share .encode(&mut buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G2_SHARE } _ => return Err(ConversionsError::UnsupportedCodec(self.mk.codec).into()), }; let opaque_key_bytes = ssh_key::public::OpaquePublicKeyBytes::decode(&mut buf.as_slice()) - .map_err(ConversionsError::SshKey)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; Ok(ssh_key::PublicKey::new( ssh_key::public::KeyData::Other(ssh_key::public::OpaquePublicKey { algorithm: ssh_key::Algorithm::Other( ssh_key::AlgorithmName::new(name) - .map_err(ConversionsError::SshKeyLabel)?, + .map_err(|e| ConversionsError::Ssh(e.into()))?, ), key: opaque_key_bytes, }), @@ -515,14 +520,14 @@ impl<'a> ConvView for View<'a> { fn to_ssh_private_key(&self) -> Result { let secret_bytes = { let kd = self.mk.data_view()?; - + kd.secret_bytes()? }; let pk = self.to_public_key()?; let key_bytes = { let kd = pk.data_view()?; - + kd.key_bytes()? }; @@ -533,10 +538,10 @@ impl<'a> ConvView for View<'a> { Codec::Bls12381G1Priv => { secret_bytes .encode(&mut secret_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; key_bytes .encode(&mut public_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G1 } Codec::Bls12381G1PrivShare => { @@ -558,19 +563,19 @@ impl<'a> ConvView for View<'a> { .into(); secret_key_share .encode(&mut secret_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; public_key_share .encode(&mut public_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G1_SHARE } Codec::Bls12381G2Priv => { secret_bytes .encode(&mut secret_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; key_bytes .encode(&mut public_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G2 } Codec::Bls12381G2PrivShare => { @@ -592,10 +597,10 @@ impl<'a> ConvView for View<'a> { .into(); secret_key_share .encode(&mut secret_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; public_key_share .encode(&mut public_buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; ALGORITHM_NAME_G2_SHARE } _ => return Err(ConversionsError::UnsupportedCodec(self.mk.codec).into()), @@ -603,18 +608,18 @@ impl<'a> ConvView for View<'a> { let opaque_private_key_bytes = ssh_key::private::OpaquePrivateKeyBytes::decode(&mut secret_buf.as_slice()) - .map_err(ConversionsError::SshKey)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; let opaque_public_key_bytes = ssh_key::public::OpaquePublicKeyBytes::decode(&mut public_buf.as_slice()) - .map_err(ConversionsError::SshKey)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; Ok(ssh_key::PrivateKey::new( ssh_key::private::KeypairData::Other(ssh_key::private::OpaqueKeypair { public: ssh_key::public::OpaquePublicKey { algorithm: ssh_key::Algorithm::Other( ssh_key::AlgorithmName::new(name) - .map_err(ConversionsError::SshKeyLabel)?, + .map_err(|e| ConversionsError::Ssh(e.into()))?, ), key: opaque_public_key_bytes, }, @@ -622,7 +627,7 @@ impl<'a> ConvView for View<'a> { }), self.mk.comment.clone(), ) - .map_err(ConversionsError::SshKey)?) + .map_err(|e| ConversionsError::Ssh(e.into()))?) } } @@ -639,13 +644,12 @@ impl<'a> SignView for View<'a> { // get the secret key bytes let secret_bytes = { let kd = self.mk.data_view()?; - + kd.secret_bytes()? }; // get the signature scheme - let sig_scheme: SignatureSchemes = - SchemeTypeId::try_from(scheme)?.into(); + let sig_scheme: SignatureSchemes = SchemeTypeId::try_from(scheme)?.into(); match self.mk.codec { Codec::Bls12381G1Priv => { @@ -761,7 +765,7 @@ impl<'a> ThresholdView for View<'a> { // get the secret key bytes let secret_bytes = { let kd = self.mk.data_view()?; - + kd.secret_bytes()? }; @@ -980,7 +984,7 @@ impl<'a> VerifyView for View<'a> { let attr = self.mk.attr_view()?; let pubmk = if attr.is_secret_key() { let kc = self.mk.conv_view()?; - + kc.to_public_key()? } else { self.mk.clone() diff --git a/src/views/chacha20.rs b/src/views/chacha20.rs index d4ba1a9..c148e56 100644 --- a/src/views/chacha20.rs +++ b/src/views/chacha20.rs @@ -4,18 +4,23 @@ use crate::{ AttrId, AttrView, CipherAttrView, CipherView, DataView, Error, FingerprintView, KdfAttrView, Multikey, Views, }; +use chacha20::cipher::{KeyIvInit, StreamCipher}; +use chacha20::{ChaCha20, Nonce}; use multicodec::Codec; use multihash::{mh, Multihash}; use multitrait::TryDecodeFrom; use multiutil::Varuint; -use sodiumoxide::crypto::aead::chacha20poly1305; use zeroize::Zeroizing; use super::bcrypt::SALT_LENGTH; -/// the constants for ChaCha20 -pub const KEY_LENGTH: usize = chacha20poly1305::KEYBYTES; -pub const NONCE_LENGTH: usize = chacha20poly1305::NONCEBYTES; +pub const KEY_SIZE: usize = poly1305::KEY_SIZE; + +/// Return the length of the [Nonce] +#[allow(dead_code)] +pub(crate) fn nonce_length() -> usize { + Nonce::default().len() +} pub(crate) struct View<'a> { mk: &'a Multikey, @@ -94,15 +99,15 @@ impl<'a> CipherAttrView for View<'a> { .attributes .get(&AttrId::CipherNonce) .ok_or(CipherError::MissingNonce)?; - if nonce.len() != NONCE_LENGTH { - Err(CipherError::InvalidNonceLen.into()) - } else { - Ok(nonce.clone()) - } + + let nonce = + Nonce::from_exact_iter(nonce.iter().copied()).ok_or(CipherError::InvalidNonce)?; + + Ok(nonce.to_vec().into()) } fn key_length(&self) -> Result { - Ok(KEY_LENGTH) + Ok(KEY_SIZE) } } @@ -158,8 +163,7 @@ impl<'a> CipherView for View<'a> { }; // create the chacha nonce from the data - let n = chacha20poly1305::Nonce::from_slice(nonce.as_slice()) - .ok_or(CipherError::InvalidNonce)?; + let n = Nonce::from_exact_iter(nonce.iter().copied()).ok_or(CipherError::InvalidNonce)?; // get the key data from the passed-in Multikey let key = { @@ -172,25 +176,30 @@ impl<'a> CipherView for View<'a> { }; // create the chacha key from the data - let k = chacha20poly1305::Key::from_slice(key.as_slice()) - .ok_or(CipherError::InvalidKey)?; + let k = + chacha20::Key::from_exact_iter(key.iter().copied()).ok_or(CipherError::InvalidKey)?; // get the encrypted key bytes from the viewed Multikey (self) let msg = { let attr = self.mk.data_view()?; - attr.key_bytes()? }; - // decrypt the key bytes - let dec = chacha20poly1305::open(msg.as_slice(), None, &n, &k) - .map_err(|_| CipherError::DecryptionFailed)?; + // // decrypt the key bytes + // let dec = chacha20poly1305::open(msg.as_slice(), None, &n, &k) + // .map_err(|_| CipherError::DecryptionFailed)?; + + let mut chacha = ChaCha20::new(&k, &n); + + let mut dec = msg.clone(); + + chacha.apply_keystream(&mut dec); // create a new Multikey from the viewed Multikey (self) with the // decrypted key and none of the kdf or cipher attributes let mut res = self.mk.clone(); let _ = res.attributes.remove(&AttrId::KeyIsEncrypted); - res.attributes.insert(AttrId::KeyData, dec.into()); + res.attributes.insert(AttrId::KeyData, dec); let _ = res.attributes.remove(&AttrId::CipherCodec); let _ = res.attributes.remove(&AttrId::CipherKeyLen); let _ = res.attributes.remove(&AttrId::CipherNonce); @@ -216,8 +225,7 @@ impl<'a> CipherView for View<'a> { cattr.nonce_bytes()? }; - let n = chacha20poly1305::Nonce::from_slice(nonce.as_slice()) - .ok_or(CipherError::InvalidNonce)?; + let n = Nonce::from_exact_iter(nonce.iter().copied()).ok_or(CipherError::InvalidNonce)?; // get the key data from the passed-in Multikey let key = { @@ -229,18 +237,21 @@ impl<'a> CipherView for View<'a> { key }; - let k = chacha20poly1305::Key::from_slice(key.as_slice()) - .ok_or(CipherError::InvalidKey)?; + let k = + chacha20::Key::from_exact_iter(key.iter().copied()).ok_or(CipherError::InvalidKey)?; // get the secret bytes from the viewed Multikey let msg = { let kd = self.mk.data_view()?; - kd.secret_bytes()? }; - // encrypt the secret bytes from the viewed Multikey - let enc = chacha20poly1305::seal(msg.as_slice(), None, &n, &k); + let mut chacha = ChaCha20::new(&k, &n); + + let mut enc = msg.clone(); + + // apply keystream (encrypt) + chacha.apply_keystream(&mut enc); // prepare the cipher attributes let cattr = cipher.cipher_attr_view()?; @@ -260,7 +271,7 @@ impl<'a> CipherView for View<'a> { let mut res = self.mk.clone(); res.attributes .insert(AttrId::KeyIsEncrypted, is_encrypted.into()); - res.attributes.insert(AttrId::KeyData, enc.into()); + res.attributes.insert(AttrId::KeyData, enc); res.attributes .insert(AttrId::CipherCodec, cipher_codec.into()); res.attributes @@ -278,7 +289,6 @@ impl<'a> FingerprintView for View<'a> { // get the key bytes let bytes = { let kd = self.mk.data_view()?; - kd.key_bytes()? }; // hash the key bytes using the given codec diff --git a/src/views/ed25519.rs b/src/views/ed25519.rs index beca847..b2403a5 100644 --- a/src/views/ed25519.rs +++ b/src/views/ed25519.rs @@ -241,7 +241,7 @@ impl<'a> ConvView for View<'a> { }), self.mk.comment.clone(), ) - .map_err(ConversionsError::SshKey)?) + .map_err(|e| ConversionsError::Ssh(e.into()))?) } } diff --git a/src/views/secp256k1.rs b/src/views/secp256k1.rs index faedca1..d55ec41 100644 --- a/src/views/secp256k1.rs +++ b/src/views/secp256k1.rs @@ -211,15 +211,15 @@ impl<'a> ConvView for View<'a> { let mut buff: Vec = Vec::new(); key_bytes .encode(&mut buff) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; let opaque_key_bytes = ssh_key::public::OpaquePublicKeyBytes::decode(&mut buff.as_slice()) - .map_err(ConversionsError::SshKey)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; Ok(ssh_key::PublicKey::new( ssh_key::public::KeyData::Other(ssh_key::public::OpaquePublicKey { algorithm: ssh_key::Algorithm::Other( ssh_key::AlgorithmName::new(ALGORITHM_NAME) - .map_err(ConversionsError::SshKeyLabel)?, + .map_err(|e| ConversionsError::Ssh(e.into()))?, ), key: opaque_key_bytes, }), @@ -238,10 +238,10 @@ impl<'a> ConvView for View<'a> { let mut buf: Vec = Vec::new(); secret_bytes .encode(&mut buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; let opaque_private_key_bytes = ssh_key::private::OpaquePrivateKeyBytes::decode(&mut buf.as_slice()) - .map_err(ConversionsError::SshKey)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; let pk = self.to_public_key()?; let key_bytes = { @@ -253,17 +253,17 @@ impl<'a> ConvView for View<'a> { buf.clear(); key_bytes .encode(&mut buf) - .map_err(ConversionsError::SshEncoding)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; let opaque_public_key_bytes = ssh_key::public::OpaquePublicKeyBytes::decode(&mut buf.as_slice()) - .map_err(ConversionsError::SshKey)?; + .map_err(|e| ConversionsError::Ssh(e.into()))?; Ok(ssh_key::PrivateKey::new( ssh_key::private::KeypairData::Other(ssh_key::private::OpaqueKeypair { public: ssh_key::public::OpaquePublicKey { algorithm: ssh_key::Algorithm::Other( ssh_key::AlgorithmName::new(ALGORITHM_NAME) - .map_err(ConversionsError::SshKeyLabel)?, + .map_err(|e| ConversionsError::Ssh(e.into()))?, ), key: opaque_public_key_bytes, }, @@ -271,7 +271,7 @@ impl<'a> ConvView for View<'a> { }), self.mk.comment.clone(), ) - .map_err(ConversionsError::SshKey)?) + .map_err(|e| ConversionsError::Ssh(e.into()))?) } } @@ -307,7 +307,8 @@ impl<'a> SignView for View<'a> { .try_sign(msg) .map_err(|e| SignError::SigningFailed(e.to_string()))?; - let mut ms = ms::Builder::new(Codec::Es256KMsig).with_signature_bytes(&signature.to_bytes()); + let mut ms = + ms::Builder::new(Codec::Es256KMsig).with_signature_bytes(&signature.to_bytes()); if combined { ms = ms.with_message_bytes(&msg); }