hessra_token/
verify.rs

1extern crate biscuit_auth as biscuit;
2
3use biscuit::macros::{authorizer, check};
4use biscuit::{Algorithm, Biscuit, PublicKey};
5use chrono::Utc;
6use serde::Deserialize;
7
8use crate::error::TokenError;
9
10fn build_base_authorizer(
11    subject: String,
12    resource: String,
13) -> Result<biscuit::AuthorizerBuilder, TokenError> {
14    let now = Utc::now().timestamp();
15
16    let authz = authorizer!(
17        r#"
18            time({now});
19            resource({resource});
20            subject({subject});
21            operation("read");
22            operation("write");
23            allow if subject($sub), resource($res), operation($op), right($sub, $res, $op);
24        "#
25    );
26    Ok(authz)
27}
28
29/// Verifies a Biscuit authorization token locally without contacting the authorization server.
30///
31/// This function performs local verification of a Biscuit token using the provided public key.
32/// It validates that the token grants access to the specified resource for the given subject.
33///
34/// # Arguments
35///
36/// * `token` - The binary Biscuit token bytes (typically decoded from Base64)
37/// * `public_key` - The public key used to verify the token signature
38/// * `subject` - The subject (user) identifier to verify authorization for
39/// * `resource` - The resource identifier to verify authorization against
40///
41/// # Returns
42///
43/// * `Ok(())` - If the token is valid and grants access to the resource
44/// * `Err(TokenError)` - If verification fails for any reason
45///
46/// # Errors
47///
48/// Returns an error if:
49/// - The token is malformed or cannot be parsed
50/// - The token signature is invalid
51/// - The token does not grant the required access rights
52/// - The token has expired or other authorization checks fail
53pub fn verify_biscuit_local(
54    token: Vec<u8>,
55    public_key: PublicKey,
56    subject: String,
57    resource: String,
58) -> Result<(), TokenError> {
59    let biscuit = Biscuit::from(&token, public_key)?;
60
61    let authz = build_base_authorizer(subject, resource)?;
62    if authz.build(&biscuit)?.authorize().is_ok() {
63        Ok(())
64    } else {
65        Err(TokenError::authorization_error(
66            "Token does not grant required access rights",
67        ))
68    }
69}
70
71/// Takes a public key encoded as a string in the format "ed25519/..." or "secp256r1/..."
72/// and returns a PublicKey.
73pub fn biscuit_key_from_string(key: String) -> Result<PublicKey, TokenError> {
74    println!("Key: {:?}", key);
75    // first split the string on the /
76    let parts = key.split('/').collect::<Vec<&str>>();
77    if parts.len() != 2 {
78        return Err(TokenError::invalid_key_format(
79            "Key must be in format 'algorithm/hexkey'",
80        ));
81    }
82
83    let alg = match parts[0] {
84        "ed25519" => Algorithm::Ed25519,
85        "secp256r1" => Algorithm::Secp256r1,
86        _ => {
87            return Err(TokenError::invalid_key_format(
88                "Unsupported algorithm, must be ed25519 or secp256r1",
89            ))
90        }
91    };
92
93    // decode the key from hex
94    let key_bytes = hex::decode(parts[1])?;
95
96    // construct the public key
97    let key = PublicKey::from_bytes(&key_bytes, alg)
98        .map_err(|e| TokenError::invalid_key_format(e.to_string()))?;
99
100    Ok(key)
101}
102
103#[derive(Debug, Deserialize, Clone)]
104pub struct ServiceNode {
105    pub component: String,
106    pub public_key: String,
107}
108
109pub fn verify_service_chain_biscuit_local(
110    token: Vec<u8>,
111    public_key: PublicKey,
112    subject: String,
113    resource: String,
114    service_nodes: Vec<ServiceNode>,
115    component: Option<String>,
116) -> Result<(), TokenError> {
117    let biscuit = Biscuit::from(&token, public_key).map_err(TokenError::biscuit_error)?;
118
119    let mut authz = build_base_authorizer(subject, resource.clone())?;
120
121    for service_node in service_nodes {
122        if let Some(ref component) = component {
123            if component == &service_node.component {
124                break;
125            }
126        }
127        let service = resource.clone();
128        let node_name = service_node.component;
129        let node_key = biscuit_key_from_string(service_node.public_key)?;
130        authz = authz.check(check!(
131            r#"
132                check if node({service}, {node_name}) trusting authority, {node_key};
133            "#
134        ))?;
135    }
136
137    if authz.build(&biscuit)?.authorize().is_ok() {
138        Ok(())
139    } else {
140        Err(TokenError::authorization_error(
141            "Token does not grant required access rights",
142        ))
143    }
144}