Skip to main content

ic_agent/identity/
mod.rs

1//! Types and traits dealing with identity across the Internet Computer.
2use std::sync::Arc;
3
4use crate::{agent::EnvelopeContent, export::Principal};
5
6pub(crate) mod anonymous;
7pub(crate) mod basic;
8pub(crate) mod delegated;
9pub(crate) mod error;
10pub(crate) mod info_aware;
11pub(crate) mod prime256v1;
12pub(crate) mod secp256k1;
13
14#[doc(inline)]
15pub use anonymous::AnonymousIdentity;
16#[doc(inline)]
17pub use basic::BasicIdentity;
18#[doc(inline)]
19pub use delegated::DelegatedIdentity;
20#[doc(inline)]
21pub use error::DelegationError;
22#[doc(inline)]
23pub use ic_transport_types::{Delegation, SenderInfo, SignedDelegation};
24#[doc(inline)]
25pub use info_aware::InfoAwareIdentity;
26#[doc(inline)]
27pub use prime256v1::Prime256v1Identity;
28#[doc(inline)]
29pub use secp256k1::Secp256k1Identity;
30
31#[cfg(feature = "pem")]
32#[doc(inline)]
33pub use error::PemError;
34
35/// A cryptographic signature, signed by an [Identity].
36#[derive(Clone, Debug)]
37pub struct Signature {
38    /// This is the DER-encoded public key.
39    pub public_key: Option<Vec<u8>>,
40    /// The signature bytes.
41    pub signature: Option<Vec<u8>>,
42    /// A list of delegations connecting `public_key` to the key that signed `signature`, and in that order.
43    pub delegations: Option<Vec<SignedDelegation>>,
44}
45
46/// An `Identity` produces [`Signatures`](Signature) for requests or delegations. It knows or
47/// represents the [`Principal`] of the sender.
48///
49/// [`Agents`](crate::Agent) are assigned a single `Identity` object, but there can be multiple
50/// identities used.
51pub trait Identity: Send + Sync {
52    /// Returns a sender, ie. the Principal ID that is used to sign a request.
53    ///
54    /// Only one sender can be used per request.
55    fn sender(&self) -> Result<Principal, String>;
56
57    /// Produce the public key commonly returned in [`Signature`].
58    ///
59    /// Should only return `None` if `sign` would do the same.
60    fn public_key(&self) -> Option<Vec<u8>>;
61
62    /// Sign a request ID derived from a content map.
63    ///
64    /// Implementors should call `content.to_request_id().signable()` for the actual bytes that need to be signed.
65    fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String>;
66
67    /// Sign a delegation to let another key be used to authenticate [`sender`](Identity::sender).
68    ///
69    /// Not all `Identity` implementations support this operation, though all `ic-agent` implementations other than `AnonymousIdentity` do.
70    ///
71    /// Implementors should call `content.signable()` for the actual bytes that need to be signed.
72    fn sign_delegation(&self, content: &Delegation) -> Result<Signature, String> {
73        let _ = content; // silence unused warning
74        Err(String::from("unsupported"))
75    }
76
77    /// Sign arbitrary bytes.
78    ///
79    /// Not all `Identity` implementations support this operation, though all `ic-agent` implementations do.
80    fn sign_arbitrary(&self, content: &[u8]) -> Result<Signature, String> {
81        let _ = content; // silence unused warning
82        Err(String::from("unsupported"))
83    }
84
85    /// A list of signed delegations connecting [`sender`](Identity::sender)
86    /// to [`public_key`](Identity::public_key), and in that order.
87    fn delegation_chain(&self) -> Vec<SignedDelegation> {
88        vec![]
89    }
90
91    /// Returns canister-certified sender information to include in the request envelope, or `None`.
92    ///
93    /// See [`InfoAwareIdentity`].
94    fn sender_info(&self) -> Option<SenderInfo> {
95        None
96    }
97}
98
99macro_rules! delegating_impl {
100    ($implementor:ty, $name:ident => $self_expr:expr) => {
101        impl Identity for $implementor {
102            fn sender(&$name) -> Result<Principal, String> {
103                $self_expr.sender()
104            }
105
106            fn public_key(&$name) -> Option<Vec<u8>> {
107                $self_expr.public_key()
108            }
109
110            fn sign(&$name, content: &EnvelopeContent) -> Result<Signature, String> {
111                $self_expr.sign(content)
112            }
113
114            fn sign_delegation(&$name, content: &Delegation) -> Result<Signature, String> {
115                $self_expr.sign_delegation(content)
116            }
117
118            fn sign_arbitrary(&$name, content: &[u8]) -> Result<Signature, String> {
119                $self_expr.sign_arbitrary(content)
120            }
121
122            fn delegation_chain(&$name) -> Vec<SignedDelegation> {
123                $self_expr.delegation_chain()
124            }
125
126            fn sender_info(&$name) -> Option<SenderInfo> {
127                $self_expr.sender_info()
128            }
129        }
130    };
131}
132
133delegating_impl!(Box<dyn Identity>, self => **self);
134delegating_impl!(Arc<dyn Identity>, self => **self);
135delegating_impl!(&dyn Identity, self => *self);
136
137/// Parses a PKCS#8 ("PRIVATE KEY") EC private key from raw DER bytes.
138///
139/// Validates that the algorithm OID is EC and that the curve OID matches `expected_curve`,
140/// then returns the inner SEC1 private key bytes. Applies the dfx legacy hatchet surgery for
141/// nonconforming containers if needed.
142#[cfg(feature = "pem")]
143fn parse_ec_pkcs8_key_bytes(
144    der_bytes: &[u8],
145    expected_curve: pkcs8::der::asn1::ObjectIdentifier,
146    curve_name: &str,
147) -> Result<Vec<u8>, error::PemError> {
148    use pkcs8::{
149        der::{Decode, Encode},
150        PrivateKeyInfo,
151    };
152
153    let mut truncated: Vec<u8>;
154    let pki = match PrivateKeyInfo::from_der(der_bytes) {
155        Ok(pki) => pki,
156        Err(e) => {
157            // Very old versions of dfx generated nonconforming PKCS#8 containers.
158            // This code was copied from agent-rs@1e67be03 via icp-cli.
159            truncated = der_bytes.to_vec();
160            if truncated.len() >= 52 && truncated[48..52] == *b"\xA1\x23\x03\x21" {
161                truncated.truncate(48);
162                truncated[1] = 46;
163                truncated[4] = 0;
164                PrivateKeyInfo::from_der(&truncated).map_err(|_| e)?
165            } else {
166                return Err(e.into());
167            }
168        }
169    };
170    if pki.algorithm.oid != elliptic_curve::ALGORITHM_OID {
171        return Err(error::PemError::InvalidPrivateKey(format!(
172            "expected EC algorithm OID {}, found {}",
173            elliptic_curve::ALGORITHM_OID,
174            pki.algorithm.oid,
175        )));
176    }
177    let curve_oid = pki
178        .algorithm
179        .parameters_oid()
180        .map_err(|_| pkcs8::Error::KeyMalformed)?;
181    if curve_oid != expected_curve {
182        return Err(error::PemError::UnsupportedKeyCurve(
183            curve_name.to_string(),
184            curve_oid.to_der().unwrap_or_default(),
185        ));
186    }
187    Ok(pki.private_key.to_vec())
188}