Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ring from pkce and generator #85

Merged
merged 1 commit into from
Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions oxide-auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ autoexamples = false
[dependencies]
base64 = "0.11"
chrono = "0.4.2"
hmac = "0.7.1"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
sha2 = "0.8.1"
subtle = "2.2.2"
rand = "0.7.3"
ring = ">=0.13,<0.15"
rmp-serde = "0.14"
url = "1.7"
Expand Down
15 changes: 7 additions & 8 deletions oxide-auth/src/code_grant/extensions/pkce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::borrow::Cow;
use primitives::grant::{GrantExtension, Value};

use base64;
use ring::digest::{SHA256, digest};
use ring::constant_time::verify_slices_are_equal;
use sha2::{Digest, Sha256};
use subtle::ConstantTimeEq;

/// Proof Key for Code Exchange by OAuth Public Clients
///
Expand Down Expand Up @@ -166,13 +166,12 @@ impl Method {
fn verify(&self, verifier: &str) -> Result<(), ()> {
match self {
Method::Plain(encoded) =>
verify_slices_are_equal(encoded.as_bytes(), verifier.as_bytes())
.map_err(|_| ()),
if encoded.as_bytes().ct_eq(verifier.as_bytes()).into() { Ok(()) } else { Err(()) },
Method::Sha256(encoded) => {
let digest = digest(&SHA256, verifier.as_bytes());
let b64digest = b64encode(digest.as_ref());
verify_slices_are_equal(encoded.as_bytes(), b64digest.as_bytes())
.map_err(|_| ())
let mut hasher = Sha256::new();
hasher.input(verifier.as_bytes());
let b64digest = b64encode(&hasher.result());
if encoded.as_bytes().ct_eq(b64digest.as_bytes()).into() { Ok(()) } else { Err(()) }
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion oxide-auth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,17 @@

extern crate base64;
extern crate chrono;
extern crate url;
extern crate hmac;
extern crate rand;
extern crate ring;
extern crate rmp_serde;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate sha2;
extern crate subtle;
extern crate url;

pub mod code_grant;
pub mod endpoint;
Expand Down
56 changes: 33 additions & 23 deletions oxide-auth/src/primitives/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//! - `Assertion` cryptographically verifies the integrity of a token, trading security without
//! persistent storage for the loss of revocability. It is thus unfit for some backends, which
//! is not currently expressed in the type system or with traits.

use super::grant::{Value, Extensions, Grant};
use super::{Url, Time};
use super::scope::Scope;
Expand All @@ -18,9 +19,8 @@ use std::rc::Rc;
use std::sync::Arc;

use base64::{encode, decode};
use ring::digest::SHA256;
use ring::rand::{SystemRandom, SecureRandom};
use ring::hmac::SigningKey;
use hmac::{crypto_mac::MacResult, Mac, Hmac};
use rand::{rngs::OsRng, RngCore, thread_rng};
use rmp_serde;

/// Generic token for a specific grant.
Expand Down Expand Up @@ -49,22 +49,23 @@ pub trait TagGrant {
/// Each byte is chosen randomly from the basic `rand::thread_rng`. This generator will always
/// succeed.
pub struct RandomGenerator {
random: SystemRandom,
random: OsRng,
len: usize
}

impl RandomGenerator {
/// Generates tokens with a specific byte length.
pub fn new(length: usize) -> RandomGenerator {
RandomGenerator {
random: SystemRandom::new(),
random: OsRng {},
len: length
}
}

fn generate(&self) -> String {
let mut result = vec![0; self.len];
self.random.fill(result.as_mut_slice())
let mut rnd = self.random.clone();
rnd.try_fill_bytes(result.as_mut_slice())
.expect("Failed to generate random token");
encode(&result)
}
Expand All @@ -80,7 +81,7 @@ impl RandomGenerator {
/// signing the same grant for different uses, i.e. separating authorization from bearer grants and
/// refresh tokens.
pub struct Assertion {
secret: SigningKey,
hasher: Hmac::<sha2::Sha256>
}

/// The cryptographic suite ensuring integrity of tokens.
Expand Down Expand Up @@ -132,24 +133,24 @@ impl Assertion {
/// padding or shortening of the supplied key material may be applied in the form dictated by
/// the signature type. See the respective standards.
///
/// If future suites are added where this is not possible, his function may panic when supplied
/// If future suites are added where this is not possible, this function may panic when supplied
/// with an incorrect key length.
///
/// Currently, the implementation lacks the ability to really make use of another hasing mechanism than
/// hmac + sha256.
pub fn new(kind: AssertionKind, key: &[u8]) -> Self {
let key = match kind {
AssertionKind::HmacSha256 => SigningKey::new(&SHA256, key),
match kind {
AssertionKind::HmacSha256 => Assertion { hasher: Hmac::<sha2::Sha256>::new_varkey(key).unwrap() },
AssertionKind::__NonExhaustive => unreachable!(),
};

Assertion {
secret: key,
}
}

/// Construct an assertion instance whose tokens are only valid for the program execution.
pub fn ephemeral() -> Self {
Assertion {
secret: SigningKey::generate(&SHA256, &SystemRandom::new()).unwrap(),
}
// TODO Extract KeySize from currently selected hasher
let mut rand_bytes: [u8; 32] = [0;32];
thread_rng().fill_bytes(&mut rand_bytes);
Assertion { hasher: Hmac::<sha2::Sha256>::new_varkey(&rand_bytes).unwrap() }
}

/// Get a reference to generator for the given tag.
Expand All @@ -160,14 +161,21 @@ impl Assertion {
fn extract<'a>(&self, token: &'a str) -> Result<(Grant, String), ()> {
let decoded = decode(token).map_err(|_| ())?;
let assertion: AssertGrant = rmp_serde::from_slice(&decoded).map_err(|_| ())?;
ring::hmac::verify_with_own_key(&self.secret, &assertion.0, &assertion.1).map_err(|_| ())?;

let mut hasher = self.hasher.clone();
hasher.input(&assertion.0);
hasher.verify(assertion.1.as_slice()).map_err(|_| ())?;

let (_, serde_grant, tag): (u64, SerdeAssertionGrant, String)
= rmp_serde::from_slice(&assertion.0).map_err(|_| ())?;

Ok((serde_grant.grant(), tag))
}

fn signature(&self, data: &[u8]) -> ring::hmac::Signature {
ring::hmac::sign(&self.secret, data)
fn signature(&self, data: &[u8]) -> MacResult<<hmac::Hmac<sha2::Sha256> as Mac>::OutputSize> {
let mut hasher = self.hasher.clone();
hasher.input(data);
hasher.result()
}

fn counted_signature(&self, counter: u64, grant: &Grant)
Expand All @@ -176,14 +184,16 @@ impl Assertion {
let serde_grant = SerdeAssertionGrant::try_from(grant)?;
let tosign = rmp_serde::to_vec(&(serde_grant, counter)).unwrap();
let signature = self.signature(&tosign);
Ok(base64::encode(&signature))
Ok(base64::encode(&signature.code()))
}

fn generate_tagged(&self, counter: u64, grant: &Grant, tag: &str) -> Result<String, ()> {
let serde_grant = SerdeAssertionGrant::try_from(grant)?;
let tosign = rmp_serde::to_vec(&(counter, serde_grant, tag)).unwrap();
let signature = self.signature(&tosign);
Ok(encode(&rmp_serde::to_vec(&AssertGrant(tosign, signature.as_ref().to_vec())).unwrap()))
let signature = self.signature(&tosign);
let assert = AssertGrant(tosign, signature.code().to_vec());

Ok(encode(&rmp_serde::to_vec(&assert).unwrap()))
}
}

Expand Down