diff --git a/Cargo.lock b/Cargo.lock index 88b6da0..f84dc8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1532,6 +1532,7 @@ dependencies = [ "argon2", "rand", "serde", + "serde_json", "thiserror", "zeroize", ] @@ -1604,11 +1605,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.119" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] diff --git a/crates/seed-keeper-core/Cargo.toml b/crates/seed-keeper-core/Cargo.toml index 6bf92dc..6770a03 100644 --- a/crates/seed-keeper-core/Cargo.toml +++ b/crates/seed-keeper-core/Cargo.toml @@ -19,5 +19,8 @@ rand = "0.8.5" thiserror = "1.0.51" serde = { version = "1.0", features = ["derive"], optional = true } +[dev-dependencies] +serde_json = "1.0.128" + [features] default = ["serde", "argon2/std"] diff --git a/crates/seed-keeper-core/src/credentials.rs b/crates/seed-keeper-core/src/credentials.rs index a06d3c8..42c4622 100644 --- a/crates/seed-keeper-core/src/credentials.rs +++ b/crates/seed-keeper-core/src/credentials.rs @@ -1,6 +1,6 @@ use std::{marker::PhantomData, ops::Deref}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; use crate::{ @@ -32,13 +32,31 @@ impl Credentials { } } -#[derive(Serialize, Deserialize, Default, Debug, Zeroize, ZeroizeOnDrop)] +#[derive(Default, Debug, Zeroize, ZeroizeOnDrop)] pub struct MinString { #[zeroize] value: String, _marker: PhantomData<()>, } +impl Serialize for MinString { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.value) + } +} +impl<'de, const N: usize> Deserialize<'de> for MinString { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + MinString::new(&value).map_err(serde::de::Error::custom) + } +} + impl MinString { pub fn new(value: &str) -> Result { if value.len() >= N { @@ -149,4 +167,34 @@ mod tests { Ok(()) } + + #[test] + fn test_json_credentials_roundtrip() -> Result<(), error::Error> { + let credentials = Credentials { + username: MinString::new("username")?, + password: MinString::new("password")?, + encrypted_seed: None, + }; + + let json = serde_json::to_string(&credentials).map_err(|e| e.to_string())?; + + let credentials: Credentials = serde_json::from_str(&json).map_err(|e| e.to_string())?; + + assert_eq!(credentials.username.value(), "username"); + assert_eq!(credentials.password.value(), "password"); + + Ok(()) + } + + #[test] + fn it_fails_too_short() -> Result<(), String> { + let json = r#"{"username":"user","password":"pass","encrypted_seed":null}"#; + + // it should deserialize the json + let credentialss: Result = serde_json::from_str(json); + + assert!(credentialss.is_err()); + + Ok(()) + } }