diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d96b2..61615db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +### 0.6.0 + +__Date:__ June 20, 2022. + +__Changelog:__ +- PASERK operations are now implemented for `AsymmetricSecretKey` and `AsymmetricSecretKey` instead of `AsymmetricKeyPair` and `AsymmetricKeyPair`, respectively +- All `sign()` operations with public tokens now take only the secret key +- `V2` and `V4` token's `AsymmetricSecretKey<>` are now defined to contain both the Ed25519 secret seed and the public key (see https://github.com/MystenLabs/ed25519-unsafe-libs) +- `TryFrom> for AsymmetricPublicKey<>` is now provided for `V2` and `V4` as well + + ### 0.5.0 __Date:__ June 4, 2022. diff --git a/Cargo.toml b/Cargo.toml index 895bbe3..5324a5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pasetors" -version = "0.5.0" # Update html_root_url in lib.rs along with this. +version = "0.6.0" # Update html_root_url in lib.rs along with this. authors = ["brycx "] edition = "2018" description = "PASETO: Platform-Agnostic Security Tokens (in Rust)" diff --git a/fuzz/fuzz_targets/fuzz_paserk.rs b/fuzz/fuzz_targets/fuzz_paserk.rs index 5135e6e..20b1f9c 100644 --- a/fuzz/fuzz_targets/fuzz_paserk.rs +++ b/fuzz/fuzz_targets/fuzz_paserk.rs @@ -10,7 +10,7 @@ use pasetors::{version2::V2, version3::V3, version4::V4}; fuzz_target!(|data: &[u8]| { let data: String = String::from_utf8_lossy(data).into(); - if let Ok(valid_paserk) = AsymmetricKeyPair::::try_from(data.as_str()) { + if let Ok(valid_paserk) = AsymmetricSecretKey::::try_from(data.as_str()) { let mut buf = String::new(); valid_paserk.fmt(&mut buf).unwrap(); assert_eq!(&data, &buf); @@ -22,7 +22,7 @@ fuzz_target!(|data: &[u8]| { assert_eq!(&data, &buf); let _ = Id::from(&valid_paserk); } - if let Ok(valid_paserk) = AsymmetricKeyPair::::try_from(data.as_str()) { + if let Ok(valid_paserk) = AsymmetricSecretKey::::try_from(data.as_str()) { let mut buf = String::new(); valid_paserk.fmt(&mut buf).unwrap(); assert_eq!(&data, &buf); diff --git a/fuzz/fuzz_targets/fuzz_pasetors.rs b/fuzz/fuzz_targets/fuzz_pasetors.rs index 3124cc9..e689ae4 100644 --- a/fuzz/fuzz_targets/fuzz_pasetors.rs +++ b/fuzz/fuzz_targets/fuzz_pasetors.rs @@ -21,7 +21,7 @@ fn fuzztest_v2(data: &[u8], csprng: &mut ChaCha20Rng) { csprng.fill_bytes(&mut seed_bytes); let seed = Seed::from_slice(&seed_bytes).unwrap(); let keypair: KeyPair = KeyPair::from_seed(seed); - let sk = AsymmetricSecretKey::::from(&keypair.sk[..32]).unwrap(); + let sk = AsymmetricSecretKey::::from(keypair.sk.as_ref()).unwrap(); let pk = AsymmetricPublicKey::::from(keypair.pk.as_ref()).unwrap(); let mut key = [0u8; 32]; csprng.fill_bytes(&mut key); @@ -39,7 +39,7 @@ fn fuzztest_v2(data: &[u8], csprng: &mut ChaCha20Rng) { } let public_token = UntrustedToken::::try_from( - &version2::PublicToken::sign(&sk, &pk, message.as_bytes(), None).unwrap(), + &version2::PublicToken::sign(&sk, message.as_bytes(), None).unwrap(), ) .unwrap(); match version2::PublicToken::verify(&pk, &public_token, None) { @@ -87,7 +87,7 @@ fn fuzztest_v3(data: &[u8]) { } let public_token = UntrustedToken::::try_from( - &version3::PublicToken::sign(&kp.secret, &kp.public, message.as_bytes(), None, None) + &version3::PublicToken::sign(&kp.secret, message.as_bytes(), None, None) .unwrap(), ) .unwrap(); @@ -106,7 +106,7 @@ fn fuzztest_v4(data: &[u8], csprng: &mut ChaCha20Rng) { csprng.fill_bytes(&mut seed_bytes); let seed = Seed::from_slice(&seed_bytes).unwrap(); let keypair: KeyPair = KeyPair::from_seed(seed); - let sk = AsymmetricSecretKey::::from(&keypair.sk[..32]).unwrap(); + let sk = AsymmetricSecretKey::::from(keypair.sk.as_ref()).unwrap(); let pk = AsymmetricPublicKey::::from(keypair.pk.as_ref()).unwrap(); let mut key = [0u8; 32]; csprng.fill_bytes(&mut key); @@ -124,7 +124,7 @@ fn fuzztest_v4(data: &[u8], csprng: &mut ChaCha20Rng) { } let public_token = UntrustedToken::::try_from( - &version4::PublicToken::sign(&sk, &pk, message.as_bytes(), None, None).unwrap(), + &version4::PublicToken::sign(&sk, message.as_bytes(), None, None).unwrap(), ) .unwrap(); match version4::PublicToken::verify(&pk, &public_token, None, None) { @@ -161,7 +161,7 @@ fn fuzz_highlevel(data: &[u8], csprng: &mut ChaCha20Rng) { csprng.fill_bytes(&mut seed_bytes); let seed = Seed::from_slice(&seed_bytes).unwrap(); let keypair: KeyPair = KeyPair::from_seed(seed); - let sk = AsymmetricSecretKey::::from(&keypair.sk[..32]).unwrap(); + let sk = AsymmetricSecretKey::::from(keypair.sk.as_ref()).unwrap(); let pk = AsymmetricPublicKey::::from(keypair.pk.as_ref()).unwrap(); let mut key = [0u8; 32]; csprng.fill_bytes(&mut key); @@ -177,7 +177,7 @@ fn fuzz_highlevel(data: &[u8], csprng: &mut ChaCha20Rng) { let validation_rules = ClaimsValidationRules::new(); let public_token = UntrustedToken::::try_from( - &pasetors::public::sign(&sk, &pk, &claims, None, None).unwrap(), + &pasetors::public::sign(&sk, &claims, None, None).unwrap(), ) .unwrap(); if let Ok(trusted_token) = diff --git a/src/footer.rs b/src/footer.rs index 7326d91..66be4f2 100644 --- a/src/footer.rs +++ b/src/footer.rs @@ -256,7 +256,7 @@ mod tests { let skv4 = SymmetricKey::::generate().unwrap(); let mut buf = String::new(); - kpv2.fmt(&mut buf).unwrap(); + kpv2.secret.fmt(&mut buf).unwrap(); assert!(footer.add_additional("wpk", &buf).is_err()); assert!(footer.add_additional("kid", &buf).is_err()); assert!(footer.add_additional("custom", &buf).is_err()); @@ -280,7 +280,7 @@ mod tests { assert!(footer.add_additional("custom", &buf).is_err()); let mut buf = String::new(); - kpv4.fmt(&mut buf).unwrap(); + kpv4.secret.fmt(&mut buf).unwrap(); assert!(footer.add_additional("wpk", &buf).is_err()); assert!(footer.add_additional("kid", &buf).is_err()); assert!(footer.add_additional("custom", &buf).is_err()); @@ -322,7 +322,7 @@ mod tests { let skv4 = SymmetricKey::::generate().unwrap(); let mut buf = String::new(); - let paserk_id = Id::from(&kpv2); + let paserk_id = Id::from(&kpv2.secret); paserk_id.fmt(&mut buf).unwrap(); assert!(footer.add_additional("kid", &buf).is_err()); assert!(footer.add_additional("custom", &buf).is_ok()); @@ -358,7 +358,7 @@ mod tests { assert_eq!(footer.get_claim("kid").unwrap().as_str().unwrap(), buf); let mut buf = String::new(); - let paserk_id = Id::from(&kpv4); + let paserk_id = Id::from(&kpv4.secret); paserk_id.fmt(&mut buf).unwrap(); assert!(footer.add_additional("kid", &buf).is_err()); assert!(footer.add_additional("custom", &buf).is_ok()); diff --git a/src/keys.rs b/src/keys.rs index 779bd35..25537d5 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -54,6 +54,8 @@ impl PartialEq> for SymmetricKey { } /// An asymmetric secret key used for `.public` tokens, given a version `V`. +/// +/// In case of Ed25519, which is used in V2 and V4, this is the seed concatenated with the public key. pub struct AsymmetricSecretKey { pub(crate) bytes: Vec, pub(crate) phantom: PhantomData, diff --git a/src/lib.rs b/src/lib.rs index 32fd82a..eb66e96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ //! //! // Generate the keys and sign the claims. //! let kp = AsymmetricKeyPair::::generate()?; -//! let pub_token = public::sign(&kp.secret, &kp.public, &claims, None, Some(b"implicit assertion"))?; +//! let pub_token = public::sign(&kp.secret, &claims, None, Some(b"implicit assertion"))?; //! //! // Decide how we want to validate the claims after verifying the token itself. //! // The default verifies the `nbf`, `iat` and `exp` claims. `nbf` and `iat` are always @@ -141,7 +141,7 @@ //! footer.add_additional("custom_footer_claim", "custom_value")?; //! //! let mut claims = Claims::new()?; -//! let pub_token = public::sign(&kp.secret, &kp.public, &claims, Some(&footer), Some(b"implicit assertion"))?; +//! let pub_token = public::sign(&kp.secret, &claims, Some(&footer), Some(b"implicit assertion"))?; //! //! // If we receive a token that needs to be verified, we can still try to parse a Footer from it //! // as long one was used during creation, if we don't know it beforehand. @@ -183,7 +183,7 @@ unused_qualifications, overflowing_literals )] -#![doc(html_root_url = "https://docs.rs/pasetors/0.5.0")] +#![doc(html_root_url = "https://docs.rs/pasetors/0.6.0")] #![cfg_attr(docsrs, feature(doc_cfg))] #[macro_use] @@ -246,7 +246,6 @@ pub mod public { /// Create a public token using the latest PASETO version (v4). pub fn sign( secret_key: &AsymmetricSecretKey, - public_key: &AsymmetricPublicKey, message: &Claims, footer: Option<&Footer>, implicit_assert: Option<&[u8]>, @@ -254,14 +253,12 @@ pub mod public { match footer { Some(f) => crate::version4::PublicToken::sign( secret_key, - public_key, message.to_string()?.as_bytes(), Some(f.to_string()?.as_bytes()), implicit_assert, ), None => crate::version4::PublicToken::sign( secret_key, - public_key, message.to_string()?.as_bytes(), None, implicit_assert, diff --git a/src/paserk.rs b/src/paserk.rs index c99096c..bc7ee60 100644 --- a/src/paserk.rs +++ b/src/paserk.rs @@ -2,7 +2,7 @@ use crate::common::{decode_b64, encode_b64}; use crate::errors::Error; -use crate::keys::{AsymmetricKeyPair, AsymmetricPublicKey, AsymmetricSecretKey, SymmetricKey}; +use crate::keys::{AsymmetricPublicKey, AsymmetricSecretKey, SymmetricKey}; use crate::version::private::Version; use alloc::string::String; use alloc::vec::Vec; @@ -96,34 +96,20 @@ impl TryFrom<&str> for SymmetricKey { } #[cfg(feature = "v2")] -impl FormatAsPaserk for AsymmetricKeyPair { +impl FormatAsPaserk for AsymmetricSecretKey { fn fmt(&self, write: &mut dyn Write) -> core::fmt::Result { write.write_str("k2.secret.")?; - - // See spec: "Here, Ed25519 secret key means the clamped 32-byte seed followed by the - // 32-byte public key, as used in the NaCl and libsodium APIs, rather than just the - // clamped 32-byte seed." - let mut buf = [0u8; V2::SECRET_KEY + V2::PUBLIC_KEY]; - buf[..V2::SECRET_KEY].copy_from_slice(self.secret.as_bytes()); - buf[V2::SECRET_KEY..].copy_from_slice(self.public.as_bytes()); - write.write_str(&encode_b64(buf).map_err(|_| core::fmt::Error)?)?; - buf.iter_mut().zeroize(); - - Ok(()) + write.write_str(&encode_b64(self.as_bytes()).map_err(|_| core::fmt::Error)?) } } #[cfg(feature = "v2")] -impl TryFrom<&str> for AsymmetricKeyPair { +impl TryFrom<&str> for AsymmetricSecretKey { type Error = Error; fn try_from(value: &str) -> Result { - let mut buf = - validate_paserk_string(value, "k2", "secret", V2::SECRET_KEY + V2::PUBLIC_KEY)?; - let ret = Self { - secret: AsymmetricSecretKey::from(&buf[..V2::SECRET_KEY])?, - public: AsymmetricPublicKey::from(&buf[V2::SECRET_KEY..])?, - }; + let mut buf = validate_paserk_string(value, "k2", "secret", V2::SECRET_KEY)?; + let ret = Self::from(&buf)?; buf.iter_mut().zeroize(); Ok(ret) @@ -154,31 +140,20 @@ impl TryFrom<&str> for AsymmetricSecretKey { } #[cfg(feature = "v4")] -impl FormatAsPaserk for AsymmetricKeyPair { +impl FormatAsPaserk for AsymmetricSecretKey { fn fmt(&self, write: &mut dyn Write) -> core::fmt::Result { write.write_str("k4.secret.")?; - - let mut buf = [0u8; V4::SECRET_KEY + V4::PUBLIC_KEY]; - buf[..V4::SECRET_KEY].copy_from_slice(self.secret.as_bytes()); - buf[V4::SECRET_KEY..].copy_from_slice(self.public.as_bytes()); - write.write_str(&encode_b64(buf).map_err(|_| core::fmt::Error)?)?; - buf.iter_mut().zeroize(); - - Ok(()) + write.write_str(&encode_b64(self.as_bytes()).map_err(|_| core::fmt::Error)?) } } #[cfg(feature = "v4")] -impl TryFrom<&str> for AsymmetricKeyPair { +impl TryFrom<&str> for AsymmetricSecretKey { type Error = Error; fn try_from(value: &str) -> Result { - let mut buf = - validate_paserk_string(value, "k4", "secret", V4::SECRET_KEY + V4::PUBLIC_KEY)?; - let ret = Self { - secret: AsymmetricSecretKey::from(&buf[..V4::SECRET_KEY])?, - public: AsymmetricPublicKey::from(&buf[V4::SECRET_KEY..])?, - }; + let mut buf = validate_paserk_string(value, "k4", "secret", V4::SECRET_KEY)?; + let ret = Self::from(&buf)?; buf.iter_mut().zeroize(); Ok(ret) @@ -337,8 +312,8 @@ impl From<&SymmetricKey> for Id { } #[cfg(feature = "v2")] -impl From<&AsymmetricKeyPair> for Id { - fn from(key: &AsymmetricKeyPair) -> Self { +impl From<&AsymmetricSecretKey> for Id { + fn from(key: &AsymmetricSecretKey) -> Self { let header = String::from("k2.sid."); let mut hasher = blake2b::Blake2b::new(33).unwrap(); hasher.update(header.as_bytes()).unwrap(); @@ -354,8 +329,8 @@ impl From<&AsymmetricKeyPair> for Id { } #[cfg(feature = "v4")] -impl From<&AsymmetricKeyPair> for Id { - fn from(key: &AsymmetricKeyPair) -> Self { +impl From<&AsymmetricSecretKey> for Id { + fn from(key: &AsymmetricSecretKey) -> Self { let header = String::from("k4.sid."); let mut hasher = blake2b::Blake2b::new(33).unwrap(); hasher.update(header.as_bytes()).unwrap(); @@ -535,7 +510,7 @@ mod tests { test_id_type!( test_secret_k2_id, - AsymmetricKeyPair, + AsymmetricSecretKey, V2, "./test_vectors/PASERK/k2.sid.json" ); @@ -563,7 +538,7 @@ mod tests { test_paserk_type!( test_secret_k2, - AsymmetricKeyPair, + AsymmetricSecretKey, V2, "./test_vectors/PASERK/k2.secret.json" ); @@ -604,10 +579,10 @@ mod tests { ) .is_err()); - assert!(AsymmetricKeyPair::::try_from("k2.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_ok()); - assert!(AsymmetricKeyPair::::try_from("k4.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); - assert!(AsymmetricKeyPair::::try_from("k2.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); - assert!(AsymmetricKeyPair::::try_from("k4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); + assert!(AsymmetricSecretKey::::try_from("k2.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_ok()); + assert!(AsymmetricSecretKey::::try_from("k4.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); + assert!(AsymmetricSecretKey::::try_from("k2.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); + assert!(AsymmetricSecretKey::::try_from("k4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); } } @@ -696,7 +671,7 @@ mod tests { test_id_type!( test_secret_k4_id, - AsymmetricKeyPair, + AsymmetricSecretKey, V4, "./test_vectors/PASERK/k4.sid.json" ); @@ -724,7 +699,7 @@ mod tests { test_paserk_type!( test_secret_k4, - AsymmetricKeyPair, + AsymmetricSecretKey, V4, "./test_vectors/PASERK/k4.secret.json" ); @@ -765,23 +740,23 @@ mod tests { ) .is_err()); - assert!(AsymmetricKeyPair::::try_from("k4.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_ok()); - assert!(AsymmetricKeyPair::::try_from("k2.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); - assert!(AsymmetricKeyPair::::try_from("k4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); - assert!(AsymmetricKeyPair::::try_from("k2.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); + assert!(AsymmetricSecretKey::::try_from("k4.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_ok()); + assert!(AsymmetricSecretKey::::try_from("k2.secret.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); + assert!(AsymmetricSecretKey::::try_from("k4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); + assert!(AsymmetricSecretKey::::try_from("k2.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7aie8zrakLWKjqNAqbw1zZTIVdx3iQ6Y6wEihi1naKQ").is_err()); } } #[test] #[cfg(all(feature = "v4", feature = "v3"))] fn test_partial_eq_id() { - use crate::keys::Generate; + use crate::keys::{AsymmetricKeyPair, Generate}; let kpv4 = AsymmetricKeyPair::::generate().unwrap(); - assert_eq!(Id::from(&kpv4), Id::from(&kpv4)); - assert_ne!(Id::from(&kpv4), Id::from(&kpv4.public)); + assert_eq!(Id::from(&kpv4.secret), Id::from(&kpv4.secret)); + assert_ne!(Id::from(&kpv4.secret), Id::from(&kpv4.public)); let kpv3 = AsymmetricKeyPair::::generate().unwrap(); - assert_ne!(Id::from(&kpv4), Id::from(&kpv3.secret)); + assert_ne!(Id::from(&kpv4.secret), Id::from(&kpv3.secret)); } #[test] diff --git a/src/version2.rs b/src/version2.rs index c689eac..ad29067 100644 --- a/src/version2.rs +++ b/src/version2.rs @@ -1,5 +1,6 @@ #![cfg_attr(docsrs, doc(cfg(feature = "v2")))] +use core::convert::TryFrom; use core::marker::PhantomData; use crate::common::{encode_b64, validate_footer_untrusted_token}; @@ -12,12 +13,11 @@ use crate::token::{Local, Public, TrustedToken, UntrustedToken}; use crate::version::private::Version; use alloc::string::String; use alloc::vec::Vec; -use ed25519_compact::{KeyPair, PublicKey, Signature}; +use ed25519_compact::{KeyPair, PublicKey, SecretKey as SigningKey, Signature}; use orion::hazardous::aead::xchacha20poly1305::*; use orion::hazardous::mac::blake2b; use orion::hazardous::mac::poly1305::POLY1305_OUTSIZE; use orion::hazardous::stream::xchacha20::XCHACHA_NONCESIZE; -use zeroize::Zeroizing; #[derive(Debug, PartialEq, Clone)] /// Version 2 of the PASETO spec. @@ -25,7 +25,7 @@ pub struct V2; impl Version for V2 { const LOCAL_KEY: usize = 32; - const SECRET_KEY: usize = 32; + const SECRET_KEY: usize = 32 + Self::PUBLIC_KEY; // Seed || PK const PUBLIC_KEY: usize = 32; const PUBLIC_SIG: usize = 64; const LOCAL_NONCE: usize = 24; @@ -42,13 +42,27 @@ impl Version for V2 { } fn validate_secret_key(key_bytes: &[u8]) -> Result<(), Error> { - debug_assert_eq!(Self::LOCAL_KEY, Self::SECRET_KEY); - Self::validate_local_key(key_bytes) + if key_bytes.len() != Self::SECRET_KEY { + return Err(Error::Key); + } + + Ok(()) } fn validate_public_key(key_bytes: &[u8]) -> Result<(), Error> { - debug_assert_eq!(Self::LOCAL_KEY, Self::PUBLIC_KEY); - Self::validate_secret_key(key_bytes) + if key_bytes.len() != Self::PUBLIC_KEY { + return Err(Error::Key); + } + + Ok(()) + } +} + +impl TryFrom<&AsymmetricSecretKey> for AsymmetricPublicKey { + type Error = Error; + + fn try_from(value: &AsymmetricSecretKey) -> Result { + AsymmetricPublicKey::::from(&value.as_bytes()[32..]) } } @@ -56,7 +70,7 @@ impl Generate, V2> for AsymmetricKeyPair { fn generate() -> Result, Error> { let key_pair = KeyPair::generate(); - let secret = AsymmetricSecretKey::::from(&key_pair.sk[..32]) + let secret = AsymmetricSecretKey::::from(key_pair.sk.as_ref()) .map_err(|_| Error::KeyGeneration)?; let public = AsymmetricPublicKey::::from(key_pair.pk.as_ref()) .map_err(|_| Error::KeyGeneration)?; @@ -88,7 +102,6 @@ impl PublicToken { /// Create a public token. pub fn sign( secret_key: &AsymmetricSecretKey, - public_key: &AsymmetricPublicKey, message: &[u8], footer: Option<&[u8]>, ) -> Result { @@ -96,14 +109,10 @@ impl PublicToken { return Err(Error::EmptyPayload); } - let mut raw_key = Zeroizing::new([0u8; 64]); - raw_key.as_mut()[..32].copy_from_slice(secret_key.as_bytes()); - raw_key.as_mut()[32..].copy_from_slice(public_key.as_bytes()); - let kp = KeyPair::from_slice(raw_key.as_ref()).map_err(|_| Error::Key)?; - + let sk = SigningKey::from_slice(secret_key.as_bytes()).map_err(|_| Error::Key)?; let f = footer.unwrap_or(&[]); let m2 = pae::pae(&[Self::HEADER.as_bytes(), message, f])?; - let sig = kp.sk.sign(m2, None); + let sig = sk.sign(m2, None); let mut m_sig: Vec = Vec::from(message); m_sig.extend_from_slice(sig.as_ref()); @@ -326,7 +335,7 @@ mod test_vectors { debug_assert!(test.secret_key.is_some()); let sk = AsymmetricSecretKey::::from( - &hex::decode(test.secret_key.as_ref().unwrap()).unwrap()[..32], + &hex::decode(test.secret_key.as_ref().unwrap()).unwrap(), ) .unwrap(); let pk = AsymmetricPublicKey::::from( @@ -353,7 +362,7 @@ mod test_vectors { let message = test.payload.as_ref().unwrap().as_str().unwrap(); - let actual = PublicToken::sign(&sk, &pk, message.as_bytes(), footer).unwrap(); + let actual = PublicToken::sign(&sk, message.as_bytes(), footer).unwrap(); assert_eq!(actual, test.token, "Failed {:?}", test.name); let ut = UntrustedToken::::try_from(&test.token).unwrap(); @@ -392,9 +401,16 @@ mod test_tokens { use crate::token::UntrustedToken; use core::convert::TryFrom; - const TEST_SK_BYTES: [u8; 32] = [ + const TEST_LOCAL_SK_BYTES: [u8; 32] = [ + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + ]; + + const TEST_SK_BYTES: [u8; 64] = [ 180, 203, 251, 67, 223, 76, 226, 16, 114, 125, 149, 62, 74, 113, 51, 7, 250, 25, 187, 125, - 159, 133, 4, 20, 56, 217, 225, 27, 148, 42, 55, 116, + 159, 133, 4, 20, 56, 217, 225, 27, 148, 42, 55, 116, 30, 185, 219, 187, 188, 4, 124, 3, + 253, 112, 96, 78, 0, 113, 240, 152, 126, 22, 178, 139, 117, 114, 37, 193, 31, 0, 65, 93, + 14, 32, 177, 162, ]; const TEST_PK_BYTES: [u8; 32] = [ @@ -412,7 +428,7 @@ mod test_tokens { fn test_gen_keypair() { let kp = AsymmetricKeyPair::::generate().unwrap(); - let token = PublicToken::sign(&kp.secret, &kp.public, MESSAGE.as_bytes(), None).unwrap(); + let token = PublicToken::sign(&kp.secret, MESSAGE.as_bytes(), None).unwrap(); let ut = UntrustedToken::::try_from(&token).unwrap(); assert!(PublicToken::verify(&kp.public, &ut, None).is_ok()); @@ -434,13 +450,8 @@ mod test_tokens { // Public let kp = AsymmetricKeyPair::::generate().unwrap(); - let token = PublicToken::sign( - &kp.secret, - &kp.public, - MESSAGE.as_bytes(), - Some(FOOTER.as_bytes()), - ) - .unwrap(); + let token = + PublicToken::sign(&kp.secret, MESSAGE.as_bytes(), Some(FOOTER.as_bytes())).unwrap(); let untrusted_token = UntrustedToken::::try_from(token.as_str()).unwrap(); assert!(PublicToken::verify(&kp.public, &untrusted_token, Some(FOOTER.as_bytes())).is_ok()); @@ -463,7 +474,7 @@ mod test_tokens { let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); - let token = PublicToken::sign(&test_sk, &test_pk, MESSAGE.as_bytes(), None).unwrap(); + let token = PublicToken::sign(&test_sk, MESSAGE.as_bytes(), None).unwrap(); let ut = UntrustedToken::::try_from(&token).unwrap(); assert!(PublicToken::verify(&test_pk, &ut, None).is_ok()); @@ -471,7 +482,7 @@ mod test_tokens { #[test] fn footer_logic() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); let message = @@ -479,11 +490,11 @@ mod test_tokens { // We create a token with Some(footer) and with None let actual_some = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, Some(FOOTER.as_bytes())).unwrap(), + &PublicToken::sign(&test_sk, message, Some(FOOTER.as_bytes())).unwrap(), ) .unwrap(); let actual_none = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, None).unwrap(), + &PublicToken::sign(&test_sk, message, None).unwrap(), ) .unwrap(); @@ -518,12 +529,11 @@ mod test_tokens { #[test] // NOTE: See https://github.com/paseto-standard/paseto-spec/issues/17 fn empty_payload() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); - let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); assert_eq!( - PublicToken::sign(&test_sk, &test_pk, b"", None).unwrap_err(), + PublicToken::sign(&test_sk, b"", None).unwrap_err(), Error::EmptyPayload ); assert_eq!( @@ -535,7 +545,7 @@ mod test_tokens { #[test] fn err_on_modified_footer() { let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); assert_eq!( PublicToken::verify( @@ -560,7 +570,7 @@ mod test_tokens { #[test] fn err_on_footer_in_token_none_supplied() { let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); assert_eq!( PublicToken::verify( @@ -585,7 +595,7 @@ mod test_tokens { #[test] fn err_on_no_footer_in_token_some_supplied() { let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let split_public = VALID_PUBLIC_TOKEN.split('.').collect::>(); let invalid_public: String = format!( @@ -644,7 +654,7 @@ mod test_tokens { #[test] fn err_on_modified_tag() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::>(); let mut bad_tag = Vec::from(decode_b64(split_local[2]).unwrap()); @@ -670,7 +680,7 @@ mod test_tokens { #[test] fn err_on_modified_ciphertext() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::>(); let mut bad_ct = Vec::from(decode_b64(split_local[2]).unwrap()); @@ -696,7 +706,7 @@ mod test_tokens { #[test] fn err_on_modified_nonce() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::>(); let mut bad_nonce = Vec::from(decode_b64(split_local[2]).unwrap()); @@ -763,9 +773,9 @@ mod test_keys { #[test] fn test_invalid_sizes() { - assert!(AsymmetricSecretKey::::from(&[0u8; 31]).is_err()); - assert!(AsymmetricSecretKey::::from(&[0u8; 32]).is_ok()); - assert!(AsymmetricSecretKey::::from(&[0u8; 33]).is_err()); + assert!(AsymmetricSecretKey::::from(&[0u8; 63]).is_err()); + assert!(AsymmetricSecretKey::::from(&[0u8; 64]).is_ok()); + assert!(AsymmetricSecretKey::::from(&[0u8; 65]).is_err()); assert!(AsymmetricPublicKey::::from(&[0u8; 31]).is_err()); assert!(AsymmetricPublicKey::::from(&[0u8; 32]).is_ok()); @@ -776,6 +786,15 @@ mod test_keys { assert!(SymmetricKey::::from(&[0u8; 33]).is_err()); } + #[test] + fn try_from_secret_to_public() { + let kpv2 = AsymmetricKeyPair::::generate().unwrap(); + let pubv2 = AsymmetricPublicKey::::try_from(&kpv2.secret).unwrap(); + assert_eq!(pubv2.as_bytes(), kpv2.public.as_bytes()); + assert_eq!(pubv2, kpv2.public); + assert_eq!(&kpv2.secret.as_bytes()[32..], pubv2.as_bytes()); + } + #[test] fn test_trait_impls() { let debug = format!("{:?}", SymmetricKey::::generate().unwrap()); @@ -808,12 +827,4 @@ impl AsymmetricKeyPair { public: AsymmetricPublicKey::from(&bytes[V2::SECRET_KEY..])?, }) } - - pub(crate) fn as_bytes<'a>(&self) -> [u8; 64] { - let mut buf = [0u8; V2::SECRET_KEY + V2::PUBLIC_KEY]; - buf[..V2::SECRET_KEY].copy_from_slice(self.secret.as_bytes()); - buf[V2::SECRET_KEY..].copy_from_slice(self.public.as_bytes()); - - buf - } } diff --git a/src/version3.rs b/src/version3.rs index fa21327..a306f6f 100644 --- a/src/version3.rs +++ b/src/version3.rs @@ -154,10 +154,9 @@ impl PublicToken { /// Create a public token. /// - /// The `secret_key` and `public_key` **must** be in big-endian. + /// The `secret_key` **must** be in big-endian. pub fn sign( secret_key: &AsymmetricSecretKey, - public_key: &AsymmetricPublicKey, message: &[u8], footer: Option<&[u8]>, implicit_assert: Option<&[u8]>, @@ -167,16 +166,11 @@ impl PublicToken { } let signing_key = SigningKey::from_bytes(secret_key.as_bytes()).map_err(|_| Error::Key)?; + let public_key = VerifyingKey::from(&signing_key).to_encoded_point(true); let f = footer.unwrap_or(&[]); let i = implicit_assert.unwrap_or(&[]); - let m2 = pae::pae(&[ - public_key.as_bytes(), - Self::HEADER.as_bytes(), - message, - f, - i, - ])?; + let m2 = pae::pae(&[public_key.as_ref(), Self::HEADER.as_bytes(), message, f, i])?; let mut msg_digest = sha2::Sha384::new(); msg_digest.update(m2); @@ -335,8 +329,7 @@ mod test_vectors { let message = "this is a signed message"; let token = UntrustedToken::::try_from( - &PublicToken::sign(&sk, &pk, message.as_bytes(), Some(b"footer"), Some(b"impl")) - .unwrap(), + &PublicToken::sign(&sk, message.as_bytes(), Some(b"footer"), Some(b"impl")).unwrap(), ) .unwrap(); assert!(PublicToken::verify(&pk, &token, Some(b"footer"), Some(b"impl")).is_ok()); @@ -378,7 +371,7 @@ mod test_vectors { let message = test.payload.as_ref().unwrap().as_str().unwrap(); let actual = - PublicToken::sign(&sk, &pk, message.as_bytes(), footer, Some(implicit_assert)).unwrap(); + PublicToken::sign(&sk, message.as_bytes(), footer, Some(implicit_assert)).unwrap(); assert_eq!(actual, test.token, "Failed {:?}", test.name); let ut = UntrustedToken::::try_from(&test.token).unwrap(); @@ -533,8 +526,7 @@ mod test_tokens { fn test_gen_keypair() { let kp = AsymmetricKeyPair::::generate().unwrap(); - let token = - PublicToken::sign(&kp.secret, &kp.public, MESSAGE.as_bytes(), None, None).unwrap(); + let token = PublicToken::sign(&kp.secret, MESSAGE.as_bytes(), None, None).unwrap(); let ut = UntrustedToken::::try_from(&token).unwrap(); assert!(PublicToken::verify(&kp.public, &ut, None, None).is_ok()); @@ -546,7 +538,6 @@ mod test_tokens { let kp = AsymmetricKeyPair::::generate().unwrap(); let token = PublicToken::sign( &kp.secret, - &kp.public, MESSAGE.as_bytes(), Some(FOOTER.as_bytes()), None, @@ -568,7 +559,7 @@ mod test_tokens { let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); - let token = PublicToken::sign(&test_sk, &test_pk, MESSAGE.as_bytes(), None, None).unwrap(); + let token = PublicToken::sign(&test_sk, MESSAGE.as_bytes(), None, None).unwrap(); let ut = UntrustedToken::::try_from(&token).unwrap(); assert!(PublicToken::verify(&test_pk, &ut, None, None).is_ok()); @@ -583,11 +574,11 @@ mod test_tokens { // We create a token with Some(footer) and with None let actual_some = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, Some(FOOTER.as_bytes()), None).unwrap(), + &PublicToken::sign(&test_sk, message, Some(FOOTER.as_bytes()), None).unwrap(), ) .unwrap(); let actual_none = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, None, None).unwrap(), + &PublicToken::sign(&test_sk, message, None, None).unwrap(), ) .unwrap(); @@ -614,11 +605,11 @@ mod test_tokens { let implicit = b""; let actual_some = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, None, Some(implicit)).unwrap(), + &PublicToken::sign(&test_sk, message, None, Some(implicit)).unwrap(), ) .unwrap(); let actual_none = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, None, None).unwrap(), + &PublicToken::sign(&test_sk, message, None, None).unwrap(), ) .unwrap(); @@ -630,10 +621,9 @@ mod test_tokens { // NOTE: See https://github.com/paseto-standard/paseto-spec/issues/17 fn empty_payload() { let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); - let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); assert_eq!( - PublicToken::sign(&test_sk, &test_pk, b"", None, None).unwrap_err(), + PublicToken::sign(&test_sk, b"", None, None).unwrap_err(), Error::EmptyPayload ); } @@ -788,7 +778,7 @@ mod test_keys { } #[test] - fn try_from_secret_to_public_v3() { + fn try_from_secret_to_public() { let kpv3 = AsymmetricKeyPair::::generate().unwrap(); let pubv3 = AsymmetricPublicKey::::try_from(&kpv3.secret).unwrap(); assert_eq!(pubv3.as_bytes(), kpv3.public.as_bytes()); diff --git a/src/version4.rs b/src/version4.rs index 33667ac..f6d31ab 100644 --- a/src/version4.rs +++ b/src/version4.rs @@ -1,5 +1,6 @@ #![cfg_attr(docsrs, doc(cfg(feature = "v4")))] +use core::convert::TryFrom; use core::marker::PhantomData; use crate::common::{encode_b64, validate_footer_untrusted_token}; @@ -13,13 +14,12 @@ use crate::version::private::Version; use alloc::string::String; use alloc::vec::Vec; use blake2b::SecretKey as AuthKey; -use ed25519_compact::{KeyPair, PublicKey, Signature}; +use ed25519_compact::{KeyPair, PublicKey, SecretKey, Signature}; use orion::hazardous::mac::blake2b; use orion::hazardous::mac::blake2b::Blake2b; use orion::hazardous::stream::xchacha20; use xchacha20::Nonce as EncNonce; use xchacha20::SecretKey as EncKey; -use zeroize::Zeroizing; #[derive(Debug, PartialEq, Clone)] /// Version 4 of the PASETO spec. @@ -27,7 +27,7 @@ pub struct V4; impl Version for V4 { const LOCAL_KEY: usize = 32; - const SECRET_KEY: usize = 32; + const SECRET_KEY: usize = 32 + Self::PUBLIC_KEY; // Seed || PK const PUBLIC_KEY: usize = 32; const PUBLIC_SIG: usize = 64; const LOCAL_NONCE: usize = 32; @@ -44,13 +44,27 @@ impl Version for V4 { } fn validate_secret_key(key_bytes: &[u8]) -> Result<(), Error> { - debug_assert_eq!(Self::LOCAL_KEY, Self::SECRET_KEY); - Self::validate_local_key(key_bytes) + if key_bytes.len() != Self::SECRET_KEY { + return Err(Error::Key); + } + + Ok(()) } fn validate_public_key(key_bytes: &[u8]) -> Result<(), Error> { - debug_assert_eq!(Self::LOCAL_KEY, Self::PUBLIC_KEY); - Self::validate_local_key(key_bytes) + if key_bytes.len() != Self::PUBLIC_KEY { + return Err(Error::Key); + } + + Ok(()) + } +} + +impl TryFrom<&AsymmetricSecretKey> for AsymmetricPublicKey { + type Error = Error; + + fn try_from(value: &AsymmetricSecretKey) -> Result { + AsymmetricPublicKey::::from(&value.as_bytes()[32..]) } } @@ -58,7 +72,7 @@ impl Generate, V4> for AsymmetricKeyPair { fn generate() -> Result, Error> { let key_pair = KeyPair::generate(); - let secret = AsymmetricSecretKey::::from(&key_pair.sk[..32]) + let secret = AsymmetricSecretKey::::from(key_pair.sk.as_ref()) .map_err(|_| Error::KeyGeneration)?; let public = AsymmetricPublicKey::::from(key_pair.pk.as_ref()) .map_err(|_| Error::KeyGeneration)?; @@ -90,7 +104,6 @@ impl PublicToken { /// Create a public token. pub fn sign( secret_key: &AsymmetricSecretKey, - public_key: &AsymmetricPublicKey, message: &[u8], footer: Option<&[u8]>, implicit_assert: Option<&[u8]>, @@ -99,15 +112,12 @@ impl PublicToken { return Err(Error::EmptyPayload); } - let mut raw_key = Zeroizing::new([0u8; 64]); - raw_key.as_mut()[..32].copy_from_slice(secret_key.as_bytes()); - raw_key.as_mut()[32..].copy_from_slice(public_key.as_bytes()); - let kp = KeyPair::from_slice(raw_key.as_ref()).map_err(|_| Error::Key)?; + let sk = SecretKey::from_slice(secret_key.as_bytes()).map_err(|_| Error::Key)?; let f = footer.unwrap_or(&[]); let i = implicit_assert.unwrap_or(&[]); let m2 = pae::pae(&[Self::HEADER.as_bytes(), message, f, i])?; - let sig = kp.sk.sign(m2, None); + let sig = sk.sign(m2, None); let mut m_sig: Vec = Vec::from(message); m_sig.extend_from_slice(sig.as_ref()); @@ -373,7 +383,7 @@ mod test_vectors { debug_assert!(test.secret_key.is_some()); let sk = AsymmetricSecretKey::::from( - &hex::decode(test.secret_key.as_ref().unwrap()).unwrap()[..32], + &hex::decode(test.secret_key.as_ref().unwrap()).unwrap(), ) .unwrap(); let pk = AsymmetricPublicKey::::from( @@ -402,7 +412,7 @@ mod test_vectors { let message = test.payload.as_ref().unwrap().as_str().unwrap(); let actual = - PublicToken::sign(&sk, &pk, message.as_bytes(), footer, Some(implicit_assert)).unwrap(); + PublicToken::sign(&sk, message.as_bytes(), footer, Some(implicit_assert)).unwrap(); assert_eq!(actual, test.token, "Failed {:?}", test.name); let ut = UntrustedToken::::try_from(&test.token).unwrap(); @@ -441,16 +451,16 @@ mod test_tokens { use crate::token::UntrustedToken; use core::convert::TryFrom; - // In version 2 tests, the SK used for public tokens is valid for the local as well. - // Not the case with version 4. const TEST_LOCAL_SK_BYTES: [u8; 32] = [ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, ]; - const TEST_SK_BYTES: [u8; 32] = [ + const TEST_SK_BYTES: [u8; 64] = [ 180, 203, 251, 67, 223, 76, 226, 16, 114, 125, 149, 62, 74, 113, 51, 7, 250, 25, 187, 125, - 159, 133, 4, 20, 56, 217, 225, 27, 148, 42, 55, 116, + 159, 133, 4, 20, 56, 217, 225, 27, 148, 42, 55, 116, 30, 185, 219, 187, 188, 4, 124, 3, + 253, 112, 96, 78, 0, 113, 240, 152, 126, 22, 178, 139, 117, 114, 37, 193, 31, 0, 65, 93, + 14, 32, 177, 162, ]; const TEST_PK_BYTES: [u8; 32] = [ @@ -468,8 +478,7 @@ mod test_tokens { fn test_gen_keypair() { let kp = AsymmetricKeyPair::::generate().unwrap(); - let token = - PublicToken::sign(&kp.secret, &kp.public, MESSAGE.as_bytes(), None, None).unwrap(); + let token = PublicToken::sign(&kp.secret, MESSAGE.as_bytes(), None, None).unwrap(); let ut = UntrustedToken::::try_from(&token).unwrap(); assert!(PublicToken::verify(&kp.public, &ut, None, None).is_ok()); @@ -495,7 +504,6 @@ mod test_tokens { let kp = AsymmetricKeyPair::::generate().unwrap(); let token = PublicToken::sign( &kp.secret, - &kp.public, MESSAGE.as_bytes(), Some(FOOTER.as_bytes()), None, @@ -526,7 +534,7 @@ mod test_tokens { let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); - let token = PublicToken::sign(&test_sk, &test_pk, MESSAGE.as_bytes(), None, None).unwrap(); + let token = PublicToken::sign(&test_sk, MESSAGE.as_bytes(), None, None).unwrap(); let ut = UntrustedToken::::try_from(&token).unwrap(); assert!(PublicToken::verify(&test_pk, &ut, None, None).is_ok()); @@ -534,7 +542,7 @@ mod test_tokens { #[test] fn footer_logic() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); let message = @@ -542,11 +550,11 @@ mod test_tokens { // We create a token with Some(footer) and with None let actual_some = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, Some(FOOTER.as_bytes()), None).unwrap(), + &PublicToken::sign(&test_sk, message, Some(FOOTER.as_bytes()), None).unwrap(), ) .unwrap(); let actual_none = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, None, None).unwrap(), + &PublicToken::sign(&test_sk, message, None, None).unwrap(), ) .unwrap(); @@ -586,7 +594,7 @@ mod test_tokens { #[test] fn implicit_none_some_empty_is_same() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); let message = @@ -594,11 +602,11 @@ mod test_tokens { let implicit = b""; let actual_some = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, None, Some(implicit)).unwrap(), + &PublicToken::sign(&test_sk, message, None, Some(implicit)).unwrap(), ) .unwrap(); let actual_none = UntrustedToken::::try_from( - &PublicToken::sign(&test_sk, &test_pk, message, None, None).unwrap(), + &PublicToken::sign(&test_sk, message, None, None).unwrap(), ) .unwrap(); assert_eq!(actual_some, actual_none); @@ -623,12 +631,11 @@ mod test_tokens { #[test] // NOTE: See https://github.com/paseto-standard/paseto-spec/issues/17 fn empty_payload() { - let test_local_sk = SymmetricKey::::from(&TEST_SK_BYTES).unwrap(); + let test_local_sk = SymmetricKey::::from(&TEST_LOCAL_SK_BYTES).unwrap(); let test_sk = AsymmetricSecretKey::::from(&TEST_SK_BYTES).unwrap(); - let test_pk = AsymmetricPublicKey::::from(&TEST_PK_BYTES).unwrap(); assert_eq!( - PublicToken::sign(&test_sk, &test_pk, b"", None, None).unwrap_err(), + PublicToken::sign(&test_sk, b"", None, None).unwrap_err(), Error::EmptyPayload ); assert_eq!( @@ -920,9 +927,9 @@ mod test_keys { #[test] fn test_invalid_sizes() { - assert!(AsymmetricSecretKey::::from(&[0u8; 31]).is_err()); - assert!(AsymmetricSecretKey::::from(&[0u8; 32]).is_ok()); - assert!(AsymmetricSecretKey::::from(&[0u8; 33]).is_err()); + assert!(AsymmetricSecretKey::::from(&[0u8; 63]).is_err()); + assert!(AsymmetricSecretKey::::from(&[0u8; 64]).is_ok()); + assert!(AsymmetricSecretKey::::from(&[0u8; 65]).is_err()); assert!(AsymmetricPublicKey::::from(&[0u8; 31]).is_err()); assert!(AsymmetricPublicKey::::from(&[0u8; 32]).is_ok()); @@ -933,6 +940,15 @@ mod test_keys { assert!(SymmetricKey::::from(&[0u8; 33]).is_err()); } + #[test] + fn try_from_secret_to_public() { + let kpv4 = AsymmetricKeyPair::::generate().unwrap(); + let pubv4 = AsymmetricPublicKey::::try_from(&kpv4.secret).unwrap(); + assert_eq!(pubv4.as_bytes(), kpv4.public.as_bytes()); + assert_eq!(pubv4, kpv4.public); + assert_eq!(&kpv4.secret.as_bytes()[32..], pubv4.as_bytes()); + } + #[test] fn test_trait_impls() { let debug = format!("{:?}", SymmetricKey::::generate().unwrap()); @@ -965,12 +981,4 @@ impl AsymmetricKeyPair { public: AsymmetricPublicKey::from(&bytes[V4::SECRET_KEY..])?, }) } - - pub(crate) fn as_bytes<'a>(&self) -> [u8; 64] { - let mut buf = [0u8; V4::SECRET_KEY + V4::PUBLIC_KEY]; - buf[..V4::SECRET_KEY].copy_from_slice(self.secret.as_bytes()); - buf[V4::SECRET_KEY..].copy_from_slice(self.public.as_bytes()); - - buf - } }