rusty_paserk-0.4.0/.cargo_vcs_info.json0000644000000001360000000000100135700ustar { "git": { "sha1": "b2ea22d591c2e43c560c908d8205ce08a85f2e6a" }, "path_in_vcs": "" }rusty_paserk-0.4.0/.github/workflows/rust.yml000064400000000000000000000032331046102023000174760ustar 00000000000000on: [push, pull_request] name: Continuous integration jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check test: name: Test Suite runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: test - uses: actions-rs/cargo@v1 with: command: test args: --no-default-features --features v3 --tests - uses: actions-rs/cargo@v1 with: command: test args: --no-default-features --features v4 --tests clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add clippy - uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings audit: name: Security Audit runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: cargo install cargo-audit - uses: actions-rs/audit-check@v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} rusty_paserk-0.4.0/.gitignore000064400000000000000000000000231046102023000143430ustar 00000000000000/target Cargo.lock rusty_paserk-0.4.0/Cargo.toml0000644000000061640000000000100115750ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "rusty_paserk" version = "0.4.0" authors = ["Conrad Ludgate "] description = "Platform Agnostic Serializable Keys (PASERK) is an extension on PASETO for key management" readme = "README.md" keywords = [ "paseto", "paserk", "token", "security", ] categories = [ "cryptography", "authentication", "encoding", "network-programming", "web-programming", ] license = "MIT" repository = "https://github.com/conradludgate/rusty-paserk" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [profile.dev.package."*"] opt-level = 2 debug = 0 incremental = false [[test]] name = "test-vectors" path = "tests/vectors.rs" harness = false [dependencies.aes] version = "0.8.2" optional = true [dependencies.arbitrary] version = "1.3" features = ["derive"] optional = true [dependencies.argon2] version = "0.5.0" optional = true [dependencies.base64] version = "0.22.1" [dependencies.base64ct] version = "1.6.0" [dependencies.blake2] version = "0.10.6" optional = true [dependencies.chacha20] version = "0.9.1" optional = true [dependencies.cipher] version = "0.4.4" [dependencies.ctr] version = "0.9.2" optional = true [dependencies.curve25519-dalek] version = "4.0.0" optional = true [dependencies.digest] version = "0.10.7" features = ["mac"] [dependencies.ed25519-dalek] version = "2.0.0" features = ["hazmat"] optional = true [dependencies.generic-array] version = "0.14" [dependencies.hmac] version = "0.12.1" optional = true [dependencies.p384] version = "0.13.0" features = [ "ecdh", "pkcs8", ] optional = true [dependencies.pbkdf2] version = "0.12.1" optional = true [dependencies.rand] version = "0.8.5" [dependencies.rusty_paseto] version = "0.7.0" features = ["core"] default-features = false [dependencies.serde] version = "1" optional = true [dependencies.sha2] version = "0.10.2" optional = true [dependencies.subtle] version = "2.5.0" [dev-dependencies.ff] version = "0.13.0" [dev-dependencies.hex] version = "0.4" [dev-dependencies.libtest-mimic] version = "0.7.3" [dev-dependencies.rand] version = "0.8" [dev-dependencies.serde] version = "1" features = ["derive"] [dev-dependencies.serde_json] version = "1" [features] arbitrary = ["dep:arbitrary"] default = ["v4"] serde = ["dep:serde"] v3 = [ "dep:hmac", "dep:aes", "dep:ctr", "dep:sha2", "dep:p384", "dep:pbkdf2", "rusty_paseto/v3_local", "rusty_paseto/v3_public", ] v4 = [ "dep:sha2", "dep:blake2", "dep:chacha20", "dep:ed25519-dalek", "dep:curve25519-dalek", "dep:argon2", "rusty_paseto/v4_local", "rusty_paseto/v4_public", ] rusty_paserk-0.4.0/Cargo.toml.orig000064400000000000000000000044551046102023000152570ustar 00000000000000[package] name = "rusty_paserk" version = "0.4.0" edition = "2021" authors = ["Conrad Ludgate "] repository = "https://github.com/conradludgate/rusty-paserk" description = "Platform Agnostic Serializable Keys (PASERK) is an extension on PASETO for key management" license = "MIT" keywords = ["paseto", "paserk", "token", "security"] categories = [ "cryptography", "authentication", "encoding", "network-programming", "web-programming", ] [features] default = ["v4"] # V3 contains NIST approved algoritms only v3 = [ "dep:hmac", "dep:aes", "dep:ctr", "dep:sha2", "dep:p384", "dep:pbkdf2", "rusty_paseto/v3_local", "rusty_paseto/v3_public", ] # V4 is recommended v4 = [ "dep:sha2", "dep:blake2", "dep:chacha20", "dep:ed25519-dalek", "dep:curve25519-dalek", "dep:argon2", "rusty_paseto/v4_local", "rusty_paseto/v4_public" ] serde = ["dep:serde"] arbitrary = ["dep:arbitrary"] [dependencies] rusty_paseto = { version = "0.7.0", default-features = false, features = ["core"] } subtle = "2.5.0" generic-array = "0.14" base64ct = "1.6.0" base64 = "0.22.1" cipher = "0.4.4" digest = { version = "0.10.7", features = ["mac"] } rand = "0.8.5" # V4 chacha20 = { version = "0.9.1", optional = true } blake2 = { version = "0.10.6", optional = true } ed25519-dalek = { version = "2.0.0", features = ["hazmat"], optional = true } curve25519-dalek = { version = "4.0.0", optional = true } argon2 = { version = "0.5.0", optional = true } # V3 aes = { version = "0.8.2", optional = true } ctr = { version = "0.9.2", optional = true } hmac = { version = "0.12.1", optional = true } sha2 = { version = "0.10.2", optional = true } p384 = { version = "0.13.0", optional = true, features = ["ecdh", "pkcs8"] } pbkdf2 = { version = "0.12.1", optional = true } arbitrary = { version = "1.3", features = ["derive"], optional = true } serde = { version = "1", optional = true } [dev-dependencies] rand = "0.8" serde_json = "1" serde = { version = "1", features = ["derive"] } hex = "0.4" ff = "0.13.0" libtest-mimic = "0.7.3" [profile.dev.package."*"] opt-level = 2 debug = false incremental = false [[test]] name = "test-vectors" path = "tests/vectors.rs" harness = false # Properly document all features on docs.rs [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] rusty_paserk-0.4.0/LICENSE.md000064400000000000000000000020571046102023000137700ustar 00000000000000MIT License Copyright (c) 2023 Conrad Ludgate Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rusty_paserk-0.4.0/README.md000064400000000000000000000054611046102023000136450ustar 00000000000000# rusty_paserk An extension of [`rusty_paseto`](https://github.com/rrrodzilla/rusty_paseto) adding the [Platform Agnostic Serializable Keys](https://github.com/paseto-standard/paserk) specifications on top. ## Examples ### Local Wrapping ```rust use rusty_paserk::wrap::{Pie, LocalWrapperExt}; use rusty_paseto::core::{PasetoSymmetricKey, V4, Local, Key}; let wrapping_key = PasetoSymmetricKey::::from(Key::try_new_random().unwrap()); let local_key = PasetoSymmetricKey::from(Key::try_new_random().unwrap()); let nonce = Key::try_new_random().unwrap(); let wrapped_local = Pie::wrap_local(&local_key, &wrapping_key, &nonce); // => "k4.local-wrap.pie.RcAvOxHI0H-0uMsIl6KGcplH_tDlOhW1omFwXltZCiynHeRNH0hmn28AkN516h3WHuAReH3CvQ2SZ6mevnTquPETSd3XnlcbRWACT5GLWcus3BsD4IFWm9wFZgNF7C_E" let mut wrapped_local = wrapped_local.into_bytes(); let local_key2 = Pie::unwrap_local(&mut wrapped_local, &wrapping_key).unwrap(); assert_eq!(local_key.as_ref(), local_key2.as_ref()); ``` ### Secret Wrapping ```rust use rusty_paserk::wrap::{Pie, SecretWrapperExt}; use rusty_paseto::core::{PasetoSymmetricKey, PasetoAsymmetricPrivateKey, V4, Key}; let wrapping_key = PasetoSymmetricKey::from(Key::try_new_random().unwrap()); let secret_key = Key::try_new_random().unwrap(); let secret_key = PasetoAsymmetricPrivateKey::from(&secret_key); let nonce = Key::try_new_random().unwrap(); let wrapped_secret = Pie::wrap_secret(&secret_key, &wrapping_key, &nonce); // => "k4.secret-wrap.pie.cTTnZwzBA3AKBugQCzmctv5R9CjyPOlelG9SLZrhupDwk6vYx-3UQFCZ7x4d57KU4K4U1qJeFP6ELzkMJ0s8qHt0hsQkW14Ni6TJ89MRzEqglUgI6hJD-EF2E9kIFO5YuC5MHwXN7Wi_vG1S3L-OoTjZgT_ZJ__8T7SJhvYLodo" let mut wrapped_secret = wrapped_secret.into_bytes(); let secret_key2 = Pie::unwrap_secret(&mut wrapped_secret, &wrapping_key).unwrap(); assert_eq!(secret_key.as_ref(), secret_key2.as_ref()); ``` ### Local IDs ```rust use rusty_paserk::id::EncodeId; use rusty_paseto::core::{PasetoSymmetricKey, V4, Local, Key}; let local_key = PasetoSymmetricKey::::from(Key::try_new_random().unwrap()); let kid = local_key.encode_id(); // => "k4.lid.XxPub51WIAEmbVTmrs-lFoFodxTSKk8RuYEJk3gl-DYB" ``` ### Secret IDs ```rust use rusty_paserk::id::EncodeId; use rusty_paseto::core::{PasetoAsymmetricPrivateKey, V4, Public, Key}; let secret_key = Key::try_new_random().unwrap(); let secret_key = PasetoAsymmetricPrivateKey::::from(&secret_key); let kid = secret_key.encode_id(); // => "k4.sid.p26RNihDPsk2QbglGMTmwMMqLYyeLY25UOQZXQDXwn61" ``` ### Public IDs ```rust use rusty_paserk::id::EncodeId; use rusty_paseto::core::{PasetoAsymmetricPublicKey, V4, Public, Key}; let public_key = Key::try_new_random().unwrap(); let public_key = PasetoAsymmetricPublicKey::::from(&public_key); let kid = public_key.encode_id(); // => "k4.pid.yMgldRRLHBLkhmcp8NG8yZrtyldbYoAjQWPv_Ma1rzRu" ``` rusty_paserk-0.4.0/SECURITY.md000064400000000000000000000004611046102023000141520ustar 00000000000000# Security Policy This crate has NOT been independently audited. As with any code you find on the web that has not been audited, usage of this crate is at your own risk. ## Reporting a Vulnerability Please report any issues to the maintainer by creating a bug report. Thank you for your contribution! rusty_paserk-0.4.0/src/id.rs000064400000000000000000000144041046102023000141140ustar 00000000000000use std::{fmt, marker::PhantomData, str::FromStr}; use generic_array::{typenum::U33, GenericArray}; use rusty_paseto::core::PasetoError; #[cfg(feature = "v3")] use rusty_paseto::core::V3; #[cfg(feature = "v4")] use rusty_paseto::core::V4; use crate::{write_b64, Key, KeyType, Version}; /// Unique ID for a key /// /// /// /// # Local IDs /// ``` /// use rusty_paserk::{KeyId, Key, Local, V4}; /// /// let local_key = Key::::new_os_random(); /// let kid: KeyId = local_key.into(); /// // kid.to_string() => "k4.lid.XxPub51WIAEmbVTmrs-lFoFodxTSKk8RuYEJk3gl-DYB" /// ``` /// /// # Public/Secret IDs /// ``` /// use rusty_paserk::{KeyId, Key, Secret, Public, V4}; /// /// let secret_key = Key::::new_os_random(); /// let kid: KeyId = secret_key.into(); /// // kid.to_string() => "k4.sid.p26RNihDPsk2QbglGMTmwMMqLYyeLY25UOQZXQDXwn61" /// /// let kid: KeyId = secret_key.public_key().into(); /// // kid.to_string() => "k4.pid.yMgldRRLHBLkhmcp8NG8yZrtyldbYoAjQWPv_Ma1rzRu" /// ``` pub struct KeyId> { id: GenericArray, key: PhantomData<(V, K)>, } impl> fmt::Debug for KeyId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } impl> fmt::Display for KeyId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(V::KEY_HEADER)?; f.write_str(K::ID)?; write_b64(&self.id, f) } } impl> FromStr for KeyId { type Err = PasetoError; fn from_str(s: &str) -> Result { let s = s .strip_prefix(V::KEY_HEADER) .ok_or(PasetoError::WrongHeader)?; let s = s.strip_prefix(K::ID).ok_or(PasetoError::WrongHeader)?; let id = crate::read_b64(s)?; Ok(KeyId { id, key: PhantomData, }) } } impl> core::cmp::PartialOrd for KeyId { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl> core::cmp::Ord for KeyId { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.id.cmp(&other.id) } } impl> core::cmp::PartialEq for KeyId { fn eq(&self, other: &Self) -> bool { self.id.eq(&other.id) } } impl> core::cmp::Eq for KeyId {} impl> Clone for KeyId { fn clone(&self) -> Self { *self } } impl> Copy for KeyId {} impl> Key where KeyId: From, { /// Unique ID for a key /// /// /// /// ``` /// use rusty_paserk::{KeyId, Key, Local, V4}; /// /// let local_key = Key::::new_os_random(); /// let kid = local_key.to_id(); /// // kid.to_string() => "k4.lid.XxPub51WIAEmbVTmrs-lFoFodxTSKk8RuYEJk3gl-DYB" /// ``` pub fn to_id(&self) -> KeyId { self.clone().into() } } #[cfg(feature = "v3")] impl> From> for KeyId { fn from(key: Key) -> Self { use base64ct::{Base64UrlUnpadded, Encoding}; use sha2::digest::Digest; // V3 Public keys are 49 bytes, V3 private keys are 48 bytes, symmetric keys are 32 bytes. // allocate enough space for 49 bytes base64 encoded which is ~66 let mut output = [0; 49 * 4 / 3 + 3]; let p = Base64UrlUnpadded::encode(key.as_ref(), &mut output).unwrap(); let mut derive_d = sha2::Sha384::new(); derive_d.update(V3::KEY_HEADER); derive_d.update(K::ID); derive_d.update(V3::KEY_HEADER); derive_d.update(K::HEADER); derive_d.update(p); let d = derive_d.finalize(); let id = *GenericArray::from_slice(&d[..33]); KeyId { id, key: PhantomData, } } } #[cfg(feature = "v4")] impl> From> for KeyId { fn from(key: Key) -> Self { use base64ct::{Base64UrlUnpadded, Encoding}; use blake2::digest::Digest; // V4 Public keys are 64 bytes, symmetric keys are 32 bytes. // allocate enough space for 64 bytes base64 encoded let mut output = [0; 64 * 4 / 3 + 3]; let p = Base64UrlUnpadded::encode(key.as_ref(), &mut output).unwrap(); let mut derive_d = blake2::Blake2b::::new(); derive_d.update(V4::KEY_HEADER); derive_d.update(K::ID); derive_d.update(V4::KEY_HEADER); derive_d.update(K::HEADER); derive_d.update(p); let id = derive_d.finalize(); KeyId { id, key: PhantomData, } } } impl super::SafeForFooter for KeyId where V: Version, K: KeyType, { } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl> serde::Serialize for KeyId { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.collect_str(self) } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl<'de, V: Version, K: KeyType> serde::Deserialize<'de> for KeyId { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FromStrVisitor(std::marker::PhantomData<(V, K)>); impl<'de, V: Version, K: KeyType> serde::de::Visitor<'de> for FromStrVisitor { type Value = KeyId; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a \"{}{}\" serialized key", V::KEY_HEADER, K::ID) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { v.parse().map_err(E::custom) } } deserializer.deserialize_str(FromStrVisitor(std::marker::PhantomData)) } } rusty_paserk-0.4.0/src/key/arbitrary.rs000064400000000000000000000027131046102023000163070ustar 00000000000000use arbitrary::{Arbitrary, Result, Unstructured}; #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] #[cfg(feature = "v3")] impl<'a> Arbitrary<'a> for super::Key { fn arbitrary(u: &mut Unstructured<'a>) -> Result { let key = <[u8; 32]>::arbitrary(u)?; Ok(Self { key: key.into() }) } } #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] #[cfg(feature = "v3")] impl<'a> Arbitrary<'a> for super::Key { fn arbitrary(u: &mut Unstructured<'a>) -> Result { let key = <[u8; 48]>::arbitrary(u)?; let key = key.into(); if p384::SecretKey::from_bytes(&key).is_err() { return Err(arbitrary::Error::IncorrectFormat); } Ok(Self { key }) } } #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] #[cfg(feature = "v4")] impl<'a> Arbitrary<'a> for super::Key { fn arbitrary(u: &mut Unstructured<'a>) -> Result { let key = <[u8; 32]>::arbitrary(u)?; Ok(Self { key: key.into() }) } } #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] #[cfg(feature = "v4")] impl<'a> Arbitrary<'a> for super::Key { fn arbitrary(u: &mut Unstructured<'a>) -> Result { let key = <[u8; 32]>::arbitrary(u)?; Ok(Self { key: ed25519_dalek::SigningKey::from_bytes(&key) .to_keypair_bytes() .into(), }) } } rusty_paserk-0.4.0/src/key/convert.rs000064400000000000000000000214331046102023000157700ustar 00000000000000use generic_array::GenericArray; use rand::{rngs::OsRng, CryptoRng, RngCore}; use rusty_paseto::core::PasetoError; #[cfg(feature = "v3")] use rusty_paseto::core::V3; #[cfg(feature = "v4")] use rusty_paseto::core::V4; use crate::{Key, KeyType, Local, Public, Secret, Version}; #[cfg(feature = "v3")] impl Key { /// Decode a PEM encoded SEC1 p384 Secret Key /// /// ``` /// use rusty_paserk::Key; /// /// let private_key = "-----BEGIN EC PRIVATE KEY----- /// MIGkAgEBBDAhUb6WGhABE1MTj0x7E/5acgyap23kh7hUAVoAavKyfhYcmI3n1Q7L /// JpHxNb792H6gBwYFK4EEACKhZANiAAT5H7mTSOyjfILDtSuavZfalI3doM8pRUlb /// TzNyYLqM9iVmajpc0JRXvKuBtGtYi7Yft+eqFr6BuzGrdb4Z1vkvRcI504m0qKiE /// zjhi6u4sNgzW23rrVkRYkb2oE3SJPko= /// -----END EC PRIVATE KEY-----"; /// /// let _key = Key::from_sec1_pem(private_key).unwrap(); /// ``` pub fn from_sec1_pem(s: &str) -> Result { let sk = p384::SecretKey::from_sec1_pem(s).map_err(|_| PasetoError::Cryption)?; Ok(Self { key: sk.to_bytes() }) } /// Decode a secret key from raw bytes pub fn from_bytes(s: &[u8]) -> Result { let sk = p384::SecretKey::from_slice(s).map_err(|_| PasetoError::Cryption)?; Ok(Self { key: sk.to_bytes() }) } /// Get the corresponding V3 public key for this V3 secret key pub fn public_key(&self) -> Key { use p384::{EncodedPoint, SecretKey}; let sk = SecretKey::from_bytes(&self.key).unwrap(); let pk: EncodedPoint = sk.public_key().into(); let pk = pk.compress(); let pk = pk.as_bytes(); Key { key: *GenericArray::from_slice(pk), } } } #[cfg(feature = "v3")] impl Key { /// Decode a PEM encoded p384 Public Key /// /// ``` /// use rusty_paserk::Key; /// /// let public_key = "-----BEGIN PUBLIC KEY----- /// MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+R+5k0jso3yCw7Urmr2X2pSN3aDPKUVJ /// W08zcmC6jPYlZmo6XNCUV7yrgbRrWIu2H7fnqha+gbsxq3W+Gdb5L0XCOdOJtKio /// hM44YuruLDYM1tt661ZEWJG9qBN0iT5K /// -----END PUBLIC KEY-----"; /// /// let _key = Key::from_public_key_pem(public_key).unwrap(); /// ``` pub fn from_public_key_pem(s: &str) -> Result { use p384::{pkcs8::DecodePublicKey, EncodedPoint}; let pk = p384::PublicKey::from_public_key_pem(s).map_err(|_| PasetoError::Cryption)?; let pk: EncodedPoint = pk.into(); let pk = pk.compress(); let pk = pk.as_bytes(); Ok(Self { key: *GenericArray::from_slice(pk), }) } /// Decode a public key from raw bytes pub fn from_sec1_bytes(s: &[u8]) -> Result { let pk = p384::PublicKey::from_sec1_bytes(s).map_err(|_| PasetoError::Cryption)?; let pk: p384::EncodedPoint = pk.into(); let pk = pk.compress(); let pk = pk.as_bytes(); Ok(Self { key: *GenericArray::from_slice(pk), }) } } #[cfg(feature = "v4")] impl Key { /// Decode an Ed25519 Secret Keypair /// /// ``` /// use rusty_paserk::Key; /// /// let private_key = "407796f4bc4b8184e9fe0c54b336822d34823092ad873d87ba14c3efb9db8c1db7715bd661458d928654d3e832f53ff5c9480542e0e3d4c9b032c768c7ce6023"; /// let private_key = hex::decode(&private_key).unwrap(); /// /// let _key = Key::from_keypair_bytes(&private_key).unwrap(); /// ``` pub fn from_keypair_bytes(key: &[u8]) -> Result { use ed25519_dalek::SigningKey; let key: [u8; 64] = key.try_into().map_err(|_| PasetoError::InvalidKey)?; match SigningKey::from_keypair_bytes(&key) { Ok(_) => {} Err(_) => return Err(PasetoError::InvalidKey), }; Ok(Key { key: key.into() }) } /// Create a new secret key from the byte array /// /// ``` /// use rusty_paserk::Key; /// /// let private_key = "407796f4bc4b8184e9fe0c54b336822d34823092ad873d87ba14c3efb9db8c1d"; /// let private_key = hex::decode(&private_key).unwrap(); /// let private_key: [u8; 32] = private_key.try_into().unwrap(); /// /// let _key = Key::from_secret_key(private_key); /// ``` pub fn from_secret_key(key: [u8; 32]) -> Self { Self { key: ed25519_dalek::SigningKey::from_bytes(&key) .to_keypair_bytes() .into(), } } /// Get the corresponding V4 public key for this V4 secret key pub fn public_key(&self) -> Key { use generic_array::sequence::Split; let (_sk, pk): (GenericArray, _) = self.key.split(); Key { key: pk } } } #[cfg(feature = "v4")] impl Key { /// Decode a PEM encoded SEC1 Ed25519 Secret Key /// /// ``` /// use rusty_paserk::Key; /// /// let public_key = "b7715bd661458d928654d3e832f53ff5c9480542e0e3d4c9b032c768c7ce6023"; /// let public_key = hex::decode(&public_key).unwrap(); /// /// let _key = Key::from_public_key(&public_key); /// ``` pub fn from_public_key(key: &[u8]) -> Result { let key = key.try_into().map_err(|_| PasetoError::InvalidKey)?; let _ = ed25519_dalek::VerifyingKey::from_bytes(&key).map_err(|_| PasetoError::InvalidKey)?; Ok(Self { key: key.into() }) } } #[cfg(feature = "v3")] impl Key { /// Create a V3 local key from raw bytes pub fn from_bytes(key: [u8; 32]) -> Self { Self { key: key.into() } } /// Get the raw bytes from this key pub fn to_bytes(&self) -> [u8; 32] { self.key.into() } } #[cfg(feature = "v4")] impl Key { /// Create a V4 local key from raw bytes pub fn from_bytes(key: [u8; 32]) -> Self { Self { key: key.into() } } /// Get the raw bytes from this key pub fn to_bytes(&self) -> [u8; 32] { self.key.into() } } impl> AsRef<[u8]> for Key { fn as_ref(&self) -> &[u8] { &self.key } } impl Key { /// Generate a random local key using OS random pub fn new_os_random() -> Self { Self::new_random(&mut OsRng) } /// Generate a random local key using the provided random source pub fn new_random(rng: &mut (impl RngCore + CryptoRng)) -> Self { let mut key = GenericArray::::default(); rng.fill_bytes(&mut key); Self { key } } } #[cfg(feature = "v4")] impl Key { /// Generate a random V4 secret key using OS random pub fn new_os_random() -> Self { Self::new_random(&mut OsRng) } /// Generate a random V4 secret key using the provided random source pub fn new_random(rng: &mut (impl RngCore + CryptoRng)) -> Self { let mut key = [0; 32]; rng.fill_bytes(&mut key); Self { key: ed25519_dalek::SigningKey::from_bytes(&key) .to_keypair_bytes() .into(), } } } #[cfg(feature = "v3")] impl Key { /// Generate a random V3 secret key using OS random pub fn new_os_random() -> Self { Self::new_random(&mut OsRng) } /// Generate a random V3 secret key using the provided random source pub fn new_random(rng: &mut (impl RngCore + CryptoRng)) -> Self { Self { key: p384::SecretKey::random(rng).to_bytes(), } } } #[cfg(feature = "v4")] impl From> for rusty_paseto::core::PasetoSymmetricKey { fn from(key: Key) -> Self { let key: [u8; 32] = key.key.into(); let key: rusty_paseto::core::Key<32> = key.into(); key.into() } } #[cfg(feature = "v3")] impl From> for rusty_paseto::core::PasetoSymmetricKey { fn from(key: Key) -> Self { let key: [u8; 32] = key.key.into(); let key: rusty_paseto::core::Key<32> = key.into(); key.into() } } #[cfg(feature = "v4")] impl From> for rusty_paseto::core::Key<32> { fn from(key: Key) -> Self { let key: [u8; 32] = key.key.into(); key.into() } } #[cfg(feature = "v4")] impl From> for rusty_paseto::core::Key<64> { fn from(key: Key) -> Self { let key: [u8; 64] = key.key.into(); key.into() } } #[cfg(feature = "v3")] impl From> for rusty_paseto::core::Key<49> { fn from(key: Key) -> Self { let key: [u8; 49] = key.key.into(); key.into() } } #[cfg(feature = "v3")] impl From> for rusty_paseto::core::Key<48> { fn from(key: Key) -> Self { let key: [u8; 48] = key.key.into(); key.into() } } rusty_paserk-0.4.0/src/key/plaintext.rs000064400000000000000000000043621046102023000163220ustar 00000000000000use std::{fmt, str::FromStr}; use rusty_paseto::core::PasetoError; use crate::{write_b64, Key, KeyType, Version}; /// A key encoded in base64. It is not a secure serialization. pub struct PlaintextKey>(pub Key); impl> fmt::Display for PlaintextKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(V::KEY_HEADER)?; f.write_str(K::HEADER)?; write_b64(&self.0.key, f) } } impl> FromStr for PlaintextKey { type Err = PasetoError; fn from_str(s: &str) -> Result { let s = s .strip_prefix(V::KEY_HEADER) .ok_or(PasetoError::WrongHeader)?; let s = s.strip_prefix(K::HEADER).ok_or(PasetoError::WrongHeader)?; let key = crate::read_b64(s)?; Ok(PlaintextKey(Key { key })) } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl> serde::Serialize for PlaintextKey { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.collect_str(self) } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl<'de, V: Version, K: KeyType> serde::Deserialize<'de> for PlaintextKey { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FromStrVisitor(std::marker::PhantomData<(V, K)>); impl<'de, V: Version, K: KeyType> serde::de::Visitor<'de> for FromStrVisitor { type Value = PlaintextKey; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( formatter, "a \"{}{}\" serialized key", V::KEY_HEADER, K::HEADER ) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { v.parse().map_err(E::custom) } } deserializer.deserialize_str(FromStrVisitor(std::marker::PhantomData)) } } rusty_paserk-0.4.0/src/key.rs000064400000000000000000000065251046102023000143150ustar 00000000000000use std::fmt; use generic_array::{ArrayLength, GenericArray}; #[cfg(feature = "v3")] use rusty_paseto::core::V3; #[cfg(feature = "v4")] use rusty_paseto::core::V4; /// General information about a PASETO/PASERK version pub trait Version { /// Size of the symmetric local key type Local: ArrayLength; /// Size of the asymmetric public key type Public: ArrayLength; /// Size of the asymmetric secret key type Secret: ArrayLength; /// Header for PASETO const TOKEN_HEADER: &'static str; /// Header for PASERK const KEY_HEADER: &'static str; } #[cfg(feature = "v3")] impl Version for V3 { type Local = generic_array::typenum::U32; /// P-384 Public Key in compressed format type Public = generic_array::typenum::U49; /// P-384 Secret Key (384 bits = 48 bytes) type Secret = generic_array::typenum::U48; const TOKEN_HEADER: &'static str = "v3."; const KEY_HEADER: &'static str = "k3."; } #[cfg(feature = "v4")] impl Version for V4 { type Local = generic_array::typenum::U32; /// Compressed edwards y point type Public = generic_array::typenum::U32; /// Ed25519 scalar key, concatenated with the public key bytes type Secret = generic_array::typenum::U64; const TOKEN_HEADER: &'static str = "v4."; const KEY_HEADER: &'static str = "k4."; } /// Public verifying/encrypting keys #[derive(Debug)] pub struct Public; /// Secret signing/decrypting keys #[derive(Debug)] pub struct Secret; /// Local symmetric encryption/decrypting keys #[derive(Debug)] pub struct Local; /// General information about key types pub trait KeyType { /// Chooses the correct length from the version type KeyLen: ArrayLength; /// Header for this key type const HEADER: &'static str; /// ID header for this key type const ID: &'static str; } impl KeyType for Public { type KeyLen = V::Public; const HEADER: &'static str = "public."; const ID: &'static str = "pid."; } impl KeyType for Secret { type KeyLen = V::Secret; const HEADER: &'static str = "secret."; const ID: &'static str = "sid."; } impl KeyType for Local { type KeyLen = V::Local; const HEADER: &'static str = "local."; const ID: &'static str = "lid."; } /// A PASETO key. /// /// It is versioned and typed to ensure that [`Local`], [`Public`] and [`Secret`] keys are not used interchangably. pub struct Key> { pub(crate) key: GenericArray, } impl> core::cmp::PartialEq for Key { fn eq(&self, other: &Self) -> bool { self.key.eq(&other.key) } } impl> core::cmp::Eq for Key {} impl> Clone for Key { fn clone(&self) -> Self { Self { key: self.key.clone(), } } } impl> Copy for Key where GenericArray: Copy {} impl> fmt::Debug for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("Key"); if cfg!(fuzzing_repro) { f.field("key", &self.key).finish() } else { f.finish_non_exhaustive() } } } mod convert; #[cfg(feature = "arbitrary")] mod arbitrary; pub mod plaintext; rusty_paserk-0.4.0/src/lib.rs000064400000000000000000000263671046102023000143010ustar 00000000000000#![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] #![warn(missing_docs)] //! [Platform-Agnostic Serialized Keys](https://github.com/paseto-standard/paserk) //! //! PASERK is an extension to [PASETO](https://paseto.io) that provides key-wrapping and serialization. //! //! ## Motivation //! //! PASETO provides two types of tokens (called a purpose) in each of its versions: //! //! | Purpose | Cryptographic Operation | //! |----------|--------------------------------------------------------------------| //! | `local` | Symmetric-key authenticated encryption with additional data (AEAD) | //! | `public` | Asymmetric-key digital signatures (**no encryption**) | //! //! These two token modes solve at least 80% of use cases for secure tokens. You can //! even solve *unencrypted* symmetric-key authentication by storing your claims in //! the unencrypted footer, rather than encrypting them. //! //! The use-cases that PASETO doesn't address out of the box are: //! //! * Key-wrapping //! * Asymmetric encryption //! * Password-based key encryption //! //! PASERK aims to provide an answer for these circumstances, as well as provide a //! consistent standard for the encoding of PASETO keys. //! //! ## PASERK //! //! A serialized key in PASERK has the format: //! //! ```text //! k[version].[type].[data] //! ``` //! //! Where `[version]` is an integer, `[data]` is the (*typically* base64url-encoded) //! payload data, and `[type]` is one of the items in the following table: //! //! | PASERK Type | Meaning | PASETO Compatibility | \[data\] Encoded? | Safe in Footer? | //! |---------------|-----------------------------------------------------------------------------|----------------------|-------------------|-----------------| //! | `lid` | Unique Identifier for a separate PASERK for `local` PASETOs. | `local` | Yes | Yes | //! | `pid` | Unique Identifier for a separate PASERK for `public` PASETOs. (Public Key) | `public` | Yes | Yes | //! | `sid` | Unique Identifier for a separate PASERK for `public` PASETOs. (Secret Key) | `public` | Yes | Yes | //! | `local` | Symmetric key for `local` tokens. | `local` | Yes | **No** | //! | `public` | Public key for verifying `public` tokens. | `public` | Yes | **No** | //! | `secret` | Secret key for signing `public` tokens. | `public` | Yes | **No** | //! | `seal` | Symmetric key wrapped using asymmetric encryption. | `local` | Yes | Yes | //! | `local-wrap` | Symmetric key wrapped by another symmetric key. | `local` | No | Yes | //! | `local-pw` | Symmetric key wrapped using password-based encryption. | `local` | Yes | **No** | //! | `secret-wrap` | Asymmetric secret key wrapped by another symmetric key. | `public` | No | Yes | //! | `secret-pw` | Asymmetric secret key wrapped using password-based encryption. | `public` | Yes | **No** | //! //! ## implementation //! //! This library offers each of the key types and PASERK types from PASETO V3/4 as a unique rust type. //! //! ### [`Key`]s //! //! Since PASETO identifies 3 different key types, so do we. They are as follows //! * [`Local`] - For local symmetric encryption. //! * [`Public`] - For public asymmetric verification and encryption //! * [`Secret`] - For public asymmetric signing and decryption //! //! Keys are also versioned. We support the following versions //! * [`V3`] - NIST based modern cryptography (hmac, sha2, aes, p384) //! * [`V4`] - Sodium based modern cryptography (blake2b, chacha, ed25519) //! //! ### IDs: `lid`/`pid`/`sid` //! //! The [`KeyId`] type represents key ids. Building a KeyID is as simple as //! //! ``` //! use rusty_paserk::{Key, Local, KeyId, V4}; //! //! let local_key = Key::::new_os_random(); //! let kid: KeyId = local_key.into(); //! // kid.to_string() => "k4.lid.XxPub51WIAEmbVTmrs-lFoFodxTSKk8RuYEJk3gl-DYB" //! ``` //! //! You can also parse the KeyId from a string to have a smaller in memory representation. It can be safely shared and stored. //! //! ### Plaintext: `local`/`public`/`secret` //! //! The [`PlaintextKey`] type represents the base64 encoded plaintext key types. //! //! ``` //! use rusty_paserk::{Key, Local, PlaintextKey, V4}; //! //! let local_key = Key::::new_os_random(); //! let key = PlaintextKey(local_key); //! // key.to_string() => "k4.local.bkwMkk5uhGbHAISf4bzY5nlm6y_sfzOIAZTfj6Tc9y0" //! ``` //! //! These are considered sensitive and should not be shared (besides public keys) //! //! ### Seal //! //! Using a public key, you can seal a local key. Using the corresponding private key, you can unseal the key again. //! //! ``` //! use rusty_paserk::{SealedKey, Key, Local, Secret, V4}; //! //! let key = Key::::new_os_random(); //! //! let secret_key = Key::::new_os_random(); //! let public_key = secret_key.public_key(); //! //! let sealed = key.seal(&public_key).to_string(); //! // => "k4.seal.23KlrMHZLW4muL75Rnuqtaro9F16mqDNvmCbgDXi2IdNyWmjrbTVBEih1DhSI_5xp7b7mCHSFo1DMv-9GtZUSpyi4646XBxpbFShHjJihF_Af8maWsDqdzOof76ia0Cv" //! //! let sealed: SealedKey = sealed.parse().unwrap(); //! let key2 = sealed.unseal(&secret_key).unwrap(); //! assert_eq!(key, key2); //! ``` //! //! See the [`SealedKey`] type for more info. //! //! ### Wrap `local-wrap`/`secret-wrap` //! //! Using a local key, you can wrap a local or a secret key. It can be unwrapped using the same local key. //! //! ``` //! use rusty_paserk::{PieWrappedKey, Key, Local, V4}; //! //! let wrapping_key = Key::::new_os_random(); //! //! let local_key = Key::::new_os_random(); //! //! let wrapped_local = local_key.wrap_pie(&wrapping_key).to_string(); //! // => "k4.local-wrap.pie.RcAvOxHI0H-0uMsIl6KGcplH_tDlOhW1omFwXltZCiynHeRNH0hmn28AkN516h3WHuAReH3CvQ2SZ6mevnTquPETSd3XnlcbRWACT5GLWcus3BsD4IFWm9wFZgNF7C_E" //! //! let wrapped_local: PieWrappedKey = wrapped_local.parse().unwrap(); //! let local_key2 = wrapped_local.unwrap_key(&wrapping_key).unwrap(); //! assert_eq!(local_key, local_key2); //! ``` //! //! See the [`PieWrappedKey`] type for more info. //! //! ### Password wrapping `local-pw`/`secret-pw` //! //! Using a password, you can wrap a local or a secret key. It can be unwrapped using the same password. //! //! ``` //! use rusty_paserk::{PwWrappedKey, Key, Local, Secret, V4, Argon2State}; //! //! let password = "hunter2"; //! //! let secret_key = Key::::new_os_random(); //! //! let wrapped_secret = secret_key.pw_wrap(password.as_bytes()).to_string(); //! // => "k4.secret-pw.uscmLPzUoxxRfuzmY0DWcAAAAAAEAAAAAAAAAgAAAAHVNddVDnjRCc-ZmT-R-Xp7c7s4Wn1iH0dllAPFBmknEJpKGYP_aPoxVzNS_O93M0sCb68t7HjdD-jXWp-ioWe56iLoA6MlxE-SmnKear60aDwqk5fYv_EMD4Y2pV049BvDNGNN-MzR6fwW_OlyhV9omEvxmczAujM" //! //! let wrapped_secret: PwWrappedKey = wrapped_secret.parse().unwrap(); //! let secret_key2 = wrapped_secret.unwrap_key(password.as_bytes()).unwrap(); //! assert_eq!(secret_key, secret_key2); //! ``` //! //! See the [`PwWrappedKey`] type for more info. use std::ops::DerefMut; use base64ct::Encoding; use cipher::Unsigned; use generic_array::sequence::GenericSequence; #[cfg(feature = "v3")] pub use rusty_paseto::core::V3; #[cfg(feature = "v4")] pub use rusty_paseto::core::V4; pub use rusty_paseto::core::PasetoError; pub use id::KeyId; pub use key::{plaintext::PlaintextKey, Key, KeyType, Local, Public, Secret, Version}; pub use pbkw::PwWrappedKey; pub use pke::SealedKey; pub use wrap::PieWrappedKey; #[cfg(feature = "v3")] pub use pbkw::Pbkdf2State; #[cfg(feature = "v4")] pub use pbkw::Argon2State; mod id; mod key; mod pbkw; mod pke; mod wrap; /// Internally used traits for encryption version configuration pub mod internal { pub use crate::pbkw::{PwType, PwVersion, PwWrapType}; pub use crate::pke::SealedVersion; pub use crate::wrap::{PieVersion, PieWrapType, WrapType}; } fn write_b64(b: &[u8], w: &mut W) -> std::fmt::Result { let mut buffer = [0; 64]; for chunk in b.chunks(48) { let s = base64ct::Base64UrlUnpadded::encode(chunk, &mut buffer).unwrap(); w.write_str(s)?; } Ok(()) } fn read_b64 + DerefMut + Default>( s: &str, ) -> Result { let expected_len = (s.len() + 3) / 4 * 3; if expected_len < ::USIZE { return Err(PasetoError::PayloadBase64Decode { source: base64::DecodeError::InvalidLength(s.len()), }); } let mut total = L::default(); let len = base64ct::Base64UrlUnpadded::decode(s, &mut total) .map_err(|_| PasetoError::PayloadBase64Decode { source: base64::DecodeError::InvalidLength(s.len()), })? .len(); if len != ::USIZE { return Err(PasetoError::PayloadBase64Decode { source: base64::DecodeError::InvalidLength(s.len()), }); } Ok(total) } /// Whether the key serialization is safe to be added to a PASETO footer. pub trait SafeForFooter {} #[cfg(any(test, fuzzing))] pub mod fuzzing { use rand::{CryptoRng, RngCore}; #[derive(Clone, Debug)] /// a consistent rng store pub struct FakeRng { pub bytes: [u8; N], pub start: usize, } #[cfg(feature = "arbitrary")] impl<'a, const N: usize> arbitrary::Arbitrary<'a> for FakeRng where [u8; N]: arbitrary::Arbitrary<'a>, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self { bytes: <[u8; N]>::arbitrary(u)?, start: 0, }) } } impl RngCore for FakeRng { fn next_u32(&mut self) -> u32 { unimplemented!() } fn next_u64(&mut self) -> u64 { unimplemented!() } fn fill_bytes(&mut self, dest: &mut [u8]) { let remaining = N - self.start; let requested = dest.len(); if requested > remaining { panic!("not enough entropy"); } dest.copy_from_slice(&self.bytes[self.start..self.start + requested]); self.start += requested; } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { self.fill_bytes(dest); Ok(()) } } // not really impl CryptoRng for FakeRng {} pub mod seal { #[cfg(feature = "v3")] pub use crate::pke::fuzz_tests::V3SealInput; #[cfg(feature = "v4")] pub use crate::pke::fuzz_tests::V4SealInput; } pub mod wrap { pub use crate::wrap::fuzz_tests::FuzzInput; } } rusty_paserk-0.4.0/src/pbkw.rs000064400000000000000000000441151046102023000144650ustar 00000000000000//! PBKD (Password-Based Key Wrapping). //! Derive a unique encryption key from a password, then use it to wrap the key. //! //! use std::{fmt, ops::DerefMut, str::FromStr}; use cipher::{IvSizeUser, KeyInit, KeyIvInit, StreamCipher}; use digest::{Digest, Mac, OutputSizeUser}; use generic_array::{ sequence::{Concat, GenericSequence, Split}, typenum::U32, ArrayLength, GenericArray, }; use rand::{CryptoRng, RngCore}; use rand::rngs::OsRng; use rusty_paseto::core::PasetoError; #[cfg(feature = "v3")] use rusty_paseto::core::V3; #[cfg(feature = "v4")] use rusty_paseto::core::V4; use subtle::ConstantTimeEq; use crate::{read_b64, write_b64, Key, KeyType, Local, Secret, Version}; /// Password wrapped keys /// /// # Local password wrapping /// ``` /// use rusty_paserk::{PwWrappedKey, Key, Local, V4, Argon2State}; /// /// let password = "hunter2"; /// /// let local_key = Key::::new_os_random(); /// /// let wrapped_local = local_key.pw_wrap(password.as_bytes()).to_string(); /// // => "k4.local-pw.Ibx3cOeBAsBEJsjMFXBgYgAAAAAEAAAAAAAAAgAAAAG2RRITciuBSfCWR3e324EuatP9XsKkyLcTgeZPxg6N-JhlpV2GAqvPQjRK89QnepimbYTaNitInOj45ksyyNfAEjRgjuVYUZo7vrI6unVfvtDehIc8VvgR" /// /// let wrapped_local: PwWrappedKey = wrapped_local.parse().unwrap(); /// let local_key2 = wrapped_local.unwrap_key(password.as_bytes()).unwrap(); /// assert_eq!(local_key, local_key2); /// ``` /// /// # Secret password wrapping /// ``` /// use rusty_paserk::{PwWrappedKey, Key, Local, Secret, V4, Argon2State}; /// /// let password = "hunter2"; /// /// let secret_key = Key::::new_os_random(); /// /// let wrapped_secret = secret_key.pw_wrap(password.as_bytes()).to_string(); /// // => "k4.secret-pw.uscmLPzUoxxRfuzmY0DWcAAAAAAEAAAAAAAAAgAAAAHVNddVDnjRCc-ZmT-R-Xp7c7s4Wn1iH0dllAPFBmknEJpKGYP_aPoxVzNS_O93M0sCb68t7HjdD-jXWp-ioWe56iLoA6MlxE-SmnKear60aDwqk5fYv_EMD4Y2pV049BvDNGNN-MzR6fwW_OlyhV9omEvxmczAujM" /// /// let wrapped_secret: PwWrappedKey = wrapped_secret.parse().unwrap(); /// let secret_key2 = wrapped_secret.unwrap_key(password.as_bytes()).unwrap(); /// assert_eq!(secret_key, secret_key2); /// ``` pub struct PwWrappedKey> { salt: V::Salt, state: V::KdfState, nonce: cipher::Iv, edk: GenericArray, tag: digest::Output, } impl> Key { /// Password wrapped keys /// /// * Use the default KDF settings /// * Use the OS RNG to determine a random salt /// /// # Local password wrapping /// ``` /// use rusty_paserk::{PwWrappedKey, Key, Local, V4, Argon2State}; /// /// let password = "hunter2"; /// /// let local_key = Key::::new_os_random(); /// /// let wrapped_local = local_key.pw_wrap(password.as_bytes()).to_string(); /// // => "k4.local-pw.Ibx3cOeBAsBEJsjMFXBgYgAAAAAEAAAAAAAAAgAAAAG2RRITciuBSfCWR3e324EuatP9XsKkyLcTgeZPxg6N-JhlpV2GAqvPQjRK89QnepimbYTaNitInOj45ksyyNfAEjRgjuVYUZo7vrI6unVfvtDehIc8VvgR" /// /// let wrapped_local: PwWrappedKey = wrapped_local.parse().unwrap(); /// let local_key2 = wrapped_local.unwrap_key(password.as_bytes()).unwrap(); /// assert_eq!(local_key, local_key2); /// ``` /// /// # Secret password wrapping /// ``` /// use rusty_paserk::{PwWrappedKey, Key, Local, Secret, V4, Argon2State}; /// /// let password = "hunter2"; /// /// let secret_key = Key::::new_os_random(); /// /// let wrapped_secret = secret_key.pw_wrap(password.as_bytes()).to_string(); /// // => "k4.secret-pw.uscmLPzUoxxRfuzmY0DWcAAAAAAEAAAAAAAAAgAAAAHVNddVDnjRCc-ZmT-R-Xp7c7s4Wn1iH0dllAPFBmknEJpKGYP_aPoxVzNS_O93M0sCb68t7HjdD-jXWp-ioWe56iLoA6MlxE-SmnKear60aDwqk5fYv_EMD4Y2pV049BvDNGNN-MzR6fwW_OlyhV9omEvxmczAujM" /// /// let wrapped_secret: PwWrappedKey = wrapped_secret.parse().unwrap(); /// let secret_key2 = wrapped_secret.unwrap_key(password.as_bytes()).unwrap(); /// assert_eq!(secret_key, secret_key2); /// ``` pub fn pw_wrap(&self, password: &[u8]) -> PwWrappedKey { self.pw_wrap_with_settings(password, V::KdfState::default()) } /// Password wrapped keys /// /// * Use the settings to configure how strong the derived key should be /// * Use the OS RNG to determine a random salt pub fn pw_wrap_with_settings( &self, password: &[u8], settings: V::KdfState, ) -> PwWrappedKey { self.pw_wrap_with_settings_and_rng(password, settings, &mut OsRng) } /// Password wrapped keys /// /// * Use the settings to configure how strong the derived key should be /// * Use the RNG source to determine a random salt pub fn pw_wrap_with_settings_and_rng( &self, password: &[u8], settings: V::KdfState, rng: &mut (impl RngCore + CryptoRng), ) -> PwWrappedKey { let mut salt = V::Salt::default(); rng.fill_bytes(&mut salt); let k = V::kdf(password, &salt, &settings); let ek = ::new() .chain_update([0xff]) .chain_update(k) .finalize(); let ek = V::split_ek(ek); let ak = ::new() .chain_update([0xfe]) .chain_update(k) .finalize(); let mut n = cipher::Iv::::default(); rng.fill_bytes(&mut n); let mut edk = GenericArray::::default(); ::new(&ek, &n) .apply_keystream_b2b(self.as_ref(), &mut edk) .unwrap(); let tag = ::new_from_slice(&ak) .unwrap() .chain_update(V::KEY_HEADER) .chain_update(K::WRAP_HEADER) .chain_update(&*salt) .chain_update(V::encode_state(&settings)) .chain_update(&n) .chain_update(&edk) .finalize() .into_bytes(); PwWrappedKey { salt, state: settings, nonce: n, edk, tag, } } } impl> PwWrappedKey { /// Unwrap the password wrapped key pub fn unwrap_key(mut self, password: &[u8]) -> Result, PasetoError> { let k = V::kdf(password, &self.salt, &self.state); let ak = ::new() .chain_update([0xfe]) .chain_update(k) .finalize(); let tag = ::new_from_slice(&ak) .unwrap() .chain_update(V::KEY_HEADER) .chain_update(K::WRAP_HEADER) .chain_update(&*self.salt) .chain_update(V::encode_state(&self.state)) .chain_update(&self.nonce) .chain_update(&self.edk) .finalize() .into_bytes(); // step 4: Compare t with t2 in constant-time. If it doesn't match, abort. if tag.ct_ne(&self.tag).into() { return Err(PasetoError::InvalidSignature); } let ek = ::new() .chain_update([0xff]) .chain_update(k) .finalize(); let ek = V::split_ek(ek); ::new(&ek, &self.nonce).apply_keystream(&mut self.edk); Ok(Key { key: self.edk }) } /// Return the password KDF settings that were used to encrypt the key. /// This is important to check prevent DOS attacks otherwise an attacked can /// send a key with arbitrary large memory and iteration counts. pub fn settings(&self) -> &V::KdfState { &self.state } } impl> FromStr for PwWrappedKey { type Err = PasetoError; fn from_str(s: &str) -> Result { let s = s .strip_prefix(V::KEY_HEADER) .ok_or(PasetoError::WrongHeader)?; let s = s .strip_prefix(K::WRAP_HEADER) .ok_or(PasetoError::WrongHeader)?; let total = read_b64::(s)?; let (salt_state_nonce_edk, tag) = total.split(); let (salt_state_nonce, edk) = salt_state_nonce_edk.split(); let (salt_state, nonce) = salt_state_nonce.split(); let (salt, state) = salt_state.split(); let state = V::decode_state(state); Ok(Self { salt, state, nonce, edk, tag, }) } } impl> fmt::Display for PwWrappedKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(V::KEY_HEADER)?; f.write_str(K::WRAP_HEADER)?; let output: K::SaltStateIv = self .salt .concat(V::encode_state(&self.state)) .concat(self.nonce.clone()) .into(); let output = output.concat(self.edk.clone()).concat(self.tag.clone()); write_b64(&output, f) } } #[cfg(feature = "v3")] /// PBKDF2 parameters for V3 password wrapping pub struct Pbkdf2State { /// Defaults to 100,000 according to the PASERK PBKW specifications. /// Password hashing recommends 600,000 iterations, but we're not directly storing the output /// of this computation pub iterations: u32, } #[cfg(feature = "v3")] impl Default for Pbkdf2State { fn default() -> Self { Self { iterations: 100_000, } } } #[cfg(feature = "v4")] /// Argon2 parameters for V4 password wrapping pub struct Argon2State { /// Defaults to 64 MiB pub mem: u32, /// Defaults to 2 pub time: u32, /// Defaults to 1 pub para: u32, } #[cfg(feature = "v4")] impl Default for Argon2State { fn default() -> Self { Self { // 64 MiB mem: 0x0400_0000, time: 2, para: 1, } } } /// Version info for configuring password wrapping pub trait PwVersion: Version { /// The settings that the KDF function uses type KdfState: Default; #[doc(hidden)] type KdfStateLen: ArrayLength; #[doc(hidden)] type Cipher: StreamCipher + KeyIvInit; #[doc(hidden)] type KeyHash: Digest; #[doc(hidden)] type TagMac: Mac + KeyInit; #[doc(hidden)] type Salt: Concat< u8, Self::KdfStateLen, Rest = GenericArray, Output = Self::SaltState, > + DerefMut + Copy + Default; #[doc(hidden)] type SaltState: Split< u8, >::Length, First = Self::Salt, Second = GenericArray, > + Concat< u8, ::IvSize, Rest = cipher::Iv, Output = Self::SaltStateIv, >; #[doc(hidden)] type SaltStateIv: Split< u8, >::Length, First = Self::SaltState, Second = cipher::Iv, >; #[doc(hidden)] fn kdf(pw: &[u8], salt: &Self::Salt, state: &Self::KdfState) -> GenericArray; #[doc(hidden)] fn split_ek(ek: digest::Output) -> cipher::Key; #[doc(hidden)] fn encode_state(s: &Self::KdfState) -> GenericArray; #[doc(hidden)] fn decode_state(s: GenericArray) -> Self::KdfState; } /// Key wrapping type. Can be either `local-pw.` or `secret-pw.` pub trait PwType { /// The type of password wrapped key const WRAP_HEADER: &'static str; } impl PwType for Local { const WRAP_HEADER: &'static str = "local-pw."; } impl PwType for Secret { const WRAP_HEADER: &'static str = "secret-pw."; } /// Helper trait for configuring the key wrapping pub trait PwWrapType: KeyType + PwType { #[doc(hidden)] type SaltStateIv: From + Concat< u8, Self::KeyLen, Rest = GenericArray, Output = Self::SaltStateIvEdk, >; #[doc(hidden)] type SaltStateIvEdk: Split< u8, >::Length, First = V::SaltStateIv, Second = GenericArray, > + Concat< u8, ::OutputSize, Rest = GenericArray::OutputSize>, Output = Self::SaltStateIvEdkTag, >; #[doc(hidden)] type SaltStateIvEdkTag: Split< u8, >::Length, First = Self::SaltStateIvEdk, Second = GenericArray::OutputSize>, > + DerefMut + Default; } #[cfg(feature = "v3")] impl PwVersion for V3 { type Cipher = ctr::Ctr64BE; type KeyHash = sha2::Sha384; type TagMac = hmac::Hmac; type KdfStateLen = generic_array::typenum::U4; type KdfState = Pbkdf2State; type Salt = GenericArray; type SaltState = GenericArray; type SaltStateIv = GenericArray; fn kdf(pw: &[u8], salt: &Self::Salt, state: &Self::KdfState) -> GenericArray { pbkdf2::pbkdf2_hmac_array::(pw, salt.as_slice(), state.iterations).into() } fn split_ek(ek: digest::Output) -> cipher::Key { let (ek, _) = ek.split(); ek } fn encode_state(s: &Self::KdfState) -> GenericArray { s.iterations.to_be_bytes().into() } fn decode_state(s: GenericArray) -> Self::KdfState { let i = u32::from_be_bytes(s.into()); Pbkdf2State { iterations: i } } } #[cfg(feature = "v4")] impl PwVersion for V4 { type Cipher = chacha20::XChaCha20; type KeyHash = blake2::Blake2b; type TagMac = blake2::Blake2bMac; type KdfStateLen = generic_array::typenum::U16; type KdfState = Argon2State; type Salt = GenericArray; type SaltState = GenericArray; type SaltStateIv = GenericArray; fn kdf(pw: &[u8], salt: &Self::Salt, state: &Self::KdfState) -> GenericArray { let mut out = GenericArray::::default(); argon2::Argon2::new( argon2::Algorithm::Argon2id, argon2::Version::V0x13, argon2::Params::new(state.mem / 1024, state.time, state.para, Some(32)).unwrap(), ) .hash_password_into(pw, salt.as_slice(), &mut out) .unwrap(); out } fn split_ek(ek: digest::Output) -> cipher::Key { ek } fn encode_state(s: &Self::KdfState) -> GenericArray { GenericArray::::default() .concat(s.mem.to_be_bytes().into()) .concat(s.time.to_be_bytes().into()) .concat(s.para.to_be_bytes().into()) } fn decode_state(s: GenericArray) -> Self::KdfState { let (mem1, b) = s.split(); let (mem2, b) = b.split(); let (time, para) = b.split(); let _mem1: GenericArray = mem1; let mem = u32::from_be_bytes(mem2.into()); let time = u32::from_be_bytes(time.into()); let para = u32::from_be_bytes(para.into()); Argon2State { mem, time, para } } } #[cfg(feature = "v4")] impl PwWrapType for Local { type SaltStateIv = GenericArray; type SaltStateIvEdk = GenericArray; type SaltStateIvEdkTag = GenericArray; } #[cfg(feature = "v3")] impl PwWrapType for Local { type SaltStateIv = GenericArray; type SaltStateIvEdk = GenericArray; type SaltStateIvEdkTag = GenericArray; } #[cfg(feature = "v4")] impl PwWrapType for Secret { type SaltStateIv = GenericArray; type SaltStateIvEdk = GenericArray; type SaltStateIvEdkTag = GenericArray; } #[cfg(feature = "v3")] impl PwWrapType for Secret { type SaltStateIv = GenericArray; type SaltStateIvEdk = GenericArray; type SaltStateIvEdkTag = GenericArray; } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl> serde::Serialize for PwWrappedKey { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.collect_str(self) } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl<'de, V: PwVersion, K: PwWrapType> serde::Deserialize<'de> for PwWrappedKey { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FromStrVisitor(std::marker::PhantomData<(V, K)>); impl<'de, V: PwVersion, K: PwWrapType> serde::de::Visitor<'de> for FromStrVisitor { type Value = PwWrappedKey; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( formatter, "a \"{}{}\" serialized key", V::KEY_HEADER, K::WRAP_HEADER ) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { v.parse().map_err(E::custom) } } deserializer.deserialize_str(FromStrVisitor(std::marker::PhantomData)) } } rusty_paserk-0.4.0/src/pke.rs000064400000000000000000000421001046102023000142710ustar 00000000000000//! PKE (Public-Key Encryption). //! PASERK uses Public-Key encryption to wrap symmetric keys for use in local tokens. //! //! use std::{fmt, str::FromStr}; use cipher::{inout::InOutBuf, KeyIvInit, StreamCipher}; use digest::{Digest, Mac}; use generic_array::{ sequence::{Concat, Split}, ArrayLength, GenericArray, }; use rand::{rngs::OsRng, CryptoRng, RngCore}; use rusty_paseto::core::PasetoError; use subtle::ConstantTimeEq; #[cfg(feature = "v3")] use rusty_paseto::core::V3; #[cfg(feature = "v4")] use rusty_paseto::core::V4; use crate::{read_b64, write_b64, Key, Local, Public, Secret, Version}; /// A local key encrypted with an asymmetric wrapping key. /// /// # Secret Wrapping /// ``` /// use rusty_paserk::{SealedKey, Key, Local, Secret, V4}; /// /// let key = Key::::new_os_random(); /// /// let secret_key = Key::::new_os_random(); /// let public_key = secret_key.public_key(); /// /// let sealed = key.seal(&public_key).to_string(); /// // => "k4.seal.23KlrMHZLW4muL75Rnuqtaro9F16mqDNvmCbgDXi2IdNyWmjrbTVBEih1DhSI_5xp7b7mCHSFo1DMv-9GtZUSpyi4646XBxpbFShHjJihF_Af8maWsDqdzOof76ia0Cv" /// /// let sealed: SealedKey = sealed.parse().unwrap(); /// let key2 = sealed.unseal(&secret_key).unwrap(); /// assert_eq!(key, key2); /// ``` pub struct SealedKey { tag: GenericArray, ephemeral_public_key: GenericArray, encrypted_data_key: GenericArray, } impl super::SafeForFooter for SealedKey where V: SealedVersion {} impl Key { /// A local key encrypted with an asymmetric wrapping key. /// /// # Secret Wrapping /// ``` /// use rusty_paserk::{SealedKey, Key, Local, Secret, V4}; /// /// let key = Key::::new_os_random(); /// /// let secret_key = Key::::new_os_random(); /// let public_key = secret_key.public_key(); /// /// let sealed = key.seal(&public_key).to_string(); /// // => "k4.seal.23KlrMHZLW4muL75Rnuqtaro9F16mqDNvmCbgDXi2IdNyWmjrbTVBEih1DhSI_5xp7b7mCHSFo1DMv-9GtZUSpyi4646XBxpbFShHjJihF_Af8maWsDqdzOof76ia0Cv" /// /// let sealed: SealedKey = sealed.parse().unwrap(); /// let key2 = sealed.unseal(&secret_key).unwrap(); /// assert_eq!(key, key2); /// ``` pub fn seal(&self, sealing_key: &Key) -> SealedKey { self.seal_with_rng(sealing_key, &mut OsRng) } /// Seal a local key, encrypted with an asymmetric wrapping key. /// /// The ephemeral key is generated from the provided random source. pub fn seal_with_rng( &self, sealing_key: &Key, rng: &mut (impl RngCore + CryptoRng), ) -> SealedKey { V::seal(self, sealing_key, rng) } } impl SealedKey { /// Unseal an encrypted local key. pub fn unseal(self, unsealing_key: &Key) -> Result, PasetoError> { V::unseal(self, unsealing_key) } } /// Version info for configuring key sealing pub trait SealedVersion: Version + Sized { #[doc(hidden)] type TagLen: ArrayLength; #[doc(hidden)] type EpkLen: ArrayLength; #[doc(hidden)] type TotalLen: ArrayLength; #[allow(clippy::type_complexity)] #[doc(hidden)] fn split_total(total: GenericArray) -> SealedKey; #[doc(hidden)] fn join_total(sealed: &SealedKey) -> GenericArray; #[doc(hidden)] fn seal( plaintext_key: &Key, sealing_key: &Key, rng: &mut (impl RngCore + CryptoRng), ) -> SealedKey; #[doc(hidden)] fn unseal( sealed_key: SealedKey, unsealing_key: &Key, ) -> Result, PasetoError>; } #[cfg(feature = "v3")] impl SealedVersion for V3 { type TagLen = generic_array::typenum::U48; type EpkLen = generic_array::typenum::U49; /// extra byte is for annoying base64 padding type TotalLen = generic_array::typenum::U129; fn split_total(total: GenericArray) -> SealedKey { let (tag, rest) = total.split(); let (ephemeral_public_key, encrypted_data_key) = rest.split(); SealedKey { tag, ephemeral_public_key, encrypted_data_key, } } fn join_total(sealed: &SealedKey) -> GenericArray { sealed .tag .concat(sealed.ephemeral_public_key) .concat(sealed.encrypted_data_key) } fn seal( plaintext_key: &Key, sealing_key: &Key, rng: &mut (impl RngCore + CryptoRng), ) -> SealedKey { use p384::ecdh::EphemeralSecret; use p384::{EncodedPoint, PublicKey}; let pk = PublicKey::from_sec1_bytes(sealing_key.as_ref()).unwrap(); let esk = EphemeralSecret::random(rng); let epk: EncodedPoint = esk.public_key().into(); let epk = epk.compress(); let epk = epk.as_bytes(); let xk = esk.diffie_hellman(&pk); let (ek, n) = sha2::Sha384::new() .chain_update([0x01]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.raw_secret_bytes()) .chain_update(epk) .chain_update(sealing_key.as_ref()) .finalize() .split(); let ak = sha2::Sha384::new() .chain_update([0x02]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.raw_secret_bytes()) .chain_update(epk) .chain_update(sealing_key.as_ref()) .finalize(); let mut edk = GenericArray::::Local>::default(); ctr::Ctr64BE::::new(&ek, &n) .apply_keystream_inout(InOutBuf::new(plaintext_key.as_ref(), &mut edk).unwrap()); let tag = hmac::Hmac::::new_from_slice(&ak) .unwrap() .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(epk) .chain_update(edk) .finalize() .into_bytes(); SealedKey { tag, ephemeral_public_key: *GenericArray::from_slice(epk), encrypted_data_key: edk, } } fn unseal( mut sealed_key: SealedKey, unsealing_key: &Key, ) -> Result, PasetoError> { use p384::ecdh::diffie_hellman; use p384::{EncodedPoint, PublicKey, SecretKey}; let sk = SecretKey::from_bytes(&unsealing_key.key).unwrap(); let pk: EncodedPoint = sk.public_key().into(); let pk = pk.compress(); let pk = pk.as_bytes(); let epk = PublicKey::from_sec1_bytes(sealed_key.ephemeral_public_key.as_slice()).unwrap(); let xk = diffie_hellman(sk.to_nonzero_scalar(), epk.as_affine()); let ak = sha2::Sha384::new() .chain_update([0x02]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.raw_secret_bytes()) .chain_update(sealed_key.ephemeral_public_key) .chain_update(pk) .finalize(); let tag = hmac::Hmac::::new_from_slice(&ak) .unwrap() .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(sealed_key.ephemeral_public_key) .chain_update(sealed_key.encrypted_data_key) .finalize() .into_bytes(); // step 6: Compare t2 with t, using a constant-time compare function. If it does not match, abort. if sealed_key.tag.ct_ne(&tag).into() { return Err(PasetoError::InvalidSignature); } let (ek, n) = sha2::Sha384::new() .chain_update([0x01]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.raw_secret_bytes()) .chain_update(sealed_key.ephemeral_public_key) .chain_update(pk) .finalize() .split(); ctr::Ctr64BE::::new(&ek, &n) .apply_keystream(&mut sealed_key.encrypted_data_key); Ok(Key { key: sealed_key.encrypted_data_key, }) } } #[cfg(feature = "v4")] impl SealedVersion for V4 { type TagLen = generic_array::typenum::U32; type EpkLen = generic_array::typenum::U32; type TotalLen = generic_array::typenum::U96; fn split_total(total: GenericArray) -> SealedKey { let (tag, rest) = total.split(); let (ephemeral_public_key, encrypted_data_key) = rest.split(); SealedKey { tag, ephemeral_public_key, encrypted_data_key, } } fn join_total(sealed: &SealedKey) -> GenericArray { sealed .tag .concat(sealed.ephemeral_public_key) .concat(sealed.encrypted_data_key) } fn seal( plaintext_key: &Key, sealing_key: &Key, rng: &mut (impl RngCore + CryptoRng), ) -> SealedKey { use curve25519_dalek::{ edwards::{CompressedEdwardsY, EdwardsPoint}, scalar::{clamp_integer, Scalar}, }; // Given a plaintext data key (pdk), and an Ed25519 public key (pk). let pk = CompressedEdwardsY(sealing_key.key.into()); // step 1: Calculate the birationally-equivalent X25519 public key (xpk) from pk. let xpk = pk.decompress().unwrap().to_montgomery(); let esk = Scalar::from_bytes_mod_order(clamp_integer({ let mut esk = [0; 32]; rng.fill_bytes(&mut esk); esk })); let epk = EdwardsPoint::mul_base(&esk).to_montgomery(); // diffie hellman exchange let xk = esk * xpk; let ek = blake2::Blake2b::new() .chain_update([0x01]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.as_bytes()) .chain_update(epk.as_bytes()) .chain_update(xpk.as_bytes()) .finalize(); let ak = blake2::Blake2b::::new() .chain_update([0x02]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.as_bytes()) .chain_update(epk.as_bytes()) .chain_update(xpk.as_bytes()) .finalize(); let n = blake2::Blake2b::new() .chain_update(epk.as_bytes()) .chain_update(xpk.as_bytes()) .finalize(); let mut edk = GenericArray::::Local>::default(); chacha20::XChaCha20::new(&ek, &n) .apply_keystream_inout(InOutBuf::new(plaintext_key.as_ref(), &mut edk).unwrap()); let tag = blake2::Blake2bMac::new_from_slice(&ak) .unwrap() .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(epk.as_bytes()) .chain_update(edk) .finalize() .into_bytes(); SealedKey { tag, ephemeral_public_key: epk.to_bytes().into(), encrypted_data_key: edk, } } fn unseal( mut sealed_key: SealedKey, unsealing_key: &Key, ) -> Result, PasetoError> { use curve25519_dalek::edwards::CompressedEdwardsY; use ed25519_dalek::hazmat::ExpandedSecretKey; let epk: [u8; 32] = sealed_key.ephemeral_public_key.into(); let epk = curve25519_dalek::MontgomeryPoint(epk); // expand pk/sk pair from ed25519 to x25519 let (sk, pk) = unsealing_key.key.split(); let pk = CompressedEdwardsY(pk.into()); let xpk = pk.decompress().unwrap().to_montgomery(); let sk: ed25519_dalek::SecretKey = sk.into(); let xsk = ExpandedSecretKey::from(&sk); // diffie hellman exchange let xk = xsk.scalar * epk; let ak = blake2::Blake2b::::new() .chain_update([0x02]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.as_bytes()) .chain_update(epk.as_bytes()) .chain_update(xpk.as_bytes()) .finalize(); let t2 = blake2::Blake2bMac::::new_from_slice(&ak) .unwrap() .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(epk.as_bytes()) .chain_update(sealed_key.encrypted_data_key) .finalize() .into_bytes(); // step 6: Compare t2 with t, using a constant-time compare function. If it does not match, abort. if sealed_key.tag.ct_ne(&t2).into() { return Err(PasetoError::InvalidSignature); } let ek = blake2::Blake2b::new() .chain_update([0x01]) .chain_update(Self::KEY_HEADER) .chain_update("seal.") .chain_update(xk.as_bytes()) .chain_update(epk.as_bytes()) .chain_update(xpk.as_bytes()) .finalize(); let n = blake2::Blake2b::new() .chain_update(epk.as_bytes()) .chain_update(xpk.as_bytes()) .finalize(); chacha20::XChaCha20::new(&ek, &n).apply_keystream(&mut sealed_key.encrypted_data_key); Ok(Key { key: sealed_key.encrypted_data_key, }) } } impl FromStr for SealedKey { type Err = PasetoError; fn from_str(s: &str) -> Result { let s = s .strip_prefix(V::KEY_HEADER) .ok_or(PasetoError::WrongHeader)?; let s = s.strip_prefix("seal.").ok_or(PasetoError::WrongHeader)?; let total = read_b64::>(s)?; Ok(V::split_total(total)) } } impl fmt::Display for SealedKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(V::KEY_HEADER)?; f.write_str("seal.")?; write_b64(&V::join_total(self), f) } } #[cfg(any(test, fuzzing))] pub mod fuzz_tests { use crate::{fuzzing::FakeRng, Key, Local, Secret}; #[cfg(feature = "v3")] #[derive(Debug)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct V3SealInput { key: Key, secret_key: Key, ephemeral: FakeRng<48>, } #[cfg(feature = "v3")] impl V3SealInput { pub fn run(mut self) { let x: Option = p384::Scalar::from_bytes(&self.ephemeral.bytes.into()).into(); match x { Some(s) if s.is_zero().into() => return, None => return, Some(_) => {} } let sealed = self .key .seal_with_rng(&self.secret_key.public_key(), &mut self.ephemeral); let local_key2 = sealed.unseal(&self.secret_key).unwrap(); assert_eq!(self.key, local_key2); } } #[cfg(feature = "v4")] #[derive(Debug)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct V4SealInput { key: Key, secret_key: Key, ephemeral: FakeRng<32>, } #[cfg(feature = "v4")] impl V4SealInput { pub fn run(mut self) { let sealed = self .key .seal_with_rng(&self.secret_key.public_key(), &mut self.ephemeral); let local_key2 = sealed.unseal(&self.secret_key).unwrap(); assert_eq!(self.key, local_key2); } } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl serde::Serialize for SealedKey { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.collect_str(self) } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl<'de, V: SealedVersion> serde::Deserialize<'de> for SealedKey { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FromStrVisitor(std::marker::PhantomData); impl<'de, V: SealedVersion> serde::de::Visitor<'de> for FromStrVisitor { type Value = SealedKey; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a \"{}seal.\" serialized key", V::KEY_HEADER) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { v.parse().map_err(E::custom) } } deserializer.deserialize_str(FromStrVisitor(std::marker::PhantomData)) } } rusty_paserk-0.4.0/src/wrap.rs000064400000000000000000000406751046102023000145020ustar 00000000000000//! PASERK uses symmetric-key encryption to wrap PASETO keys. //! //! //! use std::{fmt, ops::DerefMut, str::FromStr}; use cipher::{KeyInit, KeyIvInit, StreamCipher}; use digest::{Mac, OutputSizeUser}; use generic_array::{ sequence::{Concat, GenericSequence, Split}, typenum::U32, GenericArray, }; use rand::{rngs::OsRng, CryptoRng, RngCore}; use rusty_paseto::core::PasetoError; #[cfg(feature = "v3")] use rusty_paseto::core::V3; #[cfg(feature = "v4")] use rusty_paseto::core::V4; use subtle::ConstantTimeEq; use crate::{read_b64, write_b64, Key, KeyType, Local, Secret, Version}; /// Paragon Initiative Enterprises standard key-wrapping /// /// # Local Wrapping /// ``` /// use rusty_paserk::{PieWrappedKey, Key, Local, V4}; /// /// let wrapping_key = Key::::new_os_random(); /// /// let local_key = Key::::new_os_random(); /// /// let wrapped_local = local_key.wrap_pie(&wrapping_key).to_string(); /// // => "k4.local-wrap.pie.RcAvOxHI0H-0uMsIl6KGcplH_tDlOhW1omFwXltZCiynHeRNH0hmn28AkN516h3WHuAReH3CvQ2SZ6mevnTquPETSd3XnlcbRWACT5GLWcus3BsD4IFWm9wFZgNF7C_E" /// /// let wrapped_local: PieWrappedKey = wrapped_local.parse().unwrap(); /// let local_key2 = wrapped_local.unwrap_key(&wrapping_key).unwrap(); /// assert_eq!(local_key, local_key2); /// ``` /// /// # Secret Wrapping /// ``` /// use rusty_paserk::{PieWrappedKey, Key, Local, Secret, V4}; /// /// let wrapping_key = Key::::new_os_random(); /// /// let secret_key = Key::::new_os_random(); /// /// let wrapped_secret = secret_key.wrap_pie(&wrapping_key).to_string(); /// // => "k4.secret-wrap.pie.cTTnZwzBA3AKBugQCzmctv5R9CjyPOlelG9SLZrhupDwk6vYx-3UQFCZ7x4d57KU4K4U1qJeFP6ELzkMJ0s8qHt0hsQkW14Ni6TJ89MRzEqglUgI6hJD-EF2E9kIFO5YuC5MHwXN7Wi_vG1S3L-OoTjZgT_ZJ__8T7SJhvYLodo" /// /// let wrapped_secret: PieWrappedKey = wrapped_secret.parse().unwrap(); /// let secret_key2 = wrapped_secret.unwrap_key(&wrapping_key).unwrap(); /// assert_eq!(secret_key, secret_key2); /// ``` pub struct PieWrappedKey> { tag: V::Tag, nonce: GenericArray, wrapped_key: GenericArray, } impl super::SafeForFooter for PieWrappedKey where V: PieVersion, K: PieWrapType, { } impl> Key { /// Paragon Initiative Enterprises standard key-wrapping /// /// # Local Wrapping /// ``` /// use rusty_paserk::{PieWrappedKey, Key, Local, V4}; /// /// let wrapping_key = Key::::new_os_random(); /// /// let local_key = Key::::new_os_random(); /// /// let wrapped_local = local_key.wrap_pie(&wrapping_key).to_string(); /// // => "k4.local-wrap.pie.RcAvOxHI0H-0uMsIl6KGcplH_tDlOhW1omFwXltZCiynHeRNH0hmn28AkN516h3WHuAReH3CvQ2SZ6mevnTquPETSd3XnlcbRWACT5GLWcus3BsD4IFWm9wFZgNF7C_E" /// /// let wrapped_local: PieWrappedKey = wrapped_local.parse().unwrap(); /// let local_key2 = wrapped_local.unwrap_key(&wrapping_key).unwrap(); /// assert_eq!(local_key, local_key2); /// ``` /// /// # Secret Wrapping /// ``` /// use rusty_paserk::{PieWrappedKey, Key, Local, Secret, V4}; /// /// let wrapping_key = Key::::new_os_random(); /// /// let secret_key = Key::::new_os_random(); /// /// let wrapped_secret = secret_key.wrap_pie(&wrapping_key).to_string(); /// // => "k4.secret-wrap.pie.cTTnZwzBA3AKBugQCzmctv5R9CjyPOlelG9SLZrhupDwk6vYx-3UQFCZ7x4d57KU4K4U1qJeFP6ELzkMJ0s8qHt0hsQkW14Ni6TJ89MRzEqglUgI6hJD-EF2E9kIFO5YuC5MHwXN7Wi_vG1S3L-OoTjZgT_ZJ__8T7SJhvYLodo" /// /// let wrapped_secret: PieWrappedKey = wrapped_secret.parse().unwrap(); /// let secret_key2 = wrapped_secret.unwrap_key(&wrapping_key).unwrap(); /// assert_eq!(secret_key, secret_key2); /// ``` pub fn wrap_pie(&self, wrapping_key: &Key) -> PieWrappedKey { self.wrap_pie_with_rng(wrapping_key, &mut OsRng) } /// Paragon Initiative Enterprises standard key-wrapping. /// /// Using the given RNG source for the IV pub fn wrap_pie_with_rng( &self, wrapping_key: &Key, rng: &mut (impl RngCore + CryptoRng), ) -> PieWrappedKey { // step 1: Enforce Algorithm Lucidity // asserted by the caller. // step 2: Generate a 256 bit (32 bytes) random nonce, n. let mut n = GenericArray::::default(); rng.fill_bytes(&mut n); // step 3: Derive the encryption key `Ek` and XChaCha nonce `n2` let ek = ::new_from_slice(wrapping_key.as_ref()) .unwrap() .chain_update([0x80]) .chain_update(n) .finalize() .into_bytes(); let (ek, n2) = V::split_enc_key(ek); // step 4: Derive the authentication key `Ak` let ak = ::new_from_slice(wrapping_key.as_ref()) .unwrap() .chain_update([0x81]) .chain_update(n) .finalize() .into_bytes(); // step 5: Encrypt the plaintext key `ptk` with `Ek` and `n2` to obtain the wrapped key `c` let mut cipher = ::new(&ek, &n2); let mut c = GenericArray::::default(); cipher.apply_keystream_b2b(self.as_ref(), &mut c).unwrap(); // step 6: Calculate the authentication tag `t` let tag = ::new_from_slice(&ak[..32]) .unwrap() .chain_update(V::KEY_HEADER) .chain_update(K::WRAP_HEADER) .chain_update("pie.") .chain_update(n) .chain_update(&c) .finalize() .into_bytes(); PieWrappedKey { wrapped_key: c, nonce: n, tag: tag.into(), } } } impl PieWrappedKey where K: PieWrapType, V: PieVersion, { /// Paragon Initiative Enterprises standard key-wrapping /// /// # Local Wrapping /// ``` /// use rusty_paserk::{PieWrappedKey, Key, Local, V4}; /// /// let wrapping_key = Key::::new_os_random(); /// /// let local_key = Key::::new_os_random(); /// /// let wrapped_local = local_key.wrap_pie(&wrapping_key).to_string(); /// // => "k4.local-wrap.pie.RcAvOxHI0H-0uMsIl6KGcplH_tDlOhW1omFwXltZCiynHeRNH0hmn28AkN516h3WHuAReH3CvQ2SZ6mevnTquPETSd3XnlcbRWACT5GLWcus3BsD4IFWm9wFZgNF7C_E" /// /// let wrapped_local: PieWrappedKey = wrapped_local.parse().unwrap(); /// let local_key2 = wrapped_local.unwrap_key(&wrapping_key).unwrap(); /// assert_eq!(local_key, local_key2); /// ``` /// /// # Secret Wrapping /// ``` /// use rusty_paserk::{PieWrappedKey, Key, Local, Secret, V4}; /// /// let wrapping_key = Key::::new_os_random(); /// /// let secret_key = Key::::new_os_random(); /// /// let wrapped_secret = secret_key.wrap_pie(&wrapping_key).to_string(); /// // => "k4.secret-wrap.pie.cTTnZwzBA3AKBugQCzmctv5R9CjyPOlelG9SLZrhupDwk6vYx-3UQFCZ7x4d57KU4K4U1qJeFP6ELzkMJ0s8qHt0hsQkW14Ni6TJ89MRzEqglUgI6hJD-EF2E9kIFO5YuC5MHwXN7Wi_vG1S3L-OoTjZgT_ZJ__8T7SJhvYLodo" /// /// let wrapped_secret: PieWrappedKey = wrapped_secret.parse().unwrap(); /// let secret_key2 = wrapped_secret.unwrap_key(&wrapping_key).unwrap(); /// assert_eq!(secret_key, secret_key2); /// ``` pub fn unwrap_key(self, wrapping_key: &Key) -> Result, PasetoError> { let Self { mut wrapped_key, nonce, tag, .. } = self; // step 2: Derive the authentication key `Ak` let ak = ::new_from_slice(wrapping_key.as_ref()) .unwrap() .chain_update([0x81]) .chain_update(nonce) .finalize() .into_bytes(); // step 3: Recalculate the authentication tag t2 let tag2 = ::new_from_slice(&ak[..32]) .unwrap() .chain_update(V::KEY_HEADER) .chain_update(K::WRAP_HEADER) .chain_update("pie.") .chain_update(nonce) .chain_update(&wrapped_key) .finalize() .into_bytes(); // step 4: Compare t with t2 in constant-time. If it doesn't match, abort. if tag.ct_ne(&tag2).into() { return Err(PasetoError::InvalidSignature); } // step 5: Derive the encryption key `Ek` and XChaCha nonce `n2` let ek = ::new_from_slice(wrapping_key.as_ref()) .unwrap() .chain_update([0x80]) .chain_update(nonce) .finalize() .into_bytes(); let (ek, n2) = V::split_enc_key(ek); // step 6: Decrypt the wrapped key `c` with `Ek` and `n2` to obtain the plaintext key `ptk` let mut cipher = ::new(&ek, &n2); cipher.apply_keystream(&mut wrapped_key); // step 7: Enforce Algorithm Lucidity // asserted by type signature // step 8: return ptk Ok(Key { key: wrapped_key }) } } impl> FromStr for PieWrappedKey { type Err = PasetoError; fn from_str(s: &str) -> Result { let s = s .strip_prefix(V::KEY_HEADER) .ok_or(PasetoError::WrongHeader)?; let s = s .strip_prefix(K::WRAP_HEADER) .ok_or(PasetoError::WrongHeader)?; let s = s.strip_prefix("pie.").ok_or(PasetoError::WrongHeader)?; let total = read_b64::(s)?; let (tagiv, wrapped_key) = total.split(); let (tag, nonce) = tagiv.split(); Ok(Self { wrapped_key, nonce, tag, }) } } impl> fmt::Display for PieWrappedKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(V::KEY_HEADER)?; f.write_str(K::WRAP_HEADER)?; f.write_str("pie.")?; let tagiv: K::TagIv = self.tag.concat(self.nonce).into(); let output = tagiv.concat(self.wrapped_key.clone()); write_b64(&output, f) } } /// Version info for configuring PIE Key wrapping pub trait PieVersion: Version { #[doc(hidden)] type Cipher: StreamCipher + KeyIvInit; #[doc(hidden)] type AuthKeyMac: Mac + KeyInit; #[doc(hidden)] type EncKeyMac: Mac + KeyInit; #[doc(hidden)] type TagMac: Mac + KeyInit + OutputSizeUser>::Length>; #[doc(hidden)] type Tag: Concat, Output = Self::TagIv> + From> + DerefMut + Copy; #[doc(hidden)] type TagIv: Split< u8, >::Length, First = Self::Tag, Second = GenericArray, >; #[doc(hidden)] fn split_enc_key( ek: digest::Output, ) -> (cipher::Key, cipher::Iv); } #[cfg(feature = "v3")] impl PieVersion for V3 { type Cipher = ctr::Ctr64BE; type AuthKeyMac = hmac::Hmac; type EncKeyMac = hmac::Hmac; type TagMac = hmac::Hmac; type Tag = digest::Output; type TagIv = GenericArray; fn split_enc_key( ek: digest::Output, ) -> (cipher::Key, cipher::Iv) { ek.split() } } #[cfg(feature = "v4")] impl PieVersion for V4 { type Cipher = chacha20::XChaCha20; type AuthKeyMac = blake2::Blake2bMac; type EncKeyMac = blake2::Blake2bMac; type TagMac = blake2::Blake2bMac; type Tag = digest::Output; type TagIv = GenericArray; fn split_enc_key( ek: digest::Output, ) -> (cipher::Key, cipher::Iv) { ek.split() } } /// Key wrapping type. Can be either `local-wrap.` or `secret-wrap.` pub trait WrapType { /// The header used when wrapping const WRAP_HEADER: &'static str; } impl WrapType for Local { const WRAP_HEADER: &'static str = "local-wrap."; } impl WrapType for Secret { const WRAP_HEADER: &'static str = "secret-wrap."; } /// Helper trait for configuring the key wrapping pub trait PieWrapType: KeyType + WrapType { #[doc(hidden)] type Output: Split< u8, >::Length, First = V::TagIv, Second = GenericArray, > + Default + DerefMut; #[doc(hidden)] type TagIv: From + Concat, Output = Self::Output>; } #[cfg(feature = "v3")] impl PieWrapType for Local { // 32 + 48 + 32 = 112 type Output = GenericArray; type TagIv = ::TagIv; } #[cfg(feature = "v3")] impl PieWrapType for Secret { // 32 + 48 + 48 = 128 type Output = GenericArray; type TagIv = ::TagIv; } #[cfg(feature = "v4")] impl PieWrapType for Local { // 32 + 32 + 32 = 96 type Output = GenericArray; type TagIv = ::TagIv; } #[cfg(feature = "v4")] impl PieWrapType for Secret { // 32 + 32 + 64 = 128 type Output = GenericArray; type TagIv = ::TagIv; } #[cfg(any(test, fuzzing))] pub mod fuzz_tests { use crate::{fuzzing::FakeRng, Key, Local}; use super::{PieVersion, PieWrapType}; #[derive(Debug)] pub struct FuzzInput> { wrapping_key: Key, key: Key, ephemeral: FakeRng<32>, } #[cfg(feature = "arbitrary")] impl<'a, V: PieVersion, K: PieWrapType> arbitrary::Arbitrary<'a> for FuzzInput where Key: arbitrary::Arbitrary<'a>, Key: arbitrary::Arbitrary<'a>, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self { wrapping_key: u.arbitrary()?, key: u.arbitrary()?, ephemeral: u.arbitrary()?, }) } } impl> FuzzInput { pub fn run(mut self) { let mut wrapped = self .key .wrap_pie_with_rng(&self.wrapping_key, &mut self.ephemeral); let s = wrapped.to_string(); wrapped = s.parse().unwrap(); let key = wrapped.unwrap_key(&self.wrapping_key).unwrap(); assert_eq!(self.key, key); } } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl> serde::Serialize for PieWrappedKey { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.collect_str(self) } } #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg(feature = "serde")] impl<'de, V: PieVersion, K: PieWrapType> serde::Deserialize<'de> for PieWrappedKey { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FromStrVisitor(std::marker::PhantomData<(V, K)>); impl<'de, V: PieVersion, K: PieWrapType> serde::de::Visitor<'de> for FromStrVisitor { type Value = PieWrappedKey; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( formatter, "a \"{}{}pie.\" serialized key", V::KEY_HEADER, K::WRAP_HEADER ) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { v.parse().map_err(E::custom) } } deserializer.deserialize_str(FromStrVisitor(std::marker::PhantomData)) } } rusty_paserk-0.4.0/tests/test-vectors/README.md000064400000000000000000000001541046102023000174430ustar 00000000000000# PASERK Test Vectors Canonical source: https://github.com/paseto-standard/test-vectors/tree/master/PASERK rusty_paserk-0.4.0/tests/test-vectors/k3.lid.json000064400000000000000000000016761046102023000201550ustar 00000000000000{ "name": "PASERK k3.lid Test Vectors", "tests": [ { "name": "k3.lid-1", "expect-fail": false, "key": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k3.lid.c2Wpke9KunV6-Tow8dV1wsvVFRkjcTYt_7ZzOtIDRFpM" }, { "name": "k3.lid-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k3.lid.5GB-DfqfPOIMr0-y4IV8323vrjMt3mZMh_R3J3raH38l" }, { "name": "k3.lid-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": "k3.lid.Gd3T6cJNElhwD4gu9JXlaysLNClYmFTD6GRUnXRotCEr" }, { "name": "k3.lid-fail-1", "expect-fail": true, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e", "paserk": null, "comment": "If the key is too short, this must fail to serialize." } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.local-pw.json000064400000000000000000000056131046102023000211160ustar 00000000000000{ "name": "PASERK k3.local-pw Test Vectors", "tests": [ { "name": "k3.local-pw-1", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"iterations": 1000}, "paserk": "k3.local-pw.meWTPJohkeLsaKvlgigDksM935uSCUO3jvjEEHAK28QAAAPoNoLFUMJwo8QHOp5bJpbNzk-ZD_Q6jPtk0XhX4ctVhZnJ3ydru5AuXObwRudmG_RNK3PsJ7kpLSw15Vncc5vmGIkae4DKmBmPI1h3PmOxMGX_hj9DNfu1MIEEm9ukhKQq" }, { "name": "k3.local-pw-2", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"iterations": 10000}, "paserk": "k3.local-pw.BZtl8KfhFR8CCZp6hB0V2yMWttTMpK_U8HiKxnuvMI0AACcQIm4KcJGvG1kfptqCbQxzQUOp72AzgtmhCLVP1mn3orDRJIpoDzRj82dc1cMnANbUsEdcYVG8xzSuCt99zfCjQnQ2rIKbKRM66gafzcSWmD9iMoY3W6KUaN56t0P-ODV2" }, { "name": "k3.local-pw-3", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "password": "correct horse battery staple", "options": {"iterations": 10000}, "paserk": "k3.local-pw.a5cPboLhKgtNb_5C5FXniQuWVgChPXIwM4UKSEAjW3kAACcQzSQgm0Wh87-zAggZvwElhVYI__F7e0nCGL_tVsArrRMpKlkMfZrdi5d7ilpbqogeuiiKcHE9qBu2jTPYywyauZ4VymULdHlGn8fLCBZUGyNzXMbvb0qZS9kecwa4kQzP" }, { "name": "k3.local-pw-fail-1", "expect-fail": true, "comment": "Incorrect password", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c66", "options": {"iterations": 10000}, "paserk": "k3.local-pw.meWTPJohkeLsaKvlgigDksM935uSCUO3jvjEEHAK28QAAAPoNoLFUMJwo8QHOp5bJpbNzk-ZD_Q6jPtk0XhX4ctVhZnJ3ydru5AuXObwRudmG_RNK3PsJ7kpLSw15Vncc5vmGIkae4DKmBmPI1h3PmOxMGX_hj9DNfu1MIEEm9ukhKQq" }, { "name": "k3.local-pw-fail-2", "expect-fail": true, "comment": "Incorrect authentication tag on ciphertext", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"iterations": 10000}, "paserk": "k3.local-pw.meWTPJohkeLsaKvlgigDksM935uSCUO3jvjEEHAK28QAAAPoNoLFUMJwo8QHOp5bJpbNzk-ZD_Q6jPtk0XhX4ctVhZnJ3ydru5AuXObwRudmG_RNK3PsJ7kpLSw15Vncc5vmGIkae4DKmBmPI1h3PmOxMGX_hj9DNfu1MIEEm1vkhLQr" }, { "name": "k3.local-pw-fail-3", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 67108864, "opslimit": 2}, "paserk": "k4.local-pw.cyacmXuslYEP8Xyheh9i-AAAAAAQAAAAAAAAAwAAAAEJh5jS-CAQP9grqo6xhuNMwmjcs6yTAvBjOW2HwZyBrBd0NNs6btknqo-6e-tyXJebU5S5918-es1Y9jhF1dOjMW0gDrsWkPoWT3Vy_poNxjIQHxHOHXaa" } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.local-wrap.pie.json000064400000000000000000000034631046102023000222160ustar 00000000000000{ "name": "PASERK k3.local-wrap.pie Test Vectors", "tests": [ { "name": "k3.local-wrap.pie-1", "expect-fail": false, "unwrapped": "0000000000000000000000000000000000000000000000000000000000000000", "wrapping-key":"707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k3.local-wrap.pie.cLJLT84tUuU-ZLPqKWfhlDw4c2Fhk896z97sK2eM2-HYB3dk_NrHsSS340sJPsBsb7VeFpDBQMzzqRXr4Oylrpzmg-NZC9FVqgaWm1gtEikm-1yvlGRYwstUFLvUF30NrBE3GxYzI63DqJPqfmHSmQ" }, { "name": "k3.local-wrap.pie-2", "expect-fail": false, "unwrapped": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k3.local-wrap.pie.bkbHfW4bBJQ8jcPfLOYxUrg4SkKRHbsywYZwRvxUGFt1je2idZxlFr8sbkB6jTZ6hnrVlI25G2hqZtfdQyFIUcrRAiBrCWNPP1b3afdD9_YxsAXoKEA3X4AZhReuvHCzuPqXNCtrvJtpupGZn-PLFQ" }, { "name": "k3.local-wrap.pie-fail-1", "expect-fail": true, "comment": "Invalid authentication tag on ciphertext", "unwrapped": null, "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k3.local-wrap.pie.bkbHfW4bBJQ8jcPfLOYxUrg4SkKRHbsywYZwRvxUGFt1je2idZxlFr8sbkB6jTZ6hnrVlI25G2hqZtfdQyFIUcrRAiBrCWNPP1b3afdD9_YxsAXoKEA3X4AZhReuvHCzuPqXNCtrvJtpupHZo-RLFQ" }, { "name": "k3.local-wrap.pie-fail-2", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.local-wrap.pie.cy-Mu6zSfhu6q0_XdAM9p1zre_joUWjreSjHgisVNh-oHaNarN4_c7xuSyaHwqEDxF7lTbfNplBGU7wTeUyt__hZyj1J38NdNxVwuXamJY2QhRE-kWYA9_16xTsGwCQX" } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.local.json000064400000000000000000000022731046102023000204710ustar 00000000000000{ "name": "PASERK k3.local Test Vectors", "tests": [ { "name": "k3.local-1", "expect-fail": false, "key": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k3.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "name": "k3.local-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k3.local.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8" }, { "name": "k3.local-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": "k3.local.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjpA" }, { "name": "k3.local-fail-1", "expect-fail": true, "key": null, "paserk": "k3.local.HFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8", "comment": "If the PASERK is too short, this must fail to deserialize." }, { "name": "k3.local-fail-2", "expect-fail": true, "key": null, "paserk": "k4.local.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8", "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.pid.json000064400000000000000000000020371046102023000201510ustar 00000000000000{ "name": "PASERK k3.pid Test Vectors", "tests": [ { "name": "k3.pid-1", "expect-fail": false, "key": "02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "paserk": "k3.pid.mL4lGxNG7cz128frmpn83_76V9C7LmV2sHAMtJ8vIdwG" }, { "name": "k3.pid-2", "expect-fail": false, "key": "02707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "paserk": "k3.pid.gnwg7IkzZyQF9wJgLLT0OpbdMT7BYmdQoG2u-xXpeeHz" }, { "name": "k3.pid-fail-1", "expect-fail": true, "key": "02707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": null, "comment": "Small public keys must fail to serialize" }, { "name": "k3.pid-fail-2", "expect-fail": true, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": null, "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.public.json000064400000000000000000000015611046102023000206540ustar 00000000000000{ "name": "PASERK k3.public Test Vectors", "tests": [ { "name": "k3.public-1", "expect-fail": false, "key": "02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "paserk": "k3.public.AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "name": "k3.public-2", "expect-fail": false, "key": "02707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "paserk": "k3.public.AnBxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2enw" }, { "name": "k3.public-fail-1", "expect-fail": true, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": null, "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.seal.json000064400000000000000000000065531046102023000203300ustar 00000000000000{ "name": "PASERK k3.seal Test Vectors", "tests": [ { "name": "k3.seal-1", "expect-fail": false, "sealing-secret-key": "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDAhUb6WGhABE1MTj0x7E/5acgyap23kh7hUAVoAavKyfhYcmI3n1Q7L\nJpHxNb792H6gBwYFK4EEACKhZANiAAT5H7mTSOyjfILDtSuavZfalI3doM8pRUlb\nTzNyYLqM9iVmajpc0JRXvKuBtGtYi7Yft+eqFr6BuzGrdb4Z1vkvRcI504m0qKiE\nzjhi6u4sNgzW23rrVkRYkb2oE3SJPko=\n-----END EC PRIVATE KEY-----", "sealing-public-key": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+R+5k0jso3yCw7Urmr2X2pSN3aDPKUVJ\nW08zcmC6jPYlZmo6XNCUV7yrgbRrWIu2H7fnqha+gbsxq3W+Gdb5L0XCOdOJtKio\nhM44YuruLDYM1tt661ZEWJG9qBN0iT5K\n-----END PUBLIC KEY-----", "unsealed": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k3.seal.NsI9NFzAouTSs7V5mejAeyBLYcoeNlbb9eY8C2KnkPTsARsPLen9KfMFfgqeI50FAnuRCdcb4HmXPaY3i-ZdBXwfdqSiB_65lmIHosVOJ7chmqqscnBkA7vc3mEAXxM05hSytjBYFxwlUnfFE3Sq3YHUZrOELF7PM87K6FFOMqc6" }, { "name": "k3.seal-2", "expect-fail": false, "sealing-secret-key": "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDAhUb6WGhABE1MTj0x7E/5acgyap23kh7hUAVoAavKyfhYcmI3n1Q7L\nJpHxNb792H6gBwYFK4EEACKhZANiAAT5H7mTSOyjfILDtSuavZfalI3doM8pRUlb\nTzNyYLqM9iVmajpc0JRXvKuBtGtYi7Yft+eqFr6BuzGrdb4Z1vkvRcI504m0qKiE\nzjhi6u4sNgzW23rrVkRYkb2oE3SJPko=\n-----END EC PRIVATE KEY-----", "sealing-public-key": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+R+5k0jso3yCw7Urmr2X2pSN3aDPKUVJ\nW08zcmC6jPYlZmo6XNCUV7yrgbRrWIu2H7fnqha+gbsxq3W+Gdb5L0XCOdOJtKio\nhM44YuruLDYM1tt661ZEWJG9qBN0iT5K\n-----END PUBLIC KEY-----", "unsealed": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "paserk": "k3.seal.qCFR9x-TwGcUQgprulNtvJqy7ZOipwmHQMOXXaJKetgYFsDm1aP3P9ljbCcDFlj0AqWxxuxaIFi59cCHDAysYdL5gzsVUTz-boo5G4V49FGiJu4kGj5pov1RKijsvaN4XQVhui57jUKWMy1fnjC5E6DrYlII2WWBKVMSbsnXuPGI" }, { "name": "k3.seal-fail-1", "expect-fail": true, "comment": "Invalid authentication tag on sealed key.", "sealing-secret-key": "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDAhUb6WGhABE1MTj0x7E/5acgyap23kh7hUAVoAavKyfhYcmI3n1Q7L\nJpHxNb792H6gBwYFK4EEACKhZANiAAT5H7mTSOyjfILDtSuavZfalI3doM8pRUlb\nTzNyYLqM9iVmajpc0JRXvKuBtGtYi7Yft+eqFr6BuzGrdb4Z1vkvRcI504m0qKiE\nzjhi6u4sNgzW23rrVkRYkb2oE3SJPko=\n-----END EC PRIVATE KEY-----", "sealing-public-key": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+R+5k0jso3yCw7Urmr2X2pSN3aDPKUVJ\nW08zcmC6jPYlZmo6XNCUV7yrgbRrWIu2H7fnqha+gbsxq3W+Gdb5L0XCOdOJtKio\nhM44YuruLDYM1tt661ZEWJG9qBN0iT5K\n-----END PUBLIC KEY-----", "unsealed": null, "paserk": "k3.seal.LpevSc3v4VYqlUjEr3OD4LSMaYspcU-VlqI8rpywnFwVKqT1sMJQB_K3GwyszVueA8QJ3KmBUr4ravEb8DsazPuXcWbrnQF4CJmUQSgaTI4YyCb35n-xkx8CDA7ig-m-lhhYKkp_r3Ybcm-s9BKPlPW2VRr682ukbrCTRXFlR9tS" }, { "name": "k3.seal-fail-2", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "sealing-secret-key": "a770cf90f55d8a6dec51190eb640cb25ce31f7e5eb87a00ca9859022e6da9518a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "sealing-public-key": "a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "unsealed": null, "paserk": "k4.seal.3-VOL4pX5b7eV3uMhYHfOhJNN77YyYtd7wYXrH9rRucKNmq0aO-6AWIFU4xOXUCBk0mzBZeWAPAKrvejqixqeRXm-MQXt8yFGHmM1RzpdJw80nabbyDIsNCpBwltU-uj" } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.secret-pw.json000064400000000000000000000125411046102023000213070ustar 00000000000000{ "name": "PASERK k3.secret-pw Test Vectors", "tests": [ { "name": "k3.secret-pw-1", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"iterations": 1000}, "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-pw.kgGHkPhCK_nclk8kpCqJuTPM7BYfGxSwdeXETNnRglIAAAPonBRPeN3eNgUEMZAY9mLaQPXmg6zMls48IjN4429EoIyS2No-EGpKv6eRb_Zh65PdEec1pq0SaMWb434A2eqd4vt4_wcTfdobxiNovASIgyry03qnjx29FmuZ2bIvNzh-YExE_AA7UFDzs6BHom8F9Q" }, { "name": "k3.secret-pw-2", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"iterations": 10000}, "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-pw.LajP_XFziwwUW8t0xppL3ecIgaOzfSEx-5-UQG36jJ8AACcQUeD46ydUwIkMOqkXWFvacyf_eaH1BTMlJsdCy6ZhemmaFMZTclOD9LrOwCVnmhlCDQEePilxQEfvPsRM5cL_yxx1bWL0wjS4GAQABQiCvGyQTi_LGlbMnYuiZfxWgpqNJpAI6jx71m6s3f6wZIg68Q" }, { "name": "k3.secret-pw-3", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "password": "correct horse battery staple", "options": {"iterations": 10000}, "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-pw._67v-WS5Jcez0HggRU2WqRDQewjO2cTKfLwsv2smSd0AACcQC-kvhegUybh2VVNQ4TU68Uqlg9ZOlPzmT36bpDsFc1vF2Lw5Jg8WRRuX1Bg3KT6GN1sUoAW95iuc2OnyIXNpDKXIA6FRPKkpztCVHiWFhEtiIUeGOg8hh11eueUNz4GmfCH91TNz2KtbAvy1TXT5Lw" }, { "name": "k3.secret-pw-fail-1", "expect-fail": true, "comment": "Incorrect password", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c66", "options": {"iterations": 10000}, "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-pw.LajP_XFziwwUW8t0xppL3ecIgaOzfSEx-5-UQG36jJ8AACcQUeD46ydUwIkMOqkXWFvacyf_eaH1BTMlJsdCy6ZhemmaFMZTclOD9LrOwCVnmhlCDQEePilxQEfvPsRM5cL_yxx1bWL0wjS4GAQABQiCvGyQTi_LGlbMnYuiZfxWgpqNJpAI6jx71m6s3f6wZIg68Q" }, { "name": "k3.secret-pw-fail-2", "expect-fail": true, "comment": "Incorrect authentication tag on ciphertext", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"iterations": 10000}, "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-pw.LajP_XFziwwUW8t0xppL3ecIgaOzfSEx-5-UQG36jJ8AACcQUeD46ydUwIkMOqkXWFvacyf_eaH1BTMlJsdCy6ZhemmaFMZTclOD9LrOwCVnmhlCDQEePilxQEfvPsRM5cL_yxx1bWL0wjS4GAQABQiCvGyQTi_LGlbMnYuiZfxWgpqNJpAI6jx71m6s4f6wZIg79R" }, { "name": "k3.secret-pw-fail-3", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.secret-pw.1n54ImYiJUSDhTn7vzBI4QAAAAAQAAAAAAAAAwAAAAFBZythnpR02Zza64_y9DuKHeyZVEP_vZx0Y721aIry1rZc70cR08Jb2rgV4pcqR9in25TvA4pV7L4kT3r-0b-5a8Z7wk35D0zOnPLEJloAHf2XEYGleFReV2-tiV1T79G6OhlATgd-bJbXjRqlEOCsk_-pRdSsCeE" } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.secret-wrap.pie.json000064400000000000000000000064621046102023000224130ustar 00000000000000{ "name": "PASERK k3.secret-wrap.pie Test Vectors", "tests": [ { "name": "k3.secret-wrap.pie-1", "expect-fail": false, "unwrapped": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "wrapping-key":"707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b3445454143494459674145716f664b4972364c4254654f7363636538794374644734644f324b4c703575590a5766644234494a554b6a685641764a647631557062447055586a6879646771334e6866655370596d4c4739646e70692f6b704c634b666a304862306f6d6852380a36646f7845375877754d414b594c484f485836426e58704448587951366735660a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-wrap.pie.hLBw_r4wY1mTy9hu27ibzOnFfW37jc3SER1fu3x2sh0cyE31abqEFzocZRg-KgAGY7syAoMsBZZi7ZlJ4xFreLdCi7cQWpIH3ejUQ5WIRMRDK2lh4X9knh2Bj26XaBOSqtK62ACF-V2utRiI3QVeOOLhH-GWuD8RovVQvA1Vv5Q" }, { "name": "k3.secret-wrap.pie-2", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "wrapping-key": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-wrap.pie._9Npd2vzpHCHdGYDQ5QFMc6Nirlv3ubsPPb-xJnh6cLImLhpirH_PDQWRIkmb95m-fc1XpqwY2QQbUkkvb2-_tznGQBt0Yg2-0Lux2MNxObkxDIfxfz5lD_tToF58BIuTh9_Pny0dy27HmJPnUBgy7JP2Je-h3doq6i0V2Q5hRA" }, { "name": "k3.secret-wrap.pie-fail-1", "expect-fail": true, "comment": "Invalid authentication tag on ciphertext", "unwrapped": null, "wrapping-key": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret-wrap.pie._9Npd2vzpHCHdGYDQ5QFMc6Nirlv3ubsPPb-xJnh6cLImLhpirH_PDQWRIkmb95m-fc1XpqwY2QQbUkkvb2-_tznGQBt0Yg2-0Lux2MNxObkxDIfxfz5lD_tToF58BIuTh9_Pny0dy27HmJPnUBgy7JP2Je-h4doq6i0V2Q5hRB" }, { "name": "k3.secret-wrap.pie-fail-2", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "wrapping-key": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "paserk": "k4.secret-wrap.pie.dYA31PP6a-d1Cyk3xt2Dz8kpGSlbpwkG5UyrLcgRspSvq1RUO1UQicQNE3-eXYUYGhXrG9zAVnR93tize-IPtiFEyO70U3bWEXd0uU7asDJQ19I3V2mf5OPIcKQl-TnY0XXtw5DPqY1yEFEbA9WTiDG0I3z6KTWA2z09NWm0OHQ" } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.secret.json000064400000000000000000000056701046102023000206700ustar 00000000000000{ "name": "PASERK k3.secret Test Vectors", "tests": [ { "name": "k3.secret-1", "expect-fail": false, "key": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b3445454143494459674145716f664b4972364c4254654f7363636538794374644734644f324b4c703575590a5766644234494a554b6a685641764a647631557062447055586a6879646771334e6866655370596d4c4739646e70692f6b704c634b666a304862306f6d6852380a36646f7845375877754d414b594c484f485836426e58704448587951366735660a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB" }, { "name": "k3.secret-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6f" }, { "name": "k3.secret-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9ea0", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414575457544387a72334c5934574a4c3963536e6d52384b475445783449774567540a68636c326c34374e316f334345413551303370477557746a79712f6f774164684b39503968746734555439325468784a6c71485a6432555268665636714d6c420a53657a73733262506c2b366f484363475a7866624441587a34684c594e5752610a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.secret.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6g" }, { "name": "k3.secret-fail-1", "expect-fail": true, "comment": "Short keys must be rejected", "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "public-key": null, "paserk": null }, { "name": "k3.secret-fail-2", "expect-fail": true, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e9060fe37571a5d6e7d30b15154ce4a9fb92c70c870848f4ccdf1626588097f73f7", "secret-key-seed": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "public-key": "60fe37571a5d6e7d30b15154ce4a9fb92c70c870848f4ccdf1626588097f73f7", "paserk": null, "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k3.sid.json000064400000000000000000000050071046102023000201540ustar 00000000000000{ "name": "PASERK k3.sid Test Vectors", "tests": [ { "name": "k3.sid-1", "expect-fail": false, "key": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b3445454143494459674145716f664b4972364c4254654f7363636538794374644734644f324b4c703575590a5766644234494a554b6a685641764a647631557062447055586a6879646771334e6866655370596d4c4739646e70692f6b704c634b666a304862306f6d6852380a36646f7845375877754d414b594c484f485836426e58704448587951366735660a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.sid.DjlX1m4BBFtsnbwzw1zv_x0yRcrZpsvdr_gIxh_hg_Rv" }, { "name": "k3.sid-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414547706b49495a4b624a667668356a33562b5562416c637174424f58376b424e6c0a69344e757946727974362b6d584c516e4c734567714e6333575a6f4d2f55516e476a4c5554574b6b2b7559597a454d537a32336a45726974455541626e624c420a4d5441432b2b7137344b564a47754e6977454b4448556239726658676a66646e0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.sid.mNalRnF8T60OMPdi1TWSMcub-51v3Au2VB1MOqPrw8zG" }, { "name": "k3.sid-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9ea0", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414575457544387a72334c5934574a4c3963536e6d52384b475445783449774567540a68636c326c34374e316f334345413551303370477557746a79712f6f774164684b39503968746734555439325468784a6c71485a6432555268665636714d6c420a53657a73733262506c2b366f484363475a7866624441587a34684c594e5752610a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": "k3.sid.2y01jpWJruAPv3epVJkTtDDvdHLsU3luYV9cvGgsR4C6" }, { "name": "k3.sid-fail-1", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e9060fe37571a5d6e7d30b15154ce4a9fb92c70c870848f4ccdf1626588097f73f7", "seed": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": null } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.lid.json000064400000000000000000000016761046102023000201560ustar 00000000000000{ "name": "PASERK k4.lid Test Vectors", "tests": [ { "name": "k4.lid-1", "expect-fail": false, "key": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k4.lid.bqltbNc4JLUAmc9Xtpok-fBuI0dQN5_m3CD9W_nbh559" }, { "name": "k4.lid-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.lid.iVtYQDjr5gEijCSjJC3fQaJm7nCeQSeaty0Jixy8dbsk" }, { "name": "k4.lid-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": "k4.lid.-v0wjDR1FVxNT2to41Ay1P4_8X6HIxnybX1nZ1a4FCTm" }, { "name": "k4.lid-fail-1", "expect-fail": true, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e", "paserk": null, "comment": "If the key is too short, this must fail to serialize." } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.local-pw.json000064400000000000000000000056141046102023000211200ustar 00000000000000{ "name": "PASERK k4.local-pw Test Vectors", "tests": [ { "name": "k4.local-pw-1", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 67108864, "opslimit": 2}, "paserk": "k4.local-pw.9VvzoqE_i23NOqsP9xoijQAAAAAEAAAAAAAAAgAAAAG_uxDZC-NsYyOW8OUOqISJqgHN8xIfAXiPfmFTfB4GPidUzm4aKzMGJmZtRPeyZCV11MxEJS3VMIRHXxYsfUQsmWLALpFwqUhxZdk_ymFcK2Nk0-N7CVp-" }, { "name": "k4.local-pw-2", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.local-pw.cyacmXuslYEP8Xyheh9i-AAAAAAQAAAAAAAAAwAAAAEJh5jS-CAQP9grqo6xhuNMwmjcs6yTAvBjOW2HwZyBrBd0NNs6btknqo-6e-tyXJebU5S5918-es1Y9jhF1dOjMW0gDrsWkPoWT3Vy_poNxjIQHxHOHXaa" }, { "name": "k4.local-pw-3", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "password": "correct horse battery staple", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.local-pw.1zXpYe7LSsL95YKf92kkJAAAAAAQAAAAAAAAAwAAAAG9WUsB_V2gVnTE1cCAs7RrS9-j22y2rcNixSycUw4_cryOPztPM8vASZw_BYHlQHxXqCl7wBS9NkUnMLa6b8e3ZNeaGiqaGFxRpmOK1Hal4GpHys6lC2Jw" }, { "name": "k4.local-pw-fail-1", "expect-fail": true, "comment": "Incorrect password", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c66", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.local-pw.cyacmXuslYEP8Xyheh9i-AAAAAAQAAAAAAAAAwAAAAEJh5jS-CAQP9grqo6xhuNMwmjcs6yTAvBjOW2HwZyBrBd0NNs6btknqo-6e-tyXJebU5S5918-es1Y9jhF1dOjMW0gDrsWkPoWT3Vy_poNxjIQHxHOHXaa" }, { "name": "k4.local-pw-fail-2", "expect-fail": true, "comment": "Incorrect authentication tag on ciphertext", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.local-pw.cyacmXuslYEP8Xyheh9i-AAAAAAQAAAAAAAAAwAAAAEJh5jS-CAQP9grqo6xhuNMwmjcs6yTAvBjOW2HwZyBrBd0NNs6btknqo-6e-tyXJebU5S5918-es1Y9jhF1dOjMW0gDrsWkPoWT3Vy_poNxjIQHyHOHXbb" }, { "name": "k4.local-pw-fail-3", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"iterations": 10000}, "paserk": "k3.local-pw.BZtl8KfhFR8CCZp6hB0V2yMWttTMpK_U8HiKxnuvMI0AACcQIm4KcJGvG1kfptqCbQxzQUOp72AzgtmhCLVP1mn3orDRJIpoDzRj82dc1cMnANbUsEdcYVG8xzSuCt99zfCjQnQ2rIKbKRM66gafzcSWmD9iMoY3W6KUaN56t0P-ODV2" } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.local-wrap.pie.json000064400000000000000000000034071046102023000222150ustar 00000000000000{ "name": "PASERK k4.local-wrap.pie Test Vectors", "tests": [ { "name": "k4.local-wrap.pie-1", "expect-fail": false, "unwrapped": "0000000000000000000000000000000000000000000000000000000000000000", "wrapping-key":"707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.local-wrap.pie.y-PC8Zh6P1DoOBUdhRr7W8GWSgHtRKvE8PWWYA-qXy3fxJDmaRsxcZVQzuvXHZuBg5MqCgh_y5K0WbukJCrDX73Wdf631VBnE1DNHafbjnGNzFNWP59ba9ifsOAgE7Bw" }, { "name": "k4.local-wrap.pie-2", "expect-fail": false, "unwrapped": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.local-wrap.pie.cy-Mu6zSfhu6q0_XdAM9p1zre_joUWjreSjHgisVNh-oHaNarN4_c7xuSyaHwqEDxF7lTbfNplBGU7wTeUyt__hZyj1J38NdNxVwuXamJY2QhRE-kWYA9_16xTsGwCQX" }, { "name": "k4.local-wrap.pie-fail-1", "expect-fail": true, "comment": "Invalid authentication tag on ciphertext", "unwrapped": null, "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.local-wrap.pie.cy-Mu6zSfhu6q0_XdAM9p1zre_joUWjreSjHgisVNh-oHaNarN4_c7xuSyaHwqEDxF7lTbfNplBGU7wTeUyt__hZyj1J38NdNxVwuXamJY3QhRE-kWYA9_16xUtGwCQY" }, { "name": "k4.local-wrap.pie-fail-2", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k3.local-wrap.pie.bkbHfW4bBJQ8jcPfLOYxUrg4SkKRHbsywYZwRvxUGFt1je2idZxlFr8sbkB6jTZ6hnrVlI25G2hqZtfdQyFIUcrRAiBrCWNPP1b3afdD9_YxsAXoKEA3X4AZhReuvHCzuPqXNCtrvJtpupGZn-PLFQ" } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.local.json000064400000000000000000000022731046102023000204720ustar 00000000000000{ "name": "PASERK k4.local Test Vectors", "tests": [ { "name": "k4.local-1", "expect-fail": false, "key": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "name": "k4.local-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.local.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8" }, { "name": "k4.local-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": "k4.local.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjpA" }, { "name": "k4.local-fail-1", "expect-fail": true, "key": null, "paserk": "k4.local.HFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8", "comment": "If the PASERK is too short, this must fail to deserialize." }, { "name": "k4.local-fail-2", "expect-fail": true, "key": null, "paserk": "k3.local.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8", "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.pid.json000064400000000000000000000023231046102023000201500ustar 00000000000000{ "name": "PASERK k4.pid Test Vectors", "tests": [ { "name": "k4.pid-1", "expect-fail": false, "key": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k4.pid.S_XQmeEwHbbvRmiyfXfHYpLGjXGzjTRSDoT1YtTakWFE" }, { "name": "k4.pid-2", "expect-fail": false, "key": "b7715bd661458d928654d3e832f53ff5c9480542e0e3d4c9b032c768c7ce6023", "paserk": "k4.pid.AGPCOBRB8Pz0CwM8Q_BsUPL48_3f4TlM2sftGv4z93TY" }, { "name": "k4.pid-3", "expect-fail": false, "key": "a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "paserk": "k4.pid.ALkKEkc7gErKCML_CaLMeO81O8nR57b96ou84jObBonE" }, { "name": "k4.pid-fail-1", "expect-fail": true, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e", "paserk": null, "comment": "Small public keys must fail to serialize" }, { "name": "k4.pid-fail-2", "expect-fail": true, "key": "02707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "paserk": null, "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.public.json000064400000000000000000000020001046102023000206420ustar 00000000000000{ "name": "PASERK k4.public Test Vectors", "tests": [ { "name": "k4.public-1", "expect-fail": false, "key": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k4.public.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "name": "k4.public-2", "expect-fail": false, "key": "b7715bd661458d928654d3e832f53ff5c9480542e0e3d4c9b032c768c7ce6023", "paserk": "k4.public.t3Fb1mFFjZKGVNPoMvU_9clIBULg49TJsDLHaMfOYCM" }, { "name": "k4.public-3", "expect-fail": false, "key": "a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "paserk": "k4.public.oPvD3C-ZpTi0D7dhaoPPQna2zyI__1osLTI2I164fcc" }, { "name": "k4.public-fail-1", "expect-fail": true, "key": "02707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", "paserk": null, "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.seal.json000064400000000000000000000045321046102023000203240ustar 00000000000000{ "name": "PASERK k4.seal Test Vectors", "tests": [ { "name": "k4.seal-1", "expect-fail": false, "sealing-secret-key": "407796f4bc4b8184e9fe0c54b336822d34823092ad873d87ba14c3efb9db8c1db7715bd661458d928654d3e832f53ff5c9480542e0e3d4c9b032c768c7ce6023", "sealing-public-key": "b7715bd661458d928654d3e832f53ff5c9480542e0e3d4c9b032c768c7ce6023", "unsealed": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k4.seal.OPFn-AEUsKUWtAUZrutVvd9YaZ4CmV4_lk6ii8N72l5gTnl8RlL_zRFqWTZZV9gSnPzARQ_QklrZ2Qs6cJGKOENNOnsDXL5haXcr-QbTXgoLVBvT4ruJ8MdjWXGRTVc9" }, { "name": "k4.seal-2", "expect-fail": false, "sealing-secret-key": "a770cf90f55d8a6dec51190eb640cb25ce31f7e5eb87a00ca9859022e6da9518a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "sealing-public-key": "a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "unsealed": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "paserk": "k4.seal.3-VOL4pX5b7eV3uMhYHfOhJNN77YyYtd7wYXrH9rRucKNmq0aO-6AWIFU4xOXUCBk0mzBZeWAPAKrvejqixqeRXm-MQXt8yFGHmM1RzpdJw80nabbyDIsNCpBwltU-uj" }, { "name": "k4.seal-fail-1", "expect-fail": true, "comment": "Invalid authentication tag on sealed key.", "sealing-secret-key": "a770cf90f55d8a6dec51190eb640cb25ce31f7e5eb87a00ca9859022e6da9518a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "sealing-public-key": "a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "unsealed": null, "paserk": "k4.seal.3-VOL5pX5b7eV3uMhYHfOhJNN77YyYtd7wYXrH9rRucKNmq0aO-6AWIFU4xOXUCBk0mzBZeWAPAKrvejqixqeRXm-MQXt8yFGHmM1RzpdJw80nabbyDItNCpBwltV-uj" }, { "name": "k4.seal-fail-2", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "sealing-secret-key": "a770cf90f55d8a6dec51190eb640cb25ce31f7e5eb87a00ca9859022e6da9518a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "sealing-public-key": "a0fbc3dc2f99a538b40fb7616a83cf4276b6cf223fff5a2c2d3236235eb87dc7", "unsealed": null, "paserk": "k3.seal.LpevSc3v4VYqlUjEr3OD4LSMaYspcU-VlqI8rpywnFwVKqT1sMJQB_K3GwyszVueA8QJ3KmBUr4ravEb8DsazPuXcWbrnQF4CJmUQSgaTI4YyCb35n-xkx8CDA7ig-m-lhhYKkp_r3Ybcm-s9BKPlPW2VRr791ukbrCSRXFkQ8sR" } ] } rusty_paserk-0.4.0/tests/test-vectors/k4.secret-pw.json000064400000000000000000000071221046102023000213070ustar 00000000000000{ "name": "PASERK k4.secret-pw Test Vectors", "tests": [ { "name": "k4.secret-pw-1", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 67108864, "opslimit": 2}, "paserk": "k4.secret-pw.g5CZn27bLJQkPVOYjrWEQAAAAAAEAAAAAAAAAgAAAAGpohE13nAyCtWfj2Xf3rgORRrE1X0qw2U1FWSJm_6snSbneAqz59FTgsmUR2cNmC41rauCVViAEijox_mY4iJzIUOv34cHkLLIZ_te-FpqKDK0bFtH-rgdFkiy-RjCG0EN349NFFqCZHu7gOlQw98nyeRwWelHCJE" }, { "name": "k4.secret-pw-2", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.secret-pw.1n54ImYiJUSDhTn7vzBI4QAAAAAQAAAAAAAAAwAAAAFBZythnpR02Zza64_y9DuKHeyZVEP_vZx0Y721aIry1rZc70cR08Jb2rgV4pcqR9in25TvA4pV7L4kT3r-0b-5a8Z7wk35D0zOnPLEJloAHf2XEYGleFReV2-tiV1T79G6OhlATgd-bJbXjRqlEOCsk_-pRdSsCeE" }, { "name": "k4.secret-pw-3", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "password": "correct horse battery staple", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.secret-pw.l7bNRB3P5Pwp2FN11-O5dgAAAAAQAAAAAAAAAwAAAAGMe2vxUAZ5quIB-7KCN6_zP_rFqqPmgaVI8ut4xvM2QZQ7QNpr4MlmZ52UulTstEf0uT8vyWzn7bthOALA2ZH_0iBiG9pPWqD8UzmuUjUP5Yi92B_5pUw3DJ0vwNP-GgvjHQObQXLsWPfe_exECoYAWdkI6q-VtSM" }, { "name": "k4.secret-pw-fail-1", "expect-fail": true, "comment": "Incorrect password", "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "password": "636f727265637420686f727365206261747465727920737461706c66", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.secret-pw.1n54ImYiJUSDhTn7vzBI4QAAAAAQAAAAAAAAAwAAAAFBZythnpR02Zza64_y9DuKHeyZVEP_vZx0Y721aIry1rZc70cR08Jb2rgV4pcqR9in25TvA4pV7L4kT3r-0b-5a8Z7wk35D0zOnPLEJloAHf2XEYGleFReV2-tiV1T79G6OhlATgd-bJbXjRqlEOCsk_-pRdSsCeE" }, { "name": "k4.secret-pw-fail-2", "expect-fail": true, "comment": "Incorrect authentication tag on ciphertext", "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k4.secret-pw.1n54ImYiJUSDhTn7vzBI4QAAAAAQAAAAAAAAAwAAAAFBZythnpR02Zza64_y9DuKHeyZVEP_vZx0Y721aIry1rZc70cR08Jb2rgV4pcqR9in25TvA4pV7L4kT3r-0b-5a8Z7wk35D0zOnPLEJloAHf2XEYGleFReV2-tiV1T79G6OhlATgd-bJbXjRqlEOCsk_-qRdStDfF" }, { "name": "k4.secret-pw-fail-3", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "password": "636f727265637420686f727365206261747465727920737461706c65", "options": {"memlimit": 268435456, "opslimit": 3}, "paserk": "k3.secret-pw.LajP_XFziwwUW8t0xppL3ecIgaOzfSEx-5-UQG36jJ8AACcQUeD46ydUwIkMOqkXWFvacyf_eaH1BTMlJsdCy6ZhemmaFMZTclOD9LrOwCVnmhlCDQEePilxQEfvPsRM5cL_yxx1bWL0wjS4GAQABQiCvGyQTi_LGlbMnYuiZfxWgpqNJpAI6jx71m6s3f6wZIg68Q" } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.secret-wrap.pie.json000064400000000000000000000040461046102023000224100ustar 00000000000000{ "name": "PASERK k4.secret-wrap.pie Test Vectors", "tests": [ { "name": "k4.secret-wrap.pie-1", "expect-fail": false, "unwrapped": "00000000000000000000000000000000000000000000000000000000000000003b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29", "wrapping-key":"707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.secret-wrap.pie.NC6xj8t0VuK-0KE7Fy6PAKtbQwEFRyQMe39A0ctrkaIcS1zjVgvYTN6cu1AZM7bU2bz-jzKclAWu3Bln6xhSOsUqcQPi6Kw_LtKXLRCeggiuPnaqWfIT4qacjXtXhFvOvDPye21fbWOPuoNM9VppuTzN0LzYDYgNYCPsbWt2n4c" }, { "name": "k4.secret-wrap.pie-2", "expect-fail": false, "unwrapped": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "wrapping-key": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "paserk": "k4.secret-wrap.pie.dYA31PP6a-d1Cyk3xt2Dz8kpGSlbpwkG5UyrLcgRspSvq1RUO1UQicQNE3-eXYUYGhXrG9zAVnR93tize-IPtiFEyO70U3bWEXd0uU7asDJQ19I3V2mf5OPIcKQl-TnY0XXtw5DPqY1yEFEbA9WTiDG0I3z6KTWA2z09NWm0OHQ" }, { "name": "k4.secret-wrap.pie-fail-1", "expect-fail": true, "comment": "Invalid authentication tag on ciphertext", "unwrapped": null, "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.secret-wrap.pie.dYA31PP6a-d1Cyk3xt2Dz8kpGSlbpwkG5UyrLcgRspSvq1RUO1UQicQNE3-eXYUYGhXrG9zAVnR93tize-IPtiFEyO70U3bWEXd0uU7asDJQ19I3V2mf6OPIcKQl-TnY0XXtw5DPqY1yEFEbA9WTiDG0I4z6KTWA2z08NWx0OIR" }, { "name": "k4.secret-wrap.pie-fail-2", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "unwrapped": null, "wrapping-key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k3.secret-wrap.pie._9Npd2vzpHCHdGYDQ5QFMc6Nirlv3ubsPPb-xJnh6cLImLhpirH_PDQWRIkmb95m-fc1XpqwY2QQbUkkvb2-_tznGQBt0Yg2-0Lux2MNxObkxDIfxfz5lD_tToF58BIuTh9_Pny0dy27HmJPnUBgy7JP2Je-h3doq6i0V2Q5hRA" } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.secret.json000064400000000000000000000050321046102023000206610ustar 00000000000000{ "name": "PASERK k4.secret Test Vectors", "tests": [ { "name": "k4.secret-1", "expect-fail": false, "key": "00000000000000000000000000000000000000000000000000000000000000003b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29", "secret-key-seed": "0000000000000000000000000000000000000000000000000000000000000000", "public-key": "3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29", "paserk": "k4.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ" }, { "name": "k4.secret-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "secret-key-seed": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "public-key": "1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "paserk": "k4.secret.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8c5WpIyC_5kWKhS8VEYSZ05dYfuTF-ZdQFV4D9vLTcNQ" }, { "name": "k4.secret-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e9060fe37571a5d6e7d30b15154ce4a9fb92c70c870848f4ccdf1626588097f73f7", "secret-key-seed": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "public-key": "60fe37571a5d6e7d30b15154ce4a9fb92c70c870848f4ccdf1626588097f73f7", "paserk": "k4.secret.cHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjpBg_jdXGl1ufTCxUVTOSp-5LHDIcISPTM3xYmWICX9z9w" }, { "name": "k4.secret-fail-1", "expect-fail": true, "comment": "Short keys must be rejected", "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e", "public-key": null, "paserk": null }, { "name": "k4.secret-fail-2", "expect-fail": true, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9ea0", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414575457544387a72334c5934574a4c3963536e6d52384b475445783449774567540a68636c326c34374e316f334345413551303370477557746a79712f6f774164684b39503968746734555439325468784a6c71485a6432555268665636714d6c420a53657a73733262506c2b366f484363475a7866624441587a34684c594e5752610a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": null, "comment": "Implementations MUST NOT accept a PASERK of the wrong version." } ] }rusty_paserk-0.4.0/tests/test-vectors/k4.sid.json000064400000000000000000000035431046102023000201600ustar 00000000000000{ "name": "PASERK k4.sid Test Vectors", "tests": [ { "name": "k4.sid-1", "expect-fail": false, "key": "00000000000000000000000000000000000000000000000000000000000000003b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29", "seed": "0000000000000000000000000000000000000000000000000000000000000000", "paserk": "k4.sid.YujQ-NvcGquQ0Q-arRf8iYEcXiSOKg2Vk5az-n1lxiUd" }, { "name": "k4.sid-2", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f1ce56a48c82ff99162a14bc544612674e5d61fb9317e65d4055780fdbcb4dc35", "seed": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", "paserk": "k4.sid.gHYyx8y5YzqKEZeYoMDqUOKejdSnY_AWhYZiSCMjR1V5" }, { "name": "k4.sid-3", "expect-fail": false, "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e9060fe37571a5d6e7d30b15154ce4a9fb92c70c870848f4ccdf1626588097f73f7", "seed": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e90", "paserk": "k4.sid.2_m4h6ZTO3qm_PIpl-eYyAqTbNTgmIPQ85POmUEyZHNd" }, { "name": "k4.sid-fail-1", "expect-fail": true, "comment": "Implementations MUST NOT accept a PASERK of the wrong version.", "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9ea0", "public-key": "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d485977454159484b6f5a497a6a3043415159464b344545414349445967414575457544387a72334c5934574a4c3963536e6d52384b475445783449774567540a68636c326c34374e316f334345413551303370477557746a79712f6f774164684b39503968746734555439325468784a6c71485a6432555268665636714d6c420a53657a73733262506c2b366f484363475a7866624441587a34684c594e5752610a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d", "paserk": null } ] }rusty_paserk-0.4.0/tests/vectors.rs000064400000000000000000000272551046102023000155700ustar 00000000000000use std::{fs, str::FromStr}; use libtest_mimic::{Arguments, Failed, Trial}; use rusty_paserk::{ internal::{PieVersion, PieWrapType, PwVersion, PwWrapType, SealedVersion}, Key, KeyId, KeyType, Local, PasetoError, PieWrappedKey, PlaintextKey, Public, PwWrappedKey, SealedKey, Secret, Version, }; use serde::{de::DeserializeOwned, Deserialize}; fn main() { let args = Arguments::from_args(); let mut tests = vec![]; IdTest::add_all_tests(&mut tests); KeyTest::add_all_tests(&mut tests); PbkwTest::add_all_tests(&mut tests); PkeTest::add_all_tests(&mut tests); PieWrapTest::add_all_tests(&mut tests); libtest_mimic::run(&args, tests).exit(); } fn read_test(v: &str) -> TestFile { let path = format!("tests/test-vectors/{v}"); let file = fs::read_to_string(path).unwrap(); serde_json::from_str(&file).unwrap() } #[derive(Deserialize)] struct TestFile { tests: Vec>, } #[derive(Deserialize)] struct Test { name: String, #[serde(flatten)] test_data: T, } #[derive(Deserialize)] struct IdTest { paserk: Option, key: String, } impl IdTest { fn add_all_tests(tests: &mut Vec) { #[cfg(feature = "v3")] { use rusty_paserk::V3; Self::add_tests::(tests); Self::add_tests::(tests); Self::add_tests::(tests); } #[cfg(feature = "v4")] { use rusty_paserk::V4; Self::add_tests::(tests); Self::add_tests::(tests); Self::add_tests::(tests); } } fn add_tests>(tests: &mut Vec) where Key: NewKey, KeyId: From>, { let test_file: TestFile = read_test(&format!("{}{}json", V::KEY_HEADER, K::ID)); for test in test_file.tests { tests.push(Trial::test(test.name, || test.test_data.test::())); } } fn test>(self) -> Result<(), Failed> where Key: NewKey, KeyId: From>, { if let Some(paserk) = self.paserk { let key = Key::::from_key(&self.key); let kid: KeyId = key.to_id(); let kid2: KeyId = paserk.parse().unwrap(); if kid != kid2 { return Err("decode failed".into()); } if kid.to_string() != paserk { return Err("encode failed".into()); } } Ok(()) } } #[derive(Deserialize)] struct KeyTest { paserk: Option, key: Option, comment: Option, } impl KeyTest { fn add_all_tests(tests: &mut Vec) { #[cfg(feature = "v3")] { use rusty_paserk::V3; Self::add_tests::(tests); Self::add_tests::(tests); Self::add_tests::(tests); } #[cfg(feature = "v4")] { use rusty_paserk::V4; Self::add_tests::(tests); Self::add_tests::(tests); Self::add_tests::(tests); } } fn add_tests>(tests: &mut Vec) where Key: NewKey, { let test_file: TestFile = read_test(&format!("{}{}json", V::KEY_HEADER, K::HEADER)); for test in test_file.tests { tests.push(Trial::test(test.name, || test.test_data.test::())); } } fn test>(self) -> Result<(), Failed> where Key: NewKey, { match (self.key, self.paserk) { (Some(key), Some(paserk)) => { let key2: PlaintextKey = paserk.parse().unwrap(); let key = Key::::from_key(&key); let paserk2 = PlaintextKey(key.clone()).to_string(); if key != key2.0 { return Err("decode failed".into()); } if paserk != paserk2 { return Err("encode failed".into()); } Ok(()) } (None, Some(paserk)) => match PlaintextKey::::from_str(&paserk) { Ok(_) => Err(self.comment.unwrap().into()), Err(_) => Ok(()), }, (Some(_), None) => Ok(()), (None, None) => Ok(()), } } } #[derive(Deserialize)] struct PbkwTest { #[serde(rename = "expect-fail")] expect_fail: bool, paserk: String, comment: Option, unwrapped: Option, password: String, } impl PbkwTest { fn add_all_tests(tests: &mut Vec) { #[cfg(feature = "v3")] { use rusty_paserk::V3; Self::add_tests::(tests); Self::add_tests::(tests); } #[cfg(feature = "v4")] { use rusty_paserk::V4; Self::add_tests::(tests); Self::add_tests::(tests); } } fn add_tests>(tests: &mut Vec) { let test_file: TestFile = read_test(&format!("{}{}json", V::KEY_HEADER, K::WRAP_HEADER)); for test in test_file.tests { tests.push(Trial::test(test.name, || test.test_data.test::())); } } fn test>(self) -> Result<(), Failed> { let wrapped_key: PwWrappedKey = match self.paserk.parse() { Ok(wrapped_key) => wrapped_key, Err(_) if self.expect_fail => return Ok(()), Err(e) => return Err(e.to_string().into()), }; if self.expect_fail { match wrapped_key.unwrap_key(self.password.as_bytes()) { Err(_) => Ok(()), Ok(_) => Err(self.comment.unwrap().into()), } } else { let unwrapped = hex::decode(self.unwrapped.unwrap()).unwrap(); match wrapped_key.unwrap_key(self.password.as_bytes()) { Err(err) => Err(err.to_string().into()), Ok(key) if key.as_ref() != unwrapped => Err("key mismatch".into()), Ok(_) => Ok(()), } } } } #[derive(Deserialize)] struct PkeTest { #[serde(rename = "expect-fail")] expect_fail: bool, paserk: String, // comment: Option, unsealed: Option, #[serde(rename = "sealing-secret-key")] sealing_secret_key: String, } impl PkeTest { fn add_all_tests(tests: &mut Vec) { #[cfg(feature = "v3")] Self::add_tests::(tests); #[cfg(feature = "v4")] Self::add_tests::(tests); } fn add_tests(tests: &mut Vec) where Key: NewKey2, { let test_file: TestFile = read_test(&format!("{}seal.json", V::KEY_HEADER)); for test in test_file.tests { tests.push(Trial::test(test.name, || test.test_data.test::())); } } fn test(self) -> Result<(), Failed> where Key: NewKey2, { let result: Result, PasetoError> = self.paserk.parse(); let sealed_key = match result { Ok(sealed_key) => sealed_key, Err(_) if self.expect_fail => return Ok(()), Err(e) => return Err(e.to_string().into()), }; let ssk = Key::from_key2(&self.sealing_secret_key); let key = match sealed_key.unseal(&ssk) { Ok(key) => key, Err(_) if self.expect_fail => return Ok(()), Err(e) => return Err(e.to_string().into()), }; let unsealed = hex::decode(self.unsealed.unwrap()).unwrap(); if key.as_ref() != unsealed { return Err("unseal failed".into()); } Ok(()) } } #[derive(Deserialize)] struct PieWrapTest { #[serde(rename = "expect-fail")] expect_fail: bool, paserk: String, comment: Option, unwrapped: Option, #[serde(rename = "wrapping-key")] wrapping_key: String, } impl PieWrapTest { fn add_all_tests(tests: &mut Vec) { #[cfg(feature = "v3")] { use rusty_paserk::V3; Self::add_tests::(tests); Self::add_tests::(tests); } #[cfg(feature = "v4")] { use rusty_paserk::V4; Self::add_tests::(tests); Self::add_tests::(tests); } } fn add_tests>(tests: &mut Vec) where Key: NewKey, { let test_file: TestFile = read_test(&format!("{}{}pie.json", V::KEY_HEADER, K::WRAP_HEADER)); for test in test_file.tests { tests.push(Trial::test(test.name, || test.test_data.test::())); } } fn test>(self) -> Result<(), Failed> where Key: NewKey, { let wrapping_key = Key::::from_key(&self.wrapping_key); let wrapped_key: PieWrappedKey = match self.paserk.parse() { Ok(wrapped_key) => wrapped_key, Err(_) if self.expect_fail => return Ok(()), Err(e) => return Err(e.to_string().into()), }; if self.expect_fail { match wrapped_key.unwrap_key(&wrapping_key) { Err(_) => Ok(()), Ok(_) => Err(self.comment.unwrap().into()), } } else { let unwrapped = hex::decode(self.unwrapped.unwrap()).unwrap(); match wrapped_key.unwrap_key(&wrapping_key) { Err(err) => Err(err.to_string().into()), Ok(key) if key.as_ref() != unwrapped => Err("key mismatch".into()), Ok(_) => Ok(()), } } } } trait NewKey { fn from_key(s: &str) -> Self; } trait NewKey2 { fn from_key2(s: &str) -> Self; } #[cfg(feature = "v3")] mod v3_impls { use super::*; use rusty_paserk::V3; impl NewKey for Key { fn from_key(s: &str) -> Self { let b = hex::decode(s).unwrap(); Self::from_bytes(b.try_into().unwrap()) } } impl NewKey for Key { fn from_key(s: &str) -> Self { let b = hex::decode(s).unwrap(); Self::from_bytes(&b).unwrap() } } impl NewKey for Key { fn from_key(s: &str) -> Self { let b = hex::decode(s).unwrap(); Self::from_sec1_bytes(&b).unwrap() } } impl NewKey2 for Key { fn from_key2(s: &str) -> Self { Self::from_sec1_pem(s).unwrap() } } } #[cfg(feature = "v4")] mod v4_impls { use super::*; use rusty_paserk::V4; impl NewKey for Key { fn from_key(s: &str) -> Self { let b = hex::decode(s).unwrap(); Self::from_bytes(b.try_into().unwrap()) } } impl NewKey for Key { fn from_key(s: &str) -> Self { let b = hex::decode(s).unwrap(); Self::from_keypair_bytes(&b).unwrap() } } impl NewKey for Key { fn from_key(s: &str) -> Self { let b = hex::decode(s).unwrap(); Self::from_public_key(&b).unwrap() } } impl NewKey2 for Key { fn from_key2(s: &str) -> Self { let b = hex::decode(s).unwrap(); Self::from_keypair_bytes(&b).unwrap() } } }