diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b8cfdb..1873790 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,8 +62,6 @@ jobs: override: true - uses: actions-rs/tarpaulin@v0.1 - with: - args: '--all-features' - uses: codecov/codecov-action@v1 with: diff --git a/.tarpaulin.toml b/.tarpaulin.toml new file mode 100644 index 0000000..cf8887d --- /dev/null +++ b/.tarpaulin.toml @@ -0,0 +1,9 @@ +[default_coverage] +exclude-files = ["target"] +all-features = true +locked = true +target-dir = "target/tarpaulin" + +[report] +out = ["Html", "Xml"] +output-dir = "target/tarpaulin" diff --git a/Cargo.toml b/Cargo.toml index 0cf5659..14077ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonwebkey" -version = "0.1.0" +version = "0.2.0" authors = ["Nick Hynes "] description = "JSON Web Key (JWK) (de)serialization, generation, and conversion." readme = "README.md" diff --git a/README.md b/README.md index 2d4c2a4..7ab01e5 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,6 @@ println!("{:#?}", the_jwk); // looks like `jwt_str` but with reordered fields. ### Using with other crates -*Note:* The following example requires the `jwt-convert` feature. - ```rust #[cfg(all(feature = "generate", feature = "jwt-convert"))] { extern crate jsonwebtoken as jwt; diff --git a/src/key_ops.rs b/src/key_ops.rs index 2ab2d2e..f2521f0 100644 --- a/src/key_ops.rs +++ b/src/key_ops.rs @@ -54,3 +54,21 @@ impl_key_ops!( (deriveKey, DERIVE_KEY, 0b01000000), (deriveBits, DERIVE_BITS, 0b10000000), ); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn deserialize_invalid() { + let result: Result = serde_json::from_str(r#"["unknown"]"#); + assert!(result.is_err()); + } + + #[test] + fn serialize() { + let ops = KeyOps::SIGN | KeyOps::DERIVE_BITS; + let json = serde_json::to_string(&ops).unwrap(); + assert_eq!(json, r#"["sign","deriveBits"]"#) + } +} diff --git a/src/lib.rs b/src/lib.rs index 3a3bd7c..e1dbf29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,14 +193,9 @@ impl Key { } } - /// Returns true iff this key only contains non-private components. - pub fn is_public(&self) -> bool { - !self.is_private() - } - /// Returns the public part of this key (symmetric keys have no public parts). pub fn to_public(&self) -> Option> { - if self.is_public() { + if !self.is_private() { return Some(Cow::Borrowed(self)); } Some(Cow::Owned(match self { @@ -366,7 +361,7 @@ impl Key { #[cfg(feature = "generate")] pub fn generate_symmetric(num_bits: usize) -> Self { use rand::RngCore; - let mut bytes = Vec::with_capacity(num_bits / 8); + let mut bytes = vec![0; num_bits / 8]; rand::thread_rng().fill_bytes(&mut bytes); Self::Symmetric { key: bytes.into() } } @@ -506,7 +501,7 @@ const _IMPL_JWT_CONVERSIONS: () = { impl Key { /// Returns an `EncodingKey` if the key is private. pub fn try_to_encoding_key(&self) -> Result { - if self.is_public() { + if !self.is_private() { return Err(ConversionError::NotPrivate); } Ok(match self { diff --git a/src/tests.rs b/src/tests.rs index bd7a630..acf850e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -28,6 +28,12 @@ static RSA_JWK_FIXTURE: &str = r#"{ "n": "pCzbcd9kjvg5rfGHdEMWnXo49zbB6FLQ-m0B0BvVp0aojVWYa0xujC-ZP7ZhxByPxyc2PazwFJJi9ivZ_ggRww" }"#; +#[cfg(feature = "pkcs-convert")] +static OCT_FIXTURE: &str = r#"{ + "kty": "oct", + "k": "TdSBZdXL5n39JXlQc7QL3w" + }"#; + #[test] fn deserialize_es256() { let jwk = JsonWebKey::from_str(P256_JWK_FIXTURE).unwrap(); @@ -347,13 +353,66 @@ J2lmylxUG0M= #[test] fn rsa_public_to_pem() { let jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); - #[rustfmt::skip] assert_eq!( jwk.key.to_public().unwrap().to_pem(), -"-----BEGIN PUBLIC KEY----- + "-----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKQs23HfZI74Oa3xh3RDFp16OPc2wehS 0PptAdAb1adGqI1VmGtMbowvmT+2YcQcj8cnNj2s8BSSYvYr2f4IEcMCAwEAAQ== -----END PUBLIC KEY----- " ); } + +#[cfg(feature = "pkcs-convert")] +#[test] +fn oct_to_pem() { + let jwk = JsonWebKey::from_str(OCT_FIXTURE).unwrap(); + assert!(jwk.key.try_to_pem().is_err()); +} + +#[cfg(feature = "pkcs-convert")] +#[test] +fn oct_to_public() { + let jwk = JsonWebKey::from_str(OCT_FIXTURE).unwrap(); + assert!(jwk.key.to_public().is_none()); +} + +#[cfg(feature = "generate")] +#[test] +fn generate_oct() { + let bits = 56; + match Key::generate_symmetric(bits) { + Key::Symmetric { key } if key.len() == 56 / 8 => {} + k => panic!("`generate_symmetric` generated {:?}", k), + } +} + +#[test] +fn ec_is_private() { + let private_jwk = JsonWebKey::from_str(P256_JWK_FIXTURE).unwrap(); + assert!(private_jwk.key.is_private()); + assert!(!private_jwk.key.to_public().unwrap().is_private()); + let mut k: serde_json::Map = + serde_json::from_str(P256_JWK_FIXTURE).unwrap(); + k.remove("d"); + let public_jwk = JsonWebKey::from_str(&serde_json::to_string(&k).unwrap()).unwrap(); + assert!(!public_jwk.key.is_private()); + assert!(!public_jwk.key.to_public().unwrap().is_private()); +} + +#[test] +fn rsa_is_private() { + let private_jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); + assert!(private_jwk.key.is_private()); + assert!(!private_jwk.key.to_public().unwrap().is_private()); + + static PUBLIC_RSA_JWK_FIXTURE: &str = r#"{ + "kty": "RSA", + "e": "AQAB", + "n": "pCzbcd9kjvg5rfGHdEMWnXo49zbB6FLQ-m0B0BvVp0aojVWYa0xujC-ZP7ZhxByPxyc2PazwFJJi9ivZ_ggRww" + }"#; + + let public_jwk = JsonWebKey::from_str(PUBLIC_RSA_JWK_FIXTURE).unwrap(); + assert!(!public_jwk.key.is_private()); + assert!(!public_jwk.key.to_public().unwrap().is_private()); +}