1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-07 17:03:01 +03:00

Move claim hash token function to mas-jose crate

Change the hash function according to the signature algorithm,
according to the OpendID Connect spec.
This commit is contained in:
Kévin Commaille
2022-09-06 11:42:40 +02:00
committed by Quentin Gliech
parent 36668d9b91
commit 0452ac10e6
4 changed files with 83 additions and 22 deletions

View File

@@ -14,7 +14,11 @@
use std::{collections::HashMap, marker::PhantomData, ops::Deref};
use anyhow::Context;
use base64ct::{Base64UrlUnpadded, Encoding};
use mas_iana::jose::JsonWebSignatureAlg;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use sha2::{Digest, Sha256, Sha384, Sha512};
use thiserror::Error;
#[derive(Debug, Error)]
@@ -235,6 +239,81 @@ impl From<&TimeOptions> for TimeNotBefore {
}
}
/// Hash the given token with the given algorithm for an ID Token claim.
///
/// According to the [OpenID Connect Core 1.0 specification].
///
/// # Errors
///
/// Returns an error if the algorithm is not supported.
///
/// [OpenID Connect Core 1.0 specification]: https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
pub fn hash_token(alg: JsonWebSignatureAlg, token: &str) -> anyhow::Result<String> {
let bits = match alg {
JsonWebSignatureAlg::Hs256
| JsonWebSignatureAlg::Rs256
| JsonWebSignatureAlg::Es256
| JsonWebSignatureAlg::Ps256
| JsonWebSignatureAlg::Es256K => {
let mut hasher = Sha256::new();
hasher.update(token);
let hash = hasher.finalize();
// Left-most half
hash.get(..16).map(ToOwned::to_owned)
}
JsonWebSignatureAlg::Hs384
| JsonWebSignatureAlg::Rs384
| JsonWebSignatureAlg::Es384
| JsonWebSignatureAlg::Ps384 => {
let mut hasher = Sha384::new();
hasher.update(token);
let hash = hasher.finalize();
// Left-most half
hash.get(..24).map(ToOwned::to_owned)
}
JsonWebSignatureAlg::Hs512
| JsonWebSignatureAlg::Rs512
| JsonWebSignatureAlg::Es512
| JsonWebSignatureAlg::Ps512 => {
let mut hasher = Sha512::new();
hasher.update(token);
let hash = hasher.finalize();
// Left-most half
hash.get(..32).map(ToOwned::to_owned)
}
JsonWebSignatureAlg::EdDsa | JsonWebSignatureAlg::None => {
return Err(anyhow::anyhow!("unsupported algorithm for hashing"))
}
}
.context("failed to get first half of hash")?;
Ok(Base64UrlUnpadded::encode_string(&bits))
}
#[derive(Debug, Clone)]
pub struct TokenHash<'a> {
alg: JsonWebSignatureAlg,
token: &'a str,
}
impl<'a> TokenHash<'a> {
/// Creates a new `TokenHash` validator for the given algorithm and token.
#[must_use]
pub fn new(alg: JsonWebSignatureAlg, token: &'a str) -> Self {
Self { alg, token }
}
}
impl<'a> Validator<String> for TokenHash<'a> {
fn validate(&self, value: &String) -> Result<(), anyhow::Error> {
if hash_token(self.alg, self.token)? == *value {
Ok(())
} else {
Err(anyhow::anyhow!("hashes don't match"))
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
#[serde(transparent)]
pub struct Timestamp(#[serde(with = "chrono::serde::ts_seconds")] chrono::DateTime<chrono::Utc>);