Move crypto to a dir
This commit is contained in:
parent
e3632b3a2c
commit
34ea194179
|
@ -0,0 +1,18 @@
|
|||
use ring::{rand, signature};
|
||||
|
||||
use crate::errors::{Result};
|
||||
use crate::pem_decoder::PemEncodedKey;
|
||||
use crate::serialization::encode;
|
||||
|
||||
/// The actual ECDSA signing + encoding
|
||||
pub fn sign(
|
||||
alg: &'static signature::EcdsaSigningAlgorithm,
|
||||
key: &[u8],
|
||||
signing_input: &str,
|
||||
) -> Result<String> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, pem_key.as_ec_private_key()?)?;
|
||||
let rng = rand::SystemRandom::new();
|
||||
let out = signing_key.sign(&rng, signing_input.as_bytes())?;
|
||||
Ok(encode(out.as_ref()))
|
||||
}
|
|
@ -1,54 +1,26 @@
|
|||
use base64;
|
||||
use ring::constant_time::verify_slices_are_equal;
|
||||
use ring::{hmac, rand, signature};
|
||||
use ring::{hmac, signature};
|
||||
|
||||
use crate::algorithms::Algorithm;
|
||||
use crate::errors::{ErrorKind, Result};
|
||||
use crate::errors::{Result};
|
||||
use crate::pem_decoder::PemEncodedKey;
|
||||
use crate::serialization::{encode, decode};
|
||||
|
||||
pub(crate) mod rsa;
|
||||
pub(crate) mod ecdsa;
|
||||
|
||||
/// The actual HS signing + encoding
|
||||
fn sign_hmac(alg: hmac::Algorithm, key: &[u8], signing_input: &str) -> Result<String> {
|
||||
pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], signing_input: &str) -> Result<String> {
|
||||
let digest = hmac::sign(&hmac::Key::new(alg, key), signing_input.as_bytes());
|
||||
Ok(base64::encode_config::<hmac::Tag>(&digest, base64::URL_SAFE_NO_PAD))
|
||||
Ok(encode(digest.as_ref()))
|
||||
}
|
||||
|
||||
/// The actual ECDSA signing + encoding
|
||||
fn sign_ecdsa(
|
||||
alg: &'static signature::EcdsaSigningAlgorithm,
|
||||
key: &[u8],
|
||||
signing_input: &str,
|
||||
) -> Result<String> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, pem_key.as_ec_private_key()?)?;
|
||||
let rng = rand::SystemRandom::new();
|
||||
let sig = signing_key.sign(&rng, signing_input.as_bytes())?;
|
||||
Ok(base64::encode_config(&sig, base64::URL_SAFE_NO_PAD))
|
||||
}
|
||||
|
||||
/// The actual RSA signing + encoding
|
||||
/// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html
|
||||
fn sign_rsa(
|
||||
alg: &'static dyn signature::RsaEncoding,
|
||||
key: &[u8],
|
||||
signing_input: &str,
|
||||
) -> Result<String> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
let key_pair = signature::RsaKeyPair::from_der(pem_key.as_rsa_key()?)
|
||||
.map_err(|_| ErrorKind::InvalidRsaKey)?;
|
||||
|
||||
let mut signature = vec![0; key_pair.public_modulus_len()];
|
||||
let rng = rand::SystemRandom::new();
|
||||
key_pair
|
||||
.sign(alg, &rng, signing_input.as_bytes(), &mut signature)
|
||||
.map_err(|_| ErrorKind::InvalidRsaKey)?;
|
||||
|
||||
Ok(base64::encode_config::<[u8]>(&signature, base64::URL_SAFE_NO_PAD))
|
||||
}
|
||||
|
||||
/// Take the payload of a JWT, sign it using the algorithm given and return
|
||||
/// the base64 url safe encoded of the result.
|
||||
///
|
||||
/// Only use this function if you want to do something other than JWT.
|
||||
/// `key` is the secret for HMAC and a pem encoded string otherwise
|
||||
pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<String> {
|
||||
match algorithm {
|
||||
Algorithm::HS256 => sign_hmac(hmac::HMAC_SHA256, key, signing_input),
|
||||
|
@ -56,56 +28,22 @@ pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<Str
|
|||
Algorithm::HS512 => sign_hmac(hmac::HMAC_SHA512, key, signing_input),
|
||||
|
||||
Algorithm::ES256 => {
|
||||
sign_ecdsa(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, key, signing_input)
|
||||
ecdsa::sign(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, key, signing_input)
|
||||
}
|
||||
Algorithm::ES384 => {
|
||||
sign_ecdsa(&signature::ECDSA_P384_SHA384_FIXED_SIGNING, key, signing_input)
|
||||
ecdsa::sign(&signature::ECDSA_P384_SHA384_FIXED_SIGNING, key, signing_input)
|
||||
}
|
||||
|
||||
Algorithm::RS256 => sign_rsa(&signature::RSA_PKCS1_SHA256, key, signing_input),
|
||||
Algorithm::RS384 => sign_rsa(&signature::RSA_PKCS1_SHA384, key, signing_input),
|
||||
Algorithm::RS512 => sign_rsa(&signature::RSA_PKCS1_SHA512, key, signing_input),
|
||||
Algorithm::RS256 => rsa::sign(&signature::RSA_PKCS1_SHA256, key, signing_input),
|
||||
Algorithm::RS384 => rsa::sign(&signature::RSA_PKCS1_SHA384, key, signing_input),
|
||||
Algorithm::RS512 => rsa::sign(&signature::RSA_PKCS1_SHA512, key, signing_input),
|
||||
|
||||
Algorithm::PS256 => sign_rsa(&signature::RSA_PSS_SHA256, key, signing_input),
|
||||
Algorithm::PS384 => sign_rsa(&signature::RSA_PSS_SHA384, key, signing_input),
|
||||
Algorithm::PS512 => sign_rsa(&signature::RSA_PSS_SHA512, key, signing_input),
|
||||
Algorithm::PS256 => rsa::sign(&signature::RSA_PSS_SHA256, key, signing_input),
|
||||
Algorithm::PS384 => rsa::sign(&signature::RSA_PSS_SHA384, key, signing_input),
|
||||
Algorithm::PS512 => rsa::sign(&signature::RSA_PSS_SHA512, key, signing_input),
|
||||
}
|
||||
}
|
||||
|
||||
/// See Ring docs for more details
|
||||
fn verify_ring(
|
||||
alg: &'static dyn signature::VerificationAlgorithm,
|
||||
signature: &str,
|
||||
signing_input: &str,
|
||||
key: &[u8],
|
||||
) -> Result<bool> {
|
||||
let signature_bytes = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?;
|
||||
let public_key = signature::UnparsedPublicKey::new(alg, key);
|
||||
let res = public_key.verify(signing_input.as_bytes(), &signature_bytes);
|
||||
|
||||
Ok(res.is_ok())
|
||||
}
|
||||
|
||||
fn verify_ring_es(
|
||||
alg: &'static dyn signature::VerificationAlgorithm,
|
||||
signature: &str,
|
||||
signing_input: &str,
|
||||
key: &[u8],
|
||||
) -> Result<bool> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
verify_ring(alg, signature, signing_input, pem_key.as_ec_public_key()?)
|
||||
}
|
||||
|
||||
fn verify_ring_rsa(
|
||||
alg: &'static signature::RsaParameters,
|
||||
signature: &str,
|
||||
signing_input: &str,
|
||||
key: &[u8],
|
||||
) -> Result<bool> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
verify_ring(alg, signature, signing_input, pem_key.as_rsa_key()?)
|
||||
}
|
||||
|
||||
/// Compares the signature given with a re-computed signature for HMAC or using the public key
|
||||
/// for RSA.
|
||||
///
|
||||
|
@ -152,3 +90,39 @@ pub fn verify(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: see if we can remove stuff?
|
||||
|
||||
/// See Ring docs for more details
|
||||
fn verify_ring(
|
||||
alg: &'static dyn signature::VerificationAlgorithm,
|
||||
signature: &str,
|
||||
signing_input: &str,
|
||||
key: &[u8],
|
||||
) -> Result<bool> {
|
||||
let signature_bytes = decode(signature)?;
|
||||
let public_key = signature::UnparsedPublicKey::new(alg, key);
|
||||
let res = public_key.verify(signing_input.as_bytes(), &signature_bytes);
|
||||
|
||||
Ok(res.is_ok())
|
||||
}
|
||||
|
||||
fn verify_ring_es(
|
||||
alg: &'static dyn signature::VerificationAlgorithm,
|
||||
signature: &str,
|
||||
signing_input: &str,
|
||||
key: &[u8],
|
||||
) -> Result<bool> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
verify_ring(alg, signature, signing_input, pem_key.as_ec_public_key()?)
|
||||
}
|
||||
|
||||
fn verify_ring_rsa(
|
||||
alg: &'static signature::RsaParameters,
|
||||
signature: &str,
|
||||
signing_input: &str,
|
||||
key: &[u8],
|
||||
) -> Result<bool> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
verify_ring(alg, signature, signing_input, pem_key.as_rsa_key()?)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
use ring::{rand, signature};
|
||||
|
||||
use crate::errors::{ErrorKind, Result};
|
||||
use crate::pem_decoder::PemEncodedKey;
|
||||
use crate::serialization::encode;
|
||||
|
||||
/// The actual RSA signing + encoding
|
||||
/// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html
|
||||
pub fn sign(
|
||||
alg: &'static dyn signature::RsaEncoding,
|
||||
key: &[u8],
|
||||
signing_input: &str,
|
||||
) -> Result<String> {
|
||||
let pem_key = PemEncodedKey::new(key)?;
|
||||
let key_pair = signature::RsaKeyPair::from_der(pem_key.as_rsa_key()?)
|
||||
.map_err(|_| ErrorKind::InvalidRsaKey)?;
|
||||
|
||||
let mut signature = vec![0; key_pair.public_modulus_len()];
|
||||
let rng = rand::SystemRandom::new();
|
||||
key_pair
|
||||
.sign(alg, &rng, signing_input.as_bytes(), &mut signature)
|
||||
.map_err(|_| ErrorKind::InvalidRsaKey)?;
|
||||
|
||||
Ok(encode(&signature))
|
||||
}
|
|
@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::algorithms::Algorithm;
|
||||
use crate::errors::Result;
|
||||
use crate::serialization::decode;
|
||||
|
||||
|
||||
/// A basic JWT header, the alg defaults to HS256 and typ is automatically
|
||||
/// set to `JWT`. All the other fields are optional.
|
||||
|
@ -59,7 +61,7 @@ impl Header {
|
|||
|
||||
/// Converts an encoded part into the Header struct if possible
|
||||
pub(crate) fn from_encoded(encoded_part: &str) -> Result<Self> {
|
||||
let decoded = base64::decode_config(encoded_part, base64::URL_SAFE_NO_PAD)?;
|
||||
let decoded = decode(encoded_part)?;
|
||||
let s = String::from_utf8(decoded)?;
|
||||
|
||||
Ok(serde_json::from_str(&s)?)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::validation::{Validation, validate};
|
||||
use crate::header::Header;
|
||||
use crate::errors::{new_error, ErrorKind, Result};
|
||||
use crate::serialization::{from_jwt_part_claims, TokenData};
|
||||
|
||||
|
||||
pub fn decode_rsa_jwk<T: DeserializeOwned>(
|
||||
token: &str,
|
||||
modulus: &[u8],
|
||||
exponent: &[u8],
|
||||
validation: &Validation,
|
||||
) -> Result<TokenData<T>> {
|
||||
let (signature, signing_input) = expect_two!(token.rsplitn(2, '.'));
|
||||
let (claims, header) = expect_two!(signing_input.rsplitn(2, '.'));
|
||||
let header = Header::from_encoded(header)?;
|
||||
|
||||
if !verify(signature, signing_input, key, header.alg)? {
|
||||
return Err(new_error(ErrorKind::InvalidSignature));
|
||||
}
|
||||
|
||||
if !validation.algorithms.contains(&header.alg) {
|
||||
return Err(new_error(ErrorKind::InvalidAlgorithm));
|
||||
}
|
||||
|
||||
let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?;
|
||||
validate(&claims_map, validation)?;
|
||||
|
||||
Ok(TokenData { header, claims: decoded_claims })
|
||||
}
|
|
@ -12,6 +12,7 @@ mod pem_decoder;
|
|||
mod pem_encoder;
|
||||
mod serialization;
|
||||
mod validation;
|
||||
// mod jwk;
|
||||
|
||||
pub use algorithms::Algorithm;
|
||||
pub use crypto::{sign, verify};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use base64;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::ser::Serialize;
|
||||
use serde_json::map::Map;
|
||||
|
@ -16,19 +15,26 @@ pub struct TokenData<T> {
|
|||
pub claims: T,
|
||||
}
|
||||
|
||||
pub(crate) fn encode(input: &[u8]) -> String {
|
||||
base64::encode_config(input, base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
pub(crate) fn decode(input: &str) -> Result<Vec<u8>> {
|
||||
base64::decode_config(input, base64::URL_SAFE_NO_PAD).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Serializes a struct to JSON and encodes it in base64
|
||||
pub fn encode_part<T: Serialize>(input: &T) -> Result<String> {
|
||||
pub(crate) fn encode_part<T: Serialize>(input: &T) -> Result<String> {
|
||||
let json = to_string(input)?;
|
||||
Ok(base64::encode_config(json.as_bytes(), base64::URL_SAFE_NO_PAD))
|
||||
Ok(encode(json.as_bytes()))
|
||||
}
|
||||
|
||||
/// Decodes from base64 and deserializes from JSON to a struct AND a hashmap of Value so we can
|
||||
/// run validation on it
|
||||
pub fn from_jwt_part_claims<B: AsRef<str>, T: DeserializeOwned>(
|
||||
pub(crate) fn from_jwt_part_claims<B: AsRef<str>, T: DeserializeOwned>(
|
||||
encoded: B,
|
||||
) -> Result<(T, Map<String, Value>)> {
|
||||
let decoded = base64::decode_config(encoded.as_ref(), base64::URL_SAFE_NO_PAD)?;
|
||||
let s = String::from_utf8(decoded)?;
|
||||
let s = String::from_utf8(decode(encoded.as_ref())?)?;
|
||||
|
||||
let claims: T = from_str(&s)?;
|
||||
let map: Map<_, _> = from_str(&s)?;
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
mod ec;
|
||||
mod ecdsa;
|
||||
mod rsa;
|
||||
|
|
Loading…
Reference in New Issue