diff --git a/src/lib.rs b/src/lib.rs index 0243844..db07517 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,17 +18,17 @@ mod crypto; pub mod errors; mod header; mod keys; +mod pem_decoder; mod serialization; mod validation; -mod pem_decoder; pub use algorithms::Algorithm; pub use crypto::{sign, verify}; pub use header::Header; pub use keys::Key; +pub use pem_decoder::PemEncodedKey; pub use serialization::TokenData; pub use validation::Validation; -pub use pem_decoder::PemEncodedKey; use serde::de::DeserializeOwned; use serde::ser::Serialize; @@ -172,21 +172,21 @@ pub fn decode_header(token: &str) -> Result
{ /// /// This must be a tagged PEM encoded key, tags start with `-----BEGIN ..-----` /// and end with a `-----END ..-----` -/// +/// /// ```rust /// use jsonwebtoken::{decode_pem, sign, verify, Algorithm}; -/// +/// /// let pem_content = "-----BEGIN PRIVATE KEY----- /// MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWTFfCGljY6aw3Hrt /// kHmPRiazukxPLb6ilpRAewjW8nihRANCAATDskChT+Altkm9X7MI69T3IUmrQU0L /// 950IxEzvw/x5BMEINRMrXLBJhqzO9Bm+d6JbqA21YQmd1Kt4RzLJR1W+ /// -----END PRIVATE KEY-----"; -/// +/// /// // First use decode_pem from jsonwebtoken /// let privkey_pem = decode_pem(pem_content).unwrap(); /// // If it decodes Ok, then you can start using it with a given algorithm /// let privkey = privkey_pem.as_key().unwrap(); -/// +/// /// // When using the as_key function, you do not need to wrap in Key::Der or Key::Pkcs8 /// // The same code can be used for public keys too. /// let encrypted = sign("hello world", privkey, Algorithm::ES256).unwrap(); diff --git a/src/pem_decoder.rs b/src/pem_decoder.rs index e525eb9..3ccaa6d 100644 --- a/src/pem_decoder.rs +++ b/src/pem_decoder.rs @@ -1,30 +1,30 @@ +use crate::errors::{ErrorKind, Result}; use crate::keys::Key; -use crate::errors::{Result, ErrorKind}; extern crate pem; extern crate simple_asn1; -use simple_asn1::{OID, BigUint}; +use simple_asn1::{BigUint, OID}; /// Supported PEM files for EC and RSA Public and Private Keys #[derive(Debug, PartialEq)] enum PemType { - ECPublicKey, - ECPrivateKey, - RSAPublicKey, - RSAPrivateKey, + ECPublicKey, + ECPrivateKey, + RSAPublicKey, + RSAPrivateKey, } #[derive(Debug, PartialEq)] enum PemEncodedWith { - PKCS1, - PKCS8, + PKCS1, + PKCS8, } #[derive(Debug, PartialEq)] enum Classification { - EC, - RSA, + EC, + RSA, } /// The return type of a successful PEM encoded key with `decode_pem` @@ -41,106 +41,101 @@ enum Classification { /// Documentation about these formats is at /// PKCS#1: https://tools.ietf.org/html/rfc8017 /// PKCS#8: https://tools.ietf.org/html/rfc5958 +#[derive(Debug)] pub struct PemEncodedKey { - content: Vec, - asn1: Vec, - pem_type: PemType, - encoded_with: PemEncodedWith, + content: Vec, + asn1: Vec, + pem_type: PemType, + encoded_with: PemEncodedWith, } impl PemEncodedKey { - /// Read the PEM file for later key use - pub fn read(input: &str) -> Result { - match pem::parse(input) { - Ok(content) => { - let pem_contents = content.contents; - let asn1_content = match simple_asn1::from_der(pem_contents.as_slice()) { - Ok(asn1) => asn1, - Err(_) => return Err(ErrorKind::InvalidKeyFormat)?, - }; + /// Read the PEM file for later key use + pub fn read(input: &str) -> Result { + match pem::parse(input) { + Ok(content) => { + let pem_contents = content.contents; + let asn1_content = match simple_asn1::from_der(pem_contents.as_slice()) { + Ok(asn1) => asn1, + Err(_) => return Err(ErrorKind::InvalidKeyFormat)?, + }; - match content.tag.as_ref() { - // This handles a PKCS#1 RSA Private key - "RSA PRIVATE KEY" => Ok(PemEncodedKey { - content: pem_contents, - asn1: asn1_content, - pem_type: PemType::RSAPrivateKey, - encoded_with: PemEncodedWith::PKCS1, - }), - "RSA PUBLIC KEY" => Ok(PemEncodedKey { - content: pem_contents, - asn1: asn1_content, - pem_type: PemType::RSAPublicKey, - encoded_with: PemEncodedWith::PKCS1, - }), + match content.tag.as_ref() { + // This handles a PKCS#1 RSA Private key + "RSA PRIVATE KEY" => Ok(PemEncodedKey { + content: pem_contents, + asn1: asn1_content, + pem_type: PemType::RSAPrivateKey, + encoded_with: PemEncodedWith::PKCS1, + }), + "RSA PUBLIC KEY" => Ok(PemEncodedKey { + content: pem_contents, + asn1: asn1_content, + pem_type: PemType::RSAPublicKey, + encoded_with: PemEncodedWith::PKCS1, + }), - // No "EC PRIVATE KEY" - // https://security.stackexchange.com/questions/84327/converting-ecc-private-key-to-pkcs1-format - // "there is no such thing as a "PKCS#1 format" for elliptic curve (EC) keys" + // No "EC PRIVATE KEY" + // https://security.stackexchange.com/questions/84327/converting-ecc-private-key-to-pkcs1-format + // "there is no such thing as a "PKCS#1 format" for elliptic curve (EC) keys" - // This handles PKCS#8 private keys - "PRIVATE KEY" => { - match classify_pem(&asn1_content) { - Some(Classification::EC) => Ok(PemEncodedKey { - content: pem_contents, - asn1: asn1_content, - pem_type: PemType::ECPrivateKey, - encoded_with: PemEncodedWith::PKCS8, - }), - Some(Classification::RSA) => Ok(PemEncodedKey { - content: pem_contents, - asn1: asn1_content, - pem_type: PemType::RSAPrivateKey, - encoded_with: PemEncodedWith::PKCS8, - }), - _ => return Err(ErrorKind::InvalidKeyFormat)?, + // This handles PKCS#8 private keys + "PRIVATE KEY" => match classify_pem(&asn1_content) { + Some(Classification::EC) => Ok(PemEncodedKey { + content: pem_contents, + asn1: asn1_content, + pem_type: PemType::ECPrivateKey, + encoded_with: PemEncodedWith::PKCS8, + }), + Some(Classification::RSA) => Ok(PemEncodedKey { + content: pem_contents, + asn1: asn1_content, + pem_type: PemType::RSAPrivateKey, + encoded_with: PemEncodedWith::PKCS8, + }), + _ => return Err(ErrorKind::InvalidKeyFormat)?, + }, + + // This handles PKCS#8 public keys + "PUBLIC KEY" => match classify_pem(&asn1_content) { + Some(Classification::EC) => Ok(PemEncodedKey { + content: pem_contents, + asn1: asn1_content, + pem_type: PemType::ECPublicKey, + encoded_with: PemEncodedWith::PKCS8, + }), + Some(Classification::RSA) => Ok(PemEncodedKey { + content: pem_contents, + asn1: asn1_content, + pem_type: PemType::RSAPublicKey, + encoded_with: PemEncodedWith::PKCS8, + }), + _ => return Err(ErrorKind::InvalidKeyFormat)?, + }, + + // Unknown type + _ => return Err(ErrorKind::InvalidKeyFormat)?, + } } - } - - // This handles PKCS#8 public keys - "PUBLIC KEY" => { - match classify_pem(&asn1_content) { - Some(Classification::EC) => Ok(PemEncodedKey { - content: pem_contents, - asn1: asn1_content, - pem_type: PemType::ECPublicKey, - encoded_with: PemEncodedWith::PKCS8, - }), - Some(Classification::RSA) => Ok(PemEncodedKey { - content: pem_contents, - asn1: asn1_content, - pem_type: PemType::RSAPublicKey, - encoded_with: PemEncodedWith::PKCS8, - }), - _ => return Err(ErrorKind::InvalidKeyFormat)?, - } - } - - // Unknown type - _ => return Err(ErrorKind::InvalidKeyFormat)?, + Err(_) => return Err(ErrorKind::InvalidKeyFormat)?, } - }, - Err(_) => return Err(ErrorKind::InvalidKeyFormat)?, } - } - /// This will do the initial parsing of a PEM file. - /// Supported tagged pems include "RSA PRIVATE KEY", "RSA PUBLIC KEY", - /// "PRIVATE KEY", "PUBLIC KEY" - /// PEMs with multiple tagged portions are not supported - pub fn as_key(&self) -> Result> { - match self.encoded_with { - PemEncodedWith::PKCS1 => Ok(Key::Der(self.content.as_slice())), - PemEncodedWith::PKCS8 => { - match self.pem_type { - PemType::RSAPrivateKey => Ok(Key::Der(extract_first_bitstring(&self.asn1)?)), - PemType::RSAPublicKey => Ok(Key::Der(extract_first_bitstring(&self.asn1)?)), - PemType::ECPrivateKey => Ok(Key::Pkcs8(self.content.as_slice())), - PemType::ECPublicKey => Ok(Key::Pkcs8(extract_first_bitstring(&self.asn1)?)), + /// This will do the initial parsing of a PEM file. + /// Supported tagged pems include "RSA PRIVATE KEY", "RSA PUBLIC KEY", + /// "PRIVATE KEY", "PUBLIC KEY" + /// PEMs with multiple tagged portions are not supported + pub fn as_key(&self) -> Result> { + match self.encoded_with { + PemEncodedWith::PKCS1 => Ok(Key::Der(self.content.as_slice())), + PemEncodedWith::PKCS8 => match self.pem_type { + PemType::RSAPrivateKey => Ok(Key::Der(extract_first_bitstring(&self.asn1)?)), + PemType::RSAPublicKey => Ok(Key::Der(extract_first_bitstring(&self.asn1)?)), + PemType::ECPrivateKey => Ok(Key::Pkcs8(self.content.as_slice())), + PemType::ECPublicKey => Ok(Key::Pkcs8(extract_first_bitstring(&self.asn1)?)), + }, } - }, } - } } // This really just finds and returns the first bitstring or octet string @@ -149,47 +144,47 @@ impl PemEncodedKey { // Though PKCS#11 keys shouldn't have anything else. // It will get confusing with certificates. fn extract_first_bitstring(asn1: &Vec) -> Result<&[u8]> { - for asn1_entry in asn1.iter() { - match asn1_entry { - simple_asn1::ASN1Block::Sequence(_, entries) => { - if let Ok(result) = extract_first_bitstring(entries) { - return Ok(result); + for asn1_entry in asn1.iter() { + match asn1_entry { + simple_asn1::ASN1Block::Sequence(_, entries) => { + if let Ok(result) = extract_first_bitstring(entries) { + return Ok(result); + } + } + simple_asn1::ASN1Block::BitString(_, _, value) => { + return Ok(value.as_ref()); + } + simple_asn1::ASN1Block::OctetString(_, value) => { + return Ok(value.as_ref()); + } + _ => (), } - } - simple_asn1::ASN1Block::BitString(_, _, value) => { - return Ok(value.as_ref()); - } - simple_asn1::ASN1Block::OctetString(_, value) => { - return Ok(value.as_ref()); - } - _ => () } - } - return Err(ErrorKind::InvalidEcdsaKey)? + return Err(ErrorKind::InvalidEcdsaKey)?; } fn classify_pem(asn1: &Vec) -> Option { - // These should be constant but the macro requires - // #![feature(const_vec_new)] - let ec_public_key_oid = simple_asn1::oid!(1,2,840,10045,2,1); - let rsa_public_key_oid = simple_asn1::oid!(1,2,840,113549,1,1,1); + // These should be constant but the macro requires + // #![feature(const_vec_new)] + let ec_public_key_oid = simple_asn1::oid!(1, 2, 840, 10045, 2, 1); + let rsa_public_key_oid = simple_asn1::oid!(1, 2, 840, 113549, 1, 1, 1); - for asn1_entry in asn1.iter() { - match asn1_entry { - simple_asn1::ASN1Block::Sequence(_, entries) => { - if let Some(classification) = classify_pem(entries) { - return Some(classification); + for asn1_entry in asn1.iter() { + match asn1_entry { + simple_asn1::ASN1Block::Sequence(_, entries) => { + if let Some(classification) = classify_pem(entries) { + return Some(classification); + } + } + simple_asn1::ASN1Block::ObjectIdentifier(_, oid) => { + if oid == ec_public_key_oid { + return Some(Classification::EC); + } else if oid == rsa_public_key_oid { + return Some(Classification::RSA); + } + } + _ => {} } - } - simple_asn1::ASN1Block::ObjectIdentifier(_, oid) => { - if oid == ec_public_key_oid { - return Some(Classification::EC); - } else if oid == rsa_public_key_oid { - return Some(Classification::RSA); - } - } - _ => {} } - } None } diff --git a/src/validation.rs b/src/validation.rs index 8c7b2ca..463249e 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -1,7 +1,7 @@ -use std::collections::HashSet; use chrono::Utc; use serde_json::map::Map; use serde_json::{from_value, Value}; +use std::collections::HashSet; use crate::algorithms::Algorithm; use crate::errors::{new_error, ErrorKind, Result}; @@ -141,8 +141,8 @@ pub fn validate(claims: &Map, options: &Validation) -> Result<()> if let Some(ref correct_aud) = options.aud { if let Some(aud) = claims.get("aud") { - let provided_aud: HashSet = from_value(aud.clone())?; - if provided_aud.intersection(correct_aud).count() == 0 { + let provided_aud: HashSet = from_value(aud.clone())?; + if provided_aud.intersection(correct_aud).count() == 0 { return Err(new_error(ErrorKind::InvalidAudience)); } } else {