Skip to main content

ic_agent/identity/
info_aware.rs

1use candid::Principal;
2use pkcs8::{
3    der::{Decode, SliceReader},
4    spki::SubjectPublicKeyInfoRef,
5};
6
7use ic_transport_types::SenderInfo;
8
9use crate::{
10    agent::{EnvelopeContent, IC_ROOT_KEY},
11    Signature,
12};
13
14use super::{
15    delegated::{parse_canister_sig_pubkey, verify_canister_sig, CANISTER_SIG_OID},
16    error::DelegationError,
17    Delegation, Identity, SignedDelegation,
18};
19
20// Domain separator per IC interface spec §Identity attributes: \x0E (14) + "ic-sender-info"
21const SENDER_INFO_DOMAIN_SEP: &[u8; 15] = b"\x0Eic-sender-info";
22
23/// An identity wrapper that attaches canister-certified sender information to every request.
24///
25/// The inner identity's public key must be a canister signature key (OID 1.3.6.1.4.1.56387.1.2).
26/// `sig` must be a canister signature over `\x0Eic-sender-info || info` using that same key.
27pub struct InfoAwareIdentity<I: Identity> {
28    inner: I,
29    info: Vec<u8>,
30    signer: Principal,
31    sig: Vec<u8>,
32}
33
34impl<I: Identity> InfoAwareIdentity<I> {
35    /// Wraps `inner` and attaches `info`/`sig` as certified sender information.
36    ///
37    /// Verifies the canister signature against the IC mainnet root key.
38    /// `inner` must have a canister signature public key.
39    pub fn new(inner: I, info: Vec<u8>, sig: Vec<u8>) -> Result<Self, DelegationError> {
40        Self::new_impl(inner, info, sig, IC_ROOT_KEY)
41    }
42
43    /// Like [`new`](Self::new), but verifies against a custom root key (e.g. local replica or testnet).
44    pub fn new_with_root_key(
45        inner: I,
46        info: Vec<u8>,
47        sig: Vec<u8>,
48        root_key: &[u8],
49    ) -> Result<Self, DelegationError> {
50        Self::new_impl(inner, info, sig, root_key)
51    }
52
53    /// Like [`new`](Self::new), but skips cryptographic verification of the canister signature.
54    ///
55    /// The replica will still reject an invalid signature. The signer principal is still parsed
56    /// from the inner identity's public key.
57    pub fn new_unchecked(inner: I, info: Vec<u8>, sig: Vec<u8>) -> Result<Self, DelegationError> {
58        let (signer, _seed) = parse_canister_pubkey(&inner)?;
59        Ok(Self {
60            inner,
61            info,
62            signer,
63            sig,
64        })
65    }
66
67    fn new_impl(
68        inner: I,
69        info: Vec<u8>,
70        sig: Vec<u8>,
71        root_key: &[u8],
72    ) -> Result<Self, DelegationError> {
73        let (signer, seed) = parse_canister_pubkey(&inner)?;
74        let mut payload = Vec::with_capacity(SENDER_INFO_DOMAIN_SEP.len() + info.len());
75        payload.extend_from_slice(SENDER_INFO_DOMAIN_SEP);
76        payload.extend_from_slice(&info);
77        verify_canister_sig(&payload, &sig, signer, &seed, root_key)?;
78        Ok(Self {
79            inner,
80            info,
81            signer,
82            sig,
83        })
84    }
85}
86
87/// Parse the inner identity's public key as a canister sig SPKI, returning (canister_id, seed).
88fn parse_canister_pubkey<I: Identity>(inner: &I) -> Result<(Principal, Vec<u8>), DelegationError> {
89    let pubkey = inner.public_key().ok_or(DelegationError::Parse)?;
90    let spki = SubjectPublicKeyInfoRef::decode(
91        &mut SliceReader::new(&pubkey).map_err(|_| DelegationError::Parse)?,
92    )
93    .map_err(|_| DelegationError::Parse)?;
94    if spki.algorithm.oid != CANISTER_SIG_OID {
95        return Err(DelegationError::UnknownAlgorithm);
96    }
97    parse_canister_sig_pubkey(spki.subject_public_key.raw_bytes())
98}
99
100impl<I: Identity> Identity for InfoAwareIdentity<I> {
101    fn sender(&self) -> Result<Principal, String> {
102        self.inner.sender()
103    }
104
105    fn public_key(&self) -> Option<Vec<u8>> {
106        self.inner.public_key()
107    }
108
109    fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String> {
110        self.inner.sign(content)
111    }
112
113    fn sign_delegation(&self, content: &Delegation) -> Result<Signature, String> {
114        self.inner.sign_delegation(content)
115    }
116
117    fn sign_arbitrary(&self, content: &[u8]) -> Result<Signature, String> {
118        self.inner.sign_arbitrary(content)
119    }
120
121    fn delegation_chain(&self) -> Vec<SignedDelegation> {
122        self.inner.delegation_chain()
123    }
124
125    fn sender_info(&self) -> Option<SenderInfo> {
126        Some(SenderInfo {
127            info: self.info.clone(),
128            signer: self.signer.as_slice().to_vec(),
129            sig: self.sig.clone(),
130        })
131    }
132}