use {Algorithm, TwistJwtResult};
use base64::{self, URL_SAFE_NO_PAD};
use error::TwistJwt;
use ring::digest::{SHA256, SHA384, SHA512};
use ring::hmac::{sign, SigningKey};
use ring::rand::SystemRandom;
use ring::signature::{RSAKeyPair, RSASigningState, RSA_PKCS1_SHA256, RSA_PKCS1_SHA384,
RSA_PKCS1_SHA512, RSA_PSS_SHA256, RSA_PSS_SHA384, RSA_PSS_SHA512};
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
use untrusted;
fn sign_hmac(payload: &str, secret: &[u8], algorithm: Algorithm) -> TwistJwtResult<String> {
let digest = match algorithm {
Algorithm::HS256 => &SHA256,
Algorithm::HS384 => &SHA384,
Algorithm::HS512 => &SHA512,
_ => return Err(TwistJwt::InvalidAlgorithm(algorithm)),
};
let key = SigningKey::new(digest, secret);
let signed = sign(&key, payload.as_bytes());
let signed_str = signed.as_ref();
let signature = base64::encode_config(signed_str, URL_SAFE_NO_PAD);
Ok(signature)
}
fn sign_rsa(payload: &str, key_path: &str, algorithm: Algorithm) -> TwistJwtResult<String> {
let digest = match algorithm {
Algorithm::RS256 => &RSA_PKCS1_SHA256,
Algorithm::RS384 => &RSA_PKCS1_SHA384,
Algorithm::RS512 => &RSA_PKCS1_SHA512,
_ => return Err(TwistJwt::InvalidAlgorithm(algorithm)),
};
let path_buf = PathBuf::from(key_path);
let mut key_file = File::open(path_buf)?;
let mut key_bytes = Vec::new();
key_file.read_to_end(&mut key_bytes)?;
let key_bytes_der = untrusted::Input::from(&key_bytes);
let key_pair = RSAKeyPair::from_der(key_bytes_der)?;
let key_pair_arc = Arc::new(key_pair);
let mut signing_state = RSASigningState::new(key_pair_arc)?;
let len = signing_state.key_pair().public_modulus_len();
let mut signature_bytes = vec![0; len];
let rng = SystemRandom::new();
signing_state.sign(digest,
&rng,
payload.as_bytes(),
signature_bytes.as_mut_slice())?;
let signature = base64::encode_config(&signature_bytes, URL_SAFE_NO_PAD);
Ok(signature)
}
fn sign_rsa_pss(payload: &str, key_path: &str, algorithm: Algorithm) -> TwistJwtResult<String> {
let digest = match algorithm {
Algorithm::PS256 => &RSA_PSS_SHA256,
Algorithm::PS384 => &RSA_PSS_SHA384,
Algorithm::PS512 => &RSA_PSS_SHA512,
_ => return Err(TwistJwt::InvalidAlgorithm(algorithm)),
};
let path_buf = PathBuf::from(key_path);
let mut key_file = File::open(path_buf)?;
let mut key_bytes = Vec::new();
key_file.read_to_end(&mut key_bytes)?;
let key_bytes_der = untrusted::Input::from(&key_bytes);
let key_pair = RSAKeyPair::from_der(key_bytes_der)?;
let key_pair_arc = Arc::new(key_pair);
let mut signing_state = RSASigningState::new(key_pair_arc)?;
let len = signing_state.key_pair().public_modulus_len();
let mut signature_bytes = vec![0; len];
let rng = SystemRandom::new();
signing_state.sign(digest,
&rng,
payload.as_bytes(),
signature_bytes.as_mut_slice())?;
let signature = base64::encode_config(&signature_bytes, URL_SAFE_NO_PAD);
Ok(signature)
}
pub fn jwt(payload: &str, secret: &[u8], alg: Algorithm) -> TwistJwtResult<String> {
match alg {
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
Ok(sign_hmac(payload, secret, alg)?)
}
Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => {
let key_path = String::from_utf8_lossy(secret);
Ok(sign_rsa(payload, &key_path, alg)?)
}
Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => {
let key_path = String::from_utf8_lossy(secret);
Ok(sign_rsa_pss(payload, &key_path, alg)?)
}
}
}