Add ring-backed digest types

This commit is contained in:
asonix 2023-08-04 17:54:12 -05:00
parent 1f23b72140
commit ea964decc8
3 changed files with 203 additions and 6 deletions

View file

@ -1,7 +1,7 @@
[package]
name = "http-signature-normalization-actix"
description = "An HTTP Signatures library that leaves the signing to you"
version = "0.10.0"
version = "0.10.1"
authors = ["asonix <asonix@asonix.dog>"]
license = "AGPL-3.0"
readme = "README.md"
@ -12,11 +12,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["server", "sha-2", "sha-3"]
client = ["awc"]
digest = ["base64"]
server = ["actix-web"]
sha-2 = ["digest", "sha2"]
sha-3 = ["digest", "sha3"]
client = ["dep:awc"]
digest = ["dep:base64"]
server = ["dep:actix-web"]
sha-2 = ["digest", "dep:sha2"]
sha-3 = ["digest", "dep:sha3"]
ring = ["digest", "dep:ring"]
[[example]]
name = "server"
@ -34,6 +35,7 @@ awc = { version = "3.0.0", default-features = false, optional = true }
base64 = { version = "0.13", optional = true }
futures-util = { version = "0.3", default-features = false }
http-signature-normalization = { version = "0.7.0", path = ".." }
ring = { version = "0.16.20", optional = true }
sha2 = { version = "0.10", optional = true }
sha3 = { version = "0.10", optional = true }
thiserror = "1.0"

View file

@ -5,6 +5,8 @@
#[cfg(feature = "server")]
pub mod middleware;
#[cfg(feature = "ring")]
pub mod ring;
#[cfg(feature = "sha-2")]
mod sha2;
#[cfg(feature = "sha-3")]

193
actix/src/digest/ring.rs Normal file
View file

@ -0,0 +1,193 @@
//! Types for creating digests with the `ring` cryptography library
use crate::digest::DigestName;
/// A Sha256 digest backed by ring
pub struct Sha256 {
ctx: ring::digest::Context,
}
/// A Sha384 digest backed by ring
pub struct Sha384 {
ctx: ring::digest::Context,
}
/// A Sha512 digest backed by ring
pub struct Sha512 {
ctx: ring::digest::Context,
}
impl Sha256 {
/// Create a new empty digest
pub fn new() -> Self {
Self::default()
}
/// Extract the context
pub fn into_inner(self) -> ring::digest::Context {
self.ctx
}
}
impl Default for Sha256 {
fn default() -> Self {
Sha256 {
ctx: ring::digest::Context::new(&ring::digest::SHA256),
}
}
}
impl Sha384 {
/// Create a new empty digest
pub fn new() -> Self {
Self::default()
}
/// Extract the context
pub fn into_inner(self) -> ring::digest::Context {
self.ctx
}
}
impl Default for Sha384 {
fn default() -> Self {
Sha384 {
ctx: ring::digest::Context::new(&ring::digest::SHA384),
}
}
}
impl Sha512 {
/// Create a new empty digest
pub fn new() -> Self {
Self::default()
}
/// Extract the context
pub fn into_inner(self) -> ring::digest::Context {
self.ctx
}
}
impl Default for Sha512 {
fn default() -> Self {
Sha512 {
ctx: ring::digest::Context::new(&ring::digest::SHA512),
}
}
}
impl DigestName for Sha256 {
const NAME: &'static str = "SHA-256";
}
impl DigestName for Sha384 {
const NAME: &'static str = "SHA-384";
}
impl DigestName for Sha512 {
const NAME: &'static str = "SHA-512";
}
#[cfg(feature = "client")]
mod client {
use super::*;
use crate::digest::DigestCreate;
fn create(mut context: ring::digest::Context, input: &[u8]) -> String {
context.update(input);
let digest = context.finish();
base64::encode(digest.as_ref())
}
impl DigestCreate for Sha256 {
fn compute(&mut self, input: &[u8]) -> String {
create(self.ctx.clone(), input)
}
}
impl DigestCreate for Sha384 {
fn compute(&mut self, input: &[u8]) -> String {
create(self.ctx.clone(), input)
}
}
impl DigestCreate for Sha512 {
fn compute(&mut self, input: &[u8]) -> String {
create(self.ctx.clone(), input)
}
}
}
#[cfg(feature = "server")]
mod server {
use super::*;
use crate::digest::{DigestPart, DigestVerify};
use tracing::{debug, warn};
fn verify(context: ring::digest::Context, name: &str, parts: &[DigestPart]) -> bool {
if let Some(part) = parts
.iter()
.find(|p| p.algorithm.to_lowercase() == name.to_lowercase())
{
debug!("Verifying digest type, {}", name);
let digest = context.finish();
let encoded = base64::encode(digest.as_ref());
return part.digest == encoded;
}
warn!("No matching digest algorithm found for {}", name);
warn!(
"Provided: [{}]",
parts.iter().fold(String::new(), |mut acc, item| {
if acc.is_empty() {
} else {
acc.push_str(", ");
}
acc.push_str(&item.algorithm);
acc
})
);
false
}
impl DigestVerify for Sha256 {
fn update(&mut self, part: &[u8]) {
self.ctx.update(part);
}
fn verify(&mut self, parts: &[DigestPart]) -> bool {
let alg = self.ctx.algorithm();
let ctx = std::mem::replace(&mut self.ctx, ring::digest::Context::new(alg));
verify(ctx, Self::NAME, parts)
}
}
impl DigestVerify for Sha384 {
fn update(&mut self, part: &[u8]) {
self.ctx.update(part);
}
fn verify(&mut self, parts: &[DigestPart]) -> bool {
let alg = self.ctx.algorithm();
let ctx = std::mem::replace(&mut self.ctx, ring::digest::Context::new(alg));
verify(ctx, Self::NAME, parts)
}
}
impl DigestVerify for Sha512 {
fn update(&mut self, part: &[u8]) {
self.ctx.update(part);
}
fn verify(&mut self, parts: &[DigestPart]) -> bool {
let alg = self.ctx.algorithm();
let ctx = std::mem::replace(&mut self.ctx, ring::digest::Context::new(alg));
verify(ctx, Self::NAME, parts)
}
}
}