From 81d95ef2156d3b3f9f0a28189c17aa14eb2a15bb Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Thu, 9 Sep 2021 16:52:08 +0200 Subject: [PATCH] Add c_hash, at_hash and nonce claims to id_token --- Cargo.lock | 1 + matrix-authentication-service/Cargo.toml | 5 +-- matrix-authentication-service/sqlx-data.json | 36 +++++++++++-------- .../src/handlers/oauth2/token.rs | 20 +++++++++++ .../src/storage/oauth2/authorization_code.rs | 4 ++- 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6612420b6..47a0364c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1405,6 +1405,7 @@ dependencies = [ "serde_urlencoded", "serde_with", "serde_yaml", + "sha2", "sqlx", "tera", "thiserror", diff --git a/matrix-authentication-service/Cargo.toml b/matrix-authentication-service/Cargo.toml index a7221456b..bcf682bd3 100644 --- a/matrix-authentication-service/Cargo.toml +++ b/matrix-authentication-service/Cargo.toml @@ -49,12 +49,14 @@ dotenv = "0.15.0" argon2 = { version = "0.3.0", features = ["password-hash"] } password-hash = { version = "0.3.0", features = ["std"] } -# Crypto and signing stuff +# Crypto, hashing and signing stuff rsa = "0.5.0" k256 = "0.9.6" pkcs8 = { version = "0.7.5", features = ["pem"] } elliptic-curve = { version = "0.10.6", features = ["pem"] } chacha20poly1305 = { version = "0.9.0", features = ["std"] } +sha2 = "0.9.8" +crc = "2.0.0" # Various data types and utilities data-encoding = "2.3.2" @@ -66,7 +68,6 @@ rand = "0.8.4" bincode = "1.3.3" headers = "0.3.4" cookie = "0.15.1" -crc = "2.0.0" oauth2-types = { path = "../oauth2-types", features = ["sqlx_type"] } diff --git a/matrix-authentication-service/sqlx-data.json b/matrix-authentication-service/sqlx-data.json index 3f9c14d29..396f585c9 100644 --- a/matrix-authentication-service/sqlx-data.json +++ b/matrix-authentication-service/sqlx-data.json @@ -381,20 +381,8 @@ ] } }, - "88ac8783bd5881c42eafd9cf87a16fe6031f3153fd6a8618e689694584aeb2de": { - "query": "\n DELETE FROM oauth2_access_tokens\n WHERE id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - } - }, - "8c21b0b46e74ae5667b82bfe57706a64396683ac4dc29311424008c3f3e94136": { - "query": "\n SELECT\n oc.id,\n os.id AS \"oauth2_session_id!\",\n os.client_id AS \"client_id!\",\n os.redirect_uri,\n os.scope AS \"scope!\"\n FROM oauth2_codes oc\n INNER JOIN oauth2_sessions os\n ON os.id = oc.oauth2_session_id\n WHERE oc.code = $1\n ", + "886dee6a6f1f426f0e891790bbeffbc222fd75d8da0a107e7de673f1cc445f30": { + "query": "\n SELECT\n oc.id,\n os.id AS \"oauth2_session_id!\",\n os.client_id AS \"client_id!\",\n os.redirect_uri,\n os.scope AS \"scope!\",\n os.nonce\n FROM oauth2_codes oc\n INNER JOIN oauth2_sessions os\n ON os.id = oc.oauth2_session_id\n WHERE oc.code = $1\n ", "describe": { "columns": [ { @@ -421,6 +409,11 @@ "ordinal": 4, "name": "scope!", "type_info": "Text" + }, + { + "ordinal": 5, + "name": "nonce", + "type_info": "Text" } ], "parameters": { @@ -433,10 +426,23 @@ false, false, false, - false + false, + true ] } }, + "88ac8783bd5881c42eafd9cf87a16fe6031f3153fd6a8618e689694584aeb2de": { + "query": "\n DELETE FROM oauth2_access_tokens\n WHERE id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + } + }, "9ba45ab114b656105cc46b0c10fb05769860fcdc05eaf54d6225640fb914dab9": { "query": "\n INSERT INTO user_session_authentications (session_id)\n VALUES ($1)\n RETURNING created_at\n ", "describe": { diff --git a/matrix-authentication-service/src/handlers/oauth2/token.rs b/matrix-authentication-service/src/handlers/oauth2/token.rs index 24376df7a..c630d6704 100644 --- a/matrix-authentication-service/src/handlers/oauth2/token.rs +++ b/matrix-authentication-service/src/handlers/oauth2/token.rs @@ -14,6 +14,7 @@ use anyhow::Context; use chrono::Duration; +use data_encoding::BASE64URL_NOPAD; use jwt_compact::{Claims, Header, TimeOptions}; use oauth2_types::{ errors::{InvalidGrant, OAuth2Error}, @@ -23,6 +24,8 @@ use oauth2_types::{ }; use rand::thread_rng; use serde::Serialize; +use serde_with::skip_serializing_none; +use sha2::{Digest, Sha256}; use sqlx::{pool::PoolConnection, Acquire, PgPool, Postgres}; use url::Url; use warp::{Filter, Rejection, Reply}; @@ -43,6 +46,7 @@ use crate::{ tokens, }; +#[skip_serializing_none] #[derive(Serialize, Debug)] struct CustomClaims { #[serde(rename = "iss")] @@ -51,6 +55,9 @@ struct CustomClaims { subject: String, #[serde(rename = "aud")] audiences: Vec, + nonce: Option, + at_hash: String, + c_hash: String, } pub fn filter( @@ -93,6 +100,16 @@ async fn token( Ok(reply) } +fn hash(mut hasher: H, token: &str) -> anyhow::Result { + hasher.update(token); + let hash = hasher.finalize(); + // Left-most 128bit + let bits = hash + .get(..16) + .context("failed to get first 128 bits of hash")?; + Ok(BASE64URL_NOPAD.encode(bits)) +} + async fn authorization_code_grant( grant: &AuthorizationCodeGrant, client: &OAuth2ClientConfig, @@ -138,6 +155,9 @@ async fn authorization_code_grant( // TODO: get that from the session subject: "random-subject".to_string(), audiences: vec![client.client_id.clone()], + nonce: code.nonce, + at_hash: hash(Sha256::new(), &access_token.token).wrap_error()?, + c_hash: hash(Sha256::new(), &grant.code).wrap_error()?, }) .set_duration_and_issuance(&options, Duration::minutes(30)); let id_token = keys diff --git a/matrix-authentication-service/src/storage/oauth2/authorization_code.rs b/matrix-authentication-service/src/storage/oauth2/authorization_code.rs index 29cfe0a11..4ab8a3481 100644 --- a/matrix-authentication-service/src/storage/oauth2/authorization_code.rs +++ b/matrix-authentication-service/src/storage/oauth2/authorization_code.rs @@ -62,6 +62,7 @@ pub struct OAuth2CodeLookup { pub client_id: String, pub redirect_uri: String, pub scope: String, + pub nonce: Option, } pub async fn lookup_code( @@ -76,7 +77,8 @@ pub async fn lookup_code( os.id AS "oauth2_session_id!", os.client_id AS "client_id!", os.redirect_uri, - os.scope AS "scope!" + os.scope AS "scope!", + os.nonce FROM oauth2_codes oc INNER JOIN oauth2_sessions os ON os.id = oc.oauth2_session_id