1use candid::Principal;
2use ecdsa::signature::Verifier;
3use ic_certification::{Certificate, LookupResult};
4use k256::Secp256k1;
5use p256::NistP256;
6use pkcs8::der::{Decode, SliceReader};
7use pkcs8::{spki::SubjectPublicKeyInfoRef, AssociatedOid, ObjectIdentifier};
8use sec1::{EcParameters, EncodedPoint};
9use sha2::{Digest, Sha256};
10
11use crate::{
12 agent::{
13 response_authentication::{lookup_canister_ranges, DER_PREFIX, KEY_LENGTH},
14 EnvelopeContent, IC_ROOT_KEY, IC_STATE_ROOT_DOMAIN_SEPARATOR,
15 },
16 Signature,
17};
18
19use super::{error::DelegationError, Delegation, Identity, SignedDelegation};
20
21pub(crate) const CANISTER_SIG_OID: ObjectIdentifier =
23 ObjectIdentifier::new_unwrap("1.3.6.1.4.1.56387.1.2");
24
25#[derive(serde::Deserialize)]
30struct CanisterSig {
31 #[serde(with = "serde_bytes")]
32 certificate: Vec<u8>,
33 tree: ic_certification::HashTree,
34}
35
36pub struct DelegatedIdentity {
38 to: Box<dyn Identity>,
39 chain: Vec<SignedDelegation>,
40 from_key: Vec<u8>,
41}
42
43impl DelegatedIdentity {
44 pub fn new(
53 from_key: Vec<u8>,
54 to: Box<dyn Identity>,
55 chain: Vec<SignedDelegation>,
56 ) -> Result<Self, DelegationError> {
57 Self::new_impl(from_key, to, chain, IC_ROOT_KEY)
58 }
59
60 pub fn new_with_root_key(
68 from_key: Vec<u8>,
69 to: Box<dyn Identity>,
70 chain: Vec<SignedDelegation>,
71 root_key: &[u8],
72 ) -> Result<Self, DelegationError> {
73 Self::new_impl(from_key, to, chain, root_key)
74 }
75
76 pub fn new_unchecked(
81 from_key: Vec<u8>,
82 to: Box<dyn Identity>,
83 chain: Vec<SignedDelegation>,
84 ) -> Self {
85 Self {
86 to,
87 chain,
88 from_key,
89 }
90 }
91
92 fn new_impl(
93 from_key: Vec<u8>,
94 to: Box<dyn Identity>,
95 chain: Vec<SignedDelegation>,
96 root_key: &[u8],
97 ) -> Result<Self, DelegationError> {
98 let mut last_verified = &from_key;
99 for delegation in &chain {
100 verify_delegation_link(
101 last_verified,
102 &delegation.delegation,
103 &delegation.signature,
104 root_key,
105 )?;
106 last_verified = &delegation.delegation.pubkey;
107 }
108 let delegated_principal = Principal::self_authenticating(last_verified);
109 if delegated_principal != to.sender().map_err(DelegationError::IdentityError)? {
110 return Err(DelegationError::BrokenChain {
111 from: last_verified.clone(),
112 to: None,
113 });
114 }
115
116 Ok(Self::new_unchecked(from_key, to, chain))
117 }
118
119 fn chain_signature(&self, mut sig: Signature) -> Signature {
120 sig.public_key = self.public_key();
121 sig.delegations
122 .get_or_insert(vec![])
123 .extend(self.chain.iter().cloned());
124 sig
125 }
126}
127
128impl Identity for DelegatedIdentity {
129 fn sender(&self) -> Result<Principal, String> {
130 Ok(Principal::self_authenticating(&self.from_key))
131 }
132 fn public_key(&self) -> Option<Vec<u8>> {
133 Some(self.from_key.clone())
134 }
135 fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String> {
136 self.to.sign(content).map(|sig| self.chain_signature(sig))
137 }
138 fn sign_delegation(&self, content: &Delegation) -> Result<Signature, String> {
139 self.to
140 .sign_delegation(content)
141 .map(|sig| self.chain_signature(sig))
142 }
143 fn sign_arbitrary(&self, content: &[u8]) -> Result<Signature, String> {
144 self.to
145 .sign_arbitrary(content)
146 .map(|sig| self.chain_signature(sig))
147 }
148 fn delegation_chain(&self) -> Vec<SignedDelegation> {
149 let mut chain = self.to.delegation_chain();
150 chain.extend(self.chain.iter().cloned());
151 chain
152 }
153 fn sender_info(&self) -> Option<ic_transport_types::SenderInfo> {
154 self.to.sender_info()
155 }
156}
157
158fn verify_delegation_link(
160 from_key: &[u8],
161 delegation: &ic_transport_types::Delegation,
162 signature: &[u8],
163 root_key: &[u8],
164) -> Result<(), DelegationError> {
165 let spki = SubjectPublicKeyInfoRef::decode(
166 &mut SliceReader::new(from_key).map_err(|_| DelegationError::Parse)?,
167 )
168 .map_err(|_| DelegationError::Parse)?;
169
170 let payload = delegation.signable();
171
172 if spki.algorithm.oid == elliptic_curve::ALGORITHM_OID {
173 let Some(params) = spki.algorithm.parameters else {
174 return Err(DelegationError::UnknownAlgorithm);
175 };
176 let params = params
177 .decode_as::<EcParameters>()
178 .map_err(|_| DelegationError::Parse)?;
179 let curve = params
180 .named_curve()
181 .ok_or(DelegationError::UnknownAlgorithm)?;
182 if curve == Secp256k1::OID {
183 let pt = EncodedPoint::from_bytes(spki.subject_public_key.raw_bytes())
184 .map_err(|_| DelegationError::Parse)?;
185 let vk = k256::ecdsa::VerifyingKey::from_encoded_point(&pt)
186 .map_err(|_| DelegationError::Parse)?;
187 let sig =
188 k256::ecdsa::Signature::try_from(signature).map_err(|_| DelegationError::Parse)?;
189 vk.verify(&payload, &sig)
190 .map_err(|_| DelegationError::BrokenChain {
191 from: from_key.to_vec(),
192 to: Some(delegation.clone()),
193 })?;
194 } else if curve == NistP256::OID {
195 let pt = EncodedPoint::from_bytes(spki.subject_public_key.raw_bytes())
196 .map_err(|_| DelegationError::Parse)?;
197 let vk = p256::ecdsa::VerifyingKey::from_encoded_point(&pt)
198 .map_err(|_| DelegationError::Parse)?;
199 let sig =
200 p256::ecdsa::Signature::try_from(signature).map_err(|_| DelegationError::Parse)?;
201 vk.verify(&payload, &sig)
202 .map_err(|_| DelegationError::BrokenChain {
203 from: from_key.to_vec(),
204 to: Some(delegation.clone()),
205 })?;
206 } else {
207 return Err(DelegationError::UnknownAlgorithm);
208 }
209 } else if spki.algorithm.oid == ObjectIdentifier::new_unwrap("1.3.101.112") {
210 let vk = ic_ed25519::PublicKey::deserialize_raw(spki.subject_public_key.raw_bytes())
211 .map_err(|_| DelegationError::Parse)?;
212 vk.verify_signature(&payload, signature)
213 .map_err(|_| DelegationError::BrokenChain {
214 from: from_key.to_vec(),
215 to: Some(delegation.clone()),
216 })?;
217 } else if spki.algorithm.oid == CANISTER_SIG_OID {
218 let (signing_canister_id, seed) =
219 parse_canister_sig_pubkey(spki.subject_public_key.raw_bytes())?;
220 verify_canister_sig(&payload, signature, signing_canister_id, &seed, root_key)?;
221 } else {
222 return Err(DelegationError::UnknownAlgorithm);
223 }
224
225 Ok(())
226}
227
228pub(crate) fn parse_canister_sig_pubkey(
233 raw: &[u8],
234) -> Result<(Principal, Vec<u8>), DelegationError> {
235 if raw.is_empty() {
236 return Err(DelegationError::Parse);
237 }
238 let id_len = raw[0] as usize;
239 if raw.len() < 1 + id_len {
240 return Err(DelegationError::Parse);
241 }
242 let canister_id =
243 Principal::try_from_slice(&raw[1..1 + id_len]).map_err(|_| DelegationError::Parse)?;
244 let seed = raw[1 + id_len..].to_vec();
245 Ok((canister_id, seed))
246}
247
248fn extract_bls_key(der: &[u8]) -> Result<&[u8], DelegationError> {
250 if der.len() != DER_PREFIX.len() + KEY_LENGTH {
251 return Err(DelegationError::InvalidCanisterSignature(
252 "invalid BLS public key DER encoding: wrong length".into(),
253 ));
254 }
255 if &der[..DER_PREFIX.len()] != DER_PREFIX.as_ref() {
256 return Err(DelegationError::InvalidCanisterSignature(
257 "invalid BLS public key DER encoding: wrong prefix".into(),
258 ));
259 }
260 Ok(&der[DER_PREFIX.len()..])
261}
262
263fn verify_cert_bls(cert: &Certificate, raw_bls_key: &[u8]) -> Result<(), DelegationError> {
265 let root_hash = cert.tree.digest();
266 let mut msg = Vec::with_capacity(IC_STATE_ROOT_DOMAIN_SEPARATOR.len() + 32);
267 msg.extend_from_slice(IC_STATE_ROOT_DOMAIN_SEPARATOR);
268 msg.extend_from_slice(&root_hash);
269 ic_verify_bls_signature::verify_bls_signature(&cert.signature, &msg, raw_bls_key)
270 .map_err(|_| DelegationError::InvalidCanisterSignature("BLS verification failed".into()))
271}
272
273fn resolve_cert_key(
276 cert: &Certificate,
277 signing_canister_id: Principal,
278 root_key_der: &[u8],
279) -> Result<Vec<u8>, DelegationError> {
280 let delegation = match &cert.delegation {
281 None => return Ok(root_key_der.to_vec()),
282 Some(d) => d,
283 };
284
285 let delegation_cert: Certificate =
287 serde_cbor::from_slice(&delegation.certificate).map_err(|e| {
288 DelegationError::InvalidCanisterSignature(format!(
289 "invalid delegation certificate CBOR: {e}"
290 ))
291 })?;
292 if delegation_cert.delegation.is_some() {
293 return Err(DelegationError::InvalidCanisterSignature(
294 "nested delegations in certificate are not allowed".into(),
295 ));
296 }
297
298 let raw_root_key = extract_bls_key(root_key_der)?;
300 verify_cert_bls(&delegation_cert, raw_root_key)?;
301
302 let subnet_id = Principal::try_from_slice(&delegation.subnet_id).map_err(|_| {
304 DelegationError::InvalidCanisterSignature("invalid subnet_id in delegation".into())
305 })?;
306 let ranges = lookup_canister_ranges(&subnet_id, &delegation_cert).map_err(|e| {
307 DelegationError::InvalidCanisterSignature(format!("canister range lookup failed: {e}"))
308 })?;
309 if !ranges.contains(&signing_canister_id) {
310 return Err(DelegationError::InvalidCanisterSignature(
311 "signing canister is not within the delegation's authorised canister ranges".into(),
312 ));
313 }
314
315 let pk_path: [&[u8]; 3] = [b"subnet", &delegation.subnet_id, b"public_key"];
317 match delegation_cert.tree.lookup_path(pk_path) {
318 LookupResult::Found(pk) => Ok(pk.to_vec()),
319 _ => Err(DelegationError::InvalidCanisterSignature(
320 "subnet public key not found in delegation certificate".into(),
321 )),
322 }
323}
324
325pub(crate) fn verify_canister_sig(
332 payload: &[u8],
333 sig_bytes: &[u8],
334 signing_canister_id: Principal,
335 seed: &[u8],
336 root_key_der: &[u8],
337) -> Result<(), DelegationError> {
338 let canister_sig: CanisterSig = serde_cbor::from_slice(sig_bytes).map_err(|e| {
341 DelegationError::InvalidCanisterSignature(format!("invalid canister signature CBOR: {e}"))
342 })?;
343
344 let cert: Certificate = serde_cbor::from_slice(&canister_sig.certificate).map_err(|e| {
346 DelegationError::InvalidCanisterSignature(format!("invalid certificate CBOR: {e}"))
347 })?;
348
349 let verification_key_der = resolve_cert_key(&cert, signing_canister_id, root_key_der)?;
351 let raw_bls_key = extract_bls_key(&verification_key_der)?;
352 verify_cert_bls(&cert, raw_bls_key)?;
353
354 let tree_root_hash = canister_sig.tree.digest();
357 let certified_data_path: [&[u8]; 3] = [
358 b"canister",
359 signing_canister_id.as_slice(),
360 b"certified_data",
361 ];
362 let certified_data = match cert.tree.lookup_path(certified_data_path) {
363 LookupResult::Found(v) => v,
364 LookupResult::Absent => {
365 return Err(DelegationError::InvalidCanisterSignature(
366 "certified_data is absent from the certificate tree".into(),
367 ))
368 }
369 _ => {
370 return Err(DelegationError::InvalidCanisterSignature(
371 "certified_data lookup returned Unknown or Error".into(),
372 ))
373 }
374 };
375 if certified_data != tree_root_hash.as_ref() {
376 return Err(DelegationError::InvalidCanisterSignature(
377 "certified_data does not match the signature tree root hash".into(),
378 ));
379 }
380
381 let seed_hash: [u8; 32] = Sha256::digest(seed).into();
383 let payload_hash: [u8; 32] = Sha256::digest(payload).into();
384 let sig_path: [&[u8]; 3] = [b"sig", &seed_hash, &payload_hash];
385 match canister_sig.tree.lookup_path(sig_path) {
386 LookupResult::Found([]) => Ok(()),
387 LookupResult::Found(_) => Err(DelegationError::InvalidCanisterSignature(
388 "sig leaf in the signature tree is not empty".into(),
389 )),
390 _ => Err(DelegationError::InvalidCanisterSignature(
391 "sig not found in the signature tree".into(),
392 )),
393 }
394}