twist-jwt 0.3.1

An implementation of RFC7519 JSON Web Token (JWT)
//! Signing Algorithms
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;

/// Take the payload of a JWT and sign it using the HMAC algorithm given.
/// Returns the base64 url safe encoded string of the result.
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)
}

/// Take the payload of a JWT and sign it using the RSA algorithm given.
/// Returns the base64 url safe encoded string of the result.
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)?;

    // Create an `RSAKeyPair` from the DER-encoded bytes.
    let key_bytes_der = untrusted::Input::from(&key_bytes);
    let key_pair = RSAKeyPair::from_der(key_bytes_der)?;

    // Create a signing state.
    let key_pair_arc = Arc::new(key_pair);
    let mut signing_state = RSASigningState::new(key_pair_arc)?;

    // Sign the payload, using PKCS#1 v1.5 padding and the SHA256 digest algorithm.
    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)
}

/// Take the payload of a JWT and sign it using the RSA algorithm given.
/// Returns the base64 url safe encoded string of the result.
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)?;

    // Create an `RSAKeyPair` from the DER-encoded bytes.
    let key_bytes_der = untrusted::Input::from(&key_bytes);
    let key_pair = RSAKeyPair::from_der(key_bytes_der)?;

    // Create a signing state.
    let key_pair_arc = Arc::new(key_pair);
    let mut signing_state = RSASigningState::new(key_pair_arc)?;

    // Sign the payload, using the SHA256 digest algorithm.
    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)
}

/// Generate a JWT signature for the given payload, secret, and algorithm.
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)?)
        }
        // Algorithm::ES256 | Algorithm::ES384 => {
        //     "".to_string()
        //     // sign_rsa(&payload, secret, header.alg)
        // }
    }
}