ic_agent/identity/
basic.rs1use crate::{agent::EnvelopeContent, export::Principal, Identity, Signature};
2
3#[cfg(feature = "pem")]
4use crate::identity::error::PemError;
5
6use ic_ed25519::PrivateKey;
7
8use std::fmt;
9
10use super::Delegation;
11
12pub struct BasicIdentity {
16 private_key: KeyCompat,
17 der_encoded_public_key: Vec<u8>,
18}
19
20impl fmt::Debug for BasicIdentity {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 f.debug_struct("BasicIdentity")
23 .field("der_encoded_public_key", &self.der_encoded_public_key)
24 .finish_non_exhaustive()
25 }
26}
27
28impl BasicIdentity {
29 #[cfg(feature = "pem")]
31 pub fn from_pem_file<P: AsRef<std::path::Path>>(file_path: P) -> Result<Self, PemError> {
32 Self::from_pem(std::fs::File::open(file_path)?)
33 }
34
35 #[cfg(feature = "pem")]
37 pub fn from_pem<R: std::io::Read>(pem_reader: R) -> Result<Self, PemError> {
38 use der::{asn1::OctetString, Decode, ErrorKind, SliceReader, Tag, TagNumber};
39 use pkcs8::PrivateKeyInfo;
40
41 let bytes: Vec<u8> = pem_reader.bytes().collect::<Result<_, _>>()?;
42 let pem = pem::parse(bytes)?;
43 let pki_res = PrivateKeyInfo::decode(&mut SliceReader::new(pem.contents())?);
44 let mut truncated;
45 let pki = match pki_res {
46 Ok(pki) => pki,
47 Err(e) => {
48 if e.kind()
49 == (ErrorKind::Noncanonical {
50 tag: Tag::ContextSpecific {
51 constructed: true,
52 number: TagNumber::new(1),
53 },
54 })
55 {
56 truncated = pem.into_contents();
58 if truncated[48..52] != *b"\xA1\x23\x03\x21" {
59 return Err(e.into());
60 }
61 truncated.truncate(48);
63 truncated[1] = 46;
64 truncated[4] = 0;
65 PrivateKeyInfo::decode(&mut SliceReader::new(&truncated)?).map_err(|_| e)?
66 } else {
67 return Err(e.into());
68 }
69 }
70 };
71 let decoded_key = OctetString::from_der(pki.private_key)?; let key_len = decoded_key.as_bytes().len();
73 if key_len != 32 {
74 Err(PemError::InvalidPrivateKey(format!(
75 "Ed25519 expects a 32 octets private key, but got {key_len} octets",
76 )))
77 } else {
78 let raw_key: [u8; 32] = decoded_key.as_bytes().try_into().unwrap();
79 Ok(Self::from_raw_key(&raw_key))
80 }
81 }
82
83 pub fn from_raw_key(key: &[u8; 32]) -> Self {
85 let private_key = PrivateKey::deserialize_raw_32(key);
86 let public_key = private_key.public_key();
87 let der_encoded_public_key = public_key.serialize_rfc8410_der();
88 Self {
89 private_key: KeyCompat::Standard(private_key),
90 der_encoded_public_key,
91 }
92 }
93
94 #[deprecated(since = "0.41.0", note = "use BasicIdentity::from_raw_key instead")]
101 pub fn from_signing_key(key: ed25519_consensus::SigningKey) -> Self {
102 let raw_key = key.to_bytes();
103 Self::from_raw_key(&raw_key)
104 }
105
106 #[cfg(feature = "ring")]
108 pub fn from_key_pair(key_pair: ring::signature::Ed25519KeyPair) -> Self {
109 use ic_ed25519::PublicKey;
110 use ring::signature::KeyPair;
111 let raw_public_key = key_pair.public_key().as_ref().to_vec();
112 let public_key = PublicKey::deserialize_raw(&raw_public_key).unwrap();
114 let der_encoded_public_key = public_key.serialize_rfc8410_der();
115 Self {
116 private_key: KeyCompat::Ring(key_pair),
117 der_encoded_public_key,
118 }
119 }
120}
121
122enum KeyCompat {
123 Standard(PrivateKey),
125 #[cfg(feature = "ring")]
126 Ring(ring::signature::Ed25519KeyPair),
127}
128
129impl KeyCompat {
130 fn sign(&self, payload: &[u8]) -> Vec<u8> {
131 match self {
132 Self::Standard(k) => k.sign_message(payload).to_vec(),
133 #[cfg(feature = "ring")]
134 Self::Ring(k) => k.sign(payload).as_ref().to_vec(),
135 }
136 }
137}
138
139impl Identity for BasicIdentity {
140 fn sender(&self) -> Result<Principal, String> {
141 Ok(Principal::self_authenticating(&self.der_encoded_public_key))
142 }
143
144 fn public_key(&self) -> Option<Vec<u8>> {
145 Some(self.der_encoded_public_key.clone())
146 }
147
148 fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String> {
149 self.sign_arbitrary(&content.to_request_id().signable())
150 }
151
152 fn sign_delegation(&self, content: &Delegation) -> Result<Signature, String> {
153 self.sign_arbitrary(&content.signable())
154 }
155
156 fn sign_arbitrary(&self, content: &[u8]) -> Result<Signature, String> {
157 let signature = self.private_key.sign(content);
158 Ok(Signature {
159 signature: Some(signature),
160 public_key: self.public_key(),
161 delegations: None,
162 })
163 }
164}