Skip to main content

ic_auth/portable/
verified.rs

1#[cfg(any(
2    feature = "portable-signature-verification",
3    feature = "certified-proof-verification"
4))]
5use super::PortableClaimsContext;
6use crate::session_request::{SessionRequest, SessionRequestError, SessionTtlPolicy};
7use ic_auth_protocol_types::{AuthRole, PortableClaims};
8
9/// Claims authenticated by one complete portable proof verifier at a host clock.
10/// Read-only and not deserializable; decoding or editing claims cannot forge this
11/// result. It is not a session, enrollment approval or resource authorization.
12/// Consume it under the same host transaction/authority snapshot as verification;
13/// matching time alone does not establish that authority is still current.
14///
15/// ```compile_fail
16/// use ic_auth::portable::VerifiedPortableClaims;
17/// use ic_auth_protocol_types::PortableClaims;
18/// fn forge(claims: PortableClaims) -> VerifiedPortableClaims { claims.into() }
19/// ```
20#[derive(Clone, Debug, Eq, PartialEq)]
21pub struct VerifiedPortableClaims {
22    claims: PortableClaims,
23    fingerprint: [u8; 32],
24    verified_at_ns: u64,
25    authority_expires_at_ns: u64,
26    enrollment_expires_at_ns: u64,
27}
28
29impl VerifiedPortableClaims {
30    #[cfg(any(
31        feature = "portable-signature-verification",
32        feature = "certified-proof-verification"
33    ))]
34    pub(crate) fn new(
35        claims: PortableClaims,
36        fingerprint: [u8; 32],
37        context: &PortableClaimsContext<'_>,
38    ) -> Self {
39        Self {
40            claims,
41            fingerprint,
42            verified_at_ns: context.authority.now_ns,
43            authority_expires_at_ns: context.authority.authority_expires_at_ns,
44            enrollment_expires_at_ns: context.authority.enrollment_expires_at_ns,
45        }
46    }
47
48    /// Exclusive lease deadline for admission at this verification snapshot.
49    /// Proof expiry limits new admission, not the resulting lease. The signed
50    /// login deadline and protected key/enrollment deadlines cap the lease.
51    /// Persist this result once; refreshing enrollment never extends stored leases.
52    /// The role comes from protected receiver configuration, never the request.
53    /// Requested scopes must narrow that role's grant; other roles cannot supply
54    /// missing scopes. This does not check replay, revocation or current authority
55    /// after verification, and does not create or authorize a session.
56    pub fn session_expires_at_ns(
57        &self,
58        request: &SessionRequest,
59        role: &AuthRole,
60        policy: SessionTtlPolicy,
61    ) -> Result<u64, SessionRequestError> {
62        let requested = request.evaluate(&self.claims.grants, role, self.verified_at_ns, policy)?;
63        Ok(requested
64            .min(self.claims.login_deadline_ns)
65            .min(self.authority_expires_at_ns)
66            .min(self.enrollment_expires_at_ns))
67    }
68
69    pub fn claims(&self) -> &PortableClaims {
70        &self.claims
71    }
72
73    /// Canonical claims digest, independent of alternate proof/signature bytes.
74    pub fn fingerprint(&self) -> [u8; 32] {
75        self.fingerprint
76    }
77
78    pub fn verified_at_ns(&self) -> u64 {
79        self.verified_at_ns
80    }
81}