More refactoring in the crypto mod

This commit is contained in:
Vincent Prouillet 2019-11-11 20:29:57 +01:00
parent 1f6d0ffb2c
commit 8e4757cb1d
5 changed files with 78 additions and 43 deletions

View File

@ -1,17 +1,27 @@
use ring::{rand, signature};
use crate::algorithms::Algorithm;
use crate::errors::Result;
use crate::pem_decoder::PemEncodedKey;
use crate::serialization::b64_encode;
use crate::algorithms::Algorithm;
/// Only used internally when validating EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs.
pub(crate) fn alg_to_ec_verification(alg: Algorithm) -> &'static signature::EcdsaVerificationAlgorithm {
pub(crate) fn alg_to_ec_verification(
alg: Algorithm,
) -> &'static signature::EcdsaVerificationAlgorithm {
match alg {
Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED,
Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED,
_ => unreachable!("Tried to get EC signature for a non-EC algorithm"),
_ => unreachable!("Tried to get EC alg for a non-EC algorithm"),
}
}
/// Only used internally when signing EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs.
pub(crate) fn alg_to_ec_signing(alg: Algorithm) -> &'static signature::EcdsaSigningAlgorithm {
match alg {
Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED_SIGNING,
Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED_SIGNING,
_ => unreachable!("Tried to get EC alg for a non-EC algorithm"),
}
}

View File

@ -27,19 +27,33 @@ pub fn sign(message: &str, key: &[u8], algorithm: Algorithm) -> Result<String> {
Algorithm::HS384 => sign_hmac(hmac::HMAC_SHA384, key, message),
Algorithm::HS512 => sign_hmac(hmac::HMAC_SHA512, key, message),
Algorithm::ES256 => ecdsa::sign(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, key, message),
Algorithm::ES384 => ecdsa::sign(&signature::ECDSA_P384_SHA384_FIXED_SIGNING, key, message),
Algorithm::ES256 | Algorithm::ES384 => {
ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key, message)
}
Algorithm::RS256 => rsa::sign(&signature::RSA_PKCS1_SHA256, key, message),
Algorithm::RS384 => rsa::sign(&signature::RSA_PKCS1_SHA384, key, message),
Algorithm::RS512 => rsa::sign(&signature::RSA_PKCS1_SHA512, key, message),
Algorithm::PS256 => rsa::sign(&signature::RSA_PSS_SHA256, key, message),
Algorithm::PS384 => rsa::sign(&signature::RSA_PSS_SHA384, key, message),
Algorithm::PS512 => rsa::sign(&signature::RSA_PSS_SHA512, key, message),
Algorithm::RS256
| Algorithm::RS384
| Algorithm::RS512
| Algorithm::PS256
| Algorithm::PS384
| Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key, message),
}
}
/// See Ring docs for more details
fn verify_ring(
alg: &'static dyn signature::VerificationAlgorithm,
signature: &str,
message: &str,
key: &[u8],
) -> Result<bool> {
let signature_bytes = b64_decode(signature)?;
let public_key = signature::UnparsedPublicKey::new(alg, key);
let res = public_key.verify(message.as_bytes(), &signature_bytes);
Ok(res.is_ok())
}
/// Compares the signature given with a re-computed signature for HMAC or using the public key
/// for RSA/EC.
///
@ -59,31 +73,30 @@ pub fn verify(signature: &str, message: &str, key: &[u8], algorithm: Algorithm)
}
Algorithm::ES256 | Algorithm::ES384 => {
let pem_key = PemEncodedKey::new(key)?;
verify_ring(ecdsa::alg_to_ec_verification(algorithm), signature, message, pem_key.as_ec_public_key()?)
verify_ring(
ecdsa::alg_to_ec_verification(algorithm),
signature,
message,
pem_key.as_ec_public_key()?,
)
}
// only RSAs left
_ => {
Algorithm::RS256
| Algorithm::RS384
| Algorithm::RS512
| Algorithm::PS256
| Algorithm::PS384
| Algorithm::PS512 => {
let pem_key = PemEncodedKey::new(key)?;
verify_ring(rsa::alg_to_rsa_parameters(algorithm), signature, message, pem_key.as_rsa_key()?)
},
verify_ring(
rsa::alg_to_rsa_parameters(algorithm),
signature,
message,
pem_key.as_rsa_key()?,
)
}
}
}
/// See Ring docs for more details
fn verify_ring(
alg: &'static dyn signature::VerificationAlgorithm,
signature: &str,
message: &str,
key: &[u8],
) -> Result<bool> {
let signature_bytes = b64_decode(signature)?;
let public_key = signature::UnparsedPublicKey::new(alg, key);
let res = public_key.verify(message.as_bytes(), &signature_bytes);
Ok(res.is_ok())
}
/// Verify the signature given using the (n, e) components of a RSA public key.
///
/// `signature` is the signature part of a jwt (text after the second '.')
@ -96,5 +109,5 @@ pub fn verify_rsa_components(
alg: Algorithm,
) -> Result<bool> {
let signature_bytes = b64_decode(signature)?;
rsa::verify_from_components(&signature_bytes, message, components, alg)
rsa::verify_from_components(rsa::alg_to_rsa_parameters(alg), &signature_bytes, message, components)
}

View File

@ -1,11 +1,10 @@
use ring::{rand, signature};
use simple_asn1::BigUint;
use crate::algorithms::Algorithm;
use crate::errors::{ErrorKind, Result};
use crate::pem_decoder::PemEncodedKey;
use crate::serialization::{b64_encode, b64_decode};
use crate::algorithms::Algorithm;
use crate::serialization::{b64_decode, b64_encode};
/// Only used internally when validating RSA, to map from our enum to the Ring param structs.
pub(crate) fn alg_to_rsa_parameters(alg: Algorithm) -> &'static signature::RsaParameters {
@ -20,6 +19,18 @@ pub(crate) fn alg_to_rsa_parameters(alg: Algorithm) -> &'static signature::RsaPa
}
}
/// Only used internally when signing with RSA, to map from our enum to the Ring signing structs.
pub(crate) fn alg_to_rsa_signing(alg: Algorithm) -> &'static dyn signature::RsaEncoding {
match alg {
Algorithm::RS256 => &signature::RSA_PKCS1_SHA256,
Algorithm::RS384 => &signature::RSA_PKCS1_SHA384,
Algorithm::RS512 => &signature::RSA_PKCS1_SHA512,
Algorithm::PS256 => &signature::RSA_PSS_SHA256,
Algorithm::PS384 => &signature::RSA_PSS_SHA384,
Algorithm::PS512 => &signature::RSA_PSS_SHA512,
_ => unreachable!("Tried to get RSA signature for a non-rsa algorithm"),
}
}
/// The actual RSA signing + encoding
/// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html
@ -42,13 +53,14 @@ pub(crate) fn sign(
}
pub(crate) fn verify_from_components(
alg: &'static signature::RsaParameters,
signature_bytes: &[u8],
message: &str,
components: (&str, &str),
alg: Algorithm) -> Result<bool> {
) -> Result<bool> {
let n = BigUint::from_bytes_be(&b64_decode(components.0)?).to_bytes_be();
let e = BigUint::from_bytes_be(&b64_decode(components.1)?).to_bytes_be();
let pubkey = signature::RsaPublicKeyComponents { n, e };
let res = pubkey.verify(alg_to_rsa_parameters(alg), message.as_ref(), &signature_bytes);
let res = pubkey.verify(alg, message.as_ref(), &signature_bytes);
Ok(res.is_ok())
}

View File

@ -118,8 +118,8 @@ pub fn dangerous_unsafe_decode<T: DeserializeOwned>(token: &str) -> Result<Token
Ok(TokenData { header, claims: decoded_claims })
}
/// Decode a token and return the Header. This is not doing any kind of validation: it is meant to be
/// used when you don't know which `alg` the token is using and want to find out.
/// Decode a token and return the Header. This is not doing any kind of validation: it only splits
/// on the `.` and return the base64 decoded header.
///
/// If the token has an invalid format, it will return an error.
///

View File

@ -1,5 +1,5 @@
use std::time::{SystemTime, UNIX_EPOCH};
use std::collections::HashSet;
use std::time::{SystemTime, UNIX_EPOCH};
use serde_json::map::Map;
use serde_json::{from_value, Value};
@ -164,7 +164,7 @@ mod tests {
use serde_json::map::Map;
use serde_json::to_value;
use super::{validate, Validation, get_current_timestamp};
use super::{get_current_timestamp, validate, Validation};
use crate::errors::ErrorKind;