ic_agent/identity/
prime256v1.rs1use crate::{agent::EnvelopeContent, export::Principal, Identity, Signature};
2
3#[cfg(feature = "pem")]
4use crate::identity::error::PemError;
5
6use p256::{
7 ecdsa::{self, signature::Signer, SigningKey, VerifyingKey},
8 pkcs8::{Document, EncodePublicKey},
9 SecretKey,
10};
11#[cfg(feature = "pem")]
12use std::{fs::File, io, path::Path};
13
14use super::Delegation;
15
16#[derive(Clone, Debug)]
20pub struct Prime256v1Identity {
21 private_key: SigningKey,
22 _public_key: VerifyingKey,
23 der_encoded_public_key: Document,
24}
25
26impl Prime256v1Identity {
27 #[cfg(feature = "pem")]
29 pub fn from_pem_file<P: AsRef<Path>>(file_path: P) -> Result<Self, PemError> {
30 Self::from_pem(File::open(file_path)?)
31 }
32
33 #[cfg(feature = "pem")]
35 pub fn from_pem<R: io::Read>(pem_reader: R) -> Result<Self, PemError> {
36 use sec1::{pem::PemLabel, EcPrivateKey};
37
38 const EC_PARAMETERS: &str = "EC PARAMETERS";
39 const PRIME256V1: &[u8] = b"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07";
40
41 let contents = pem_reader.bytes().collect::<Result<Vec<u8>, io::Error>>()?;
42
43 for pem in pem::parse_many(contents)? {
44 if pem.tag() == EC_PARAMETERS && pem.contents() != PRIME256V1 {
45 return Err(PemError::UnsupportedKeyCurve(
46 "prime256v1".to_string(),
47 pem.contents().to_vec(),
48 ));
49 }
50
51 if pem.tag() != EcPrivateKey::PEM_LABEL {
52 continue;
53 }
54 let private_key =
55 SecretKey::from_sec1_der(pem.contents()).map_err(|_| pkcs8::Error::KeyMalformed)?;
56 return Ok(Self::from_private_key(private_key));
57 }
58 Err(pem::PemError::MissingData.into())
59 }
60
61 pub fn from_private_key(private_key: SecretKey) -> Self {
63 let public_key = private_key.public_key();
64 let der_encoded_public_key = public_key
65 .to_public_key_der()
66 .expect("Cannot DER encode prime256v1 public key.");
67 Self {
68 private_key: private_key.into(),
69 _public_key: public_key.into(),
70 der_encoded_public_key,
71 }
72 }
73}
74
75impl Identity for Prime256v1Identity {
76 fn sender(&self) -> Result<Principal, String> {
77 Ok(Principal::self_authenticating(
78 self.der_encoded_public_key.as_ref(),
79 ))
80 }
81
82 fn public_key(&self) -> Option<Vec<u8>> {
83 Some(self.der_encoded_public_key.as_ref().to_vec())
84 }
85
86 fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String> {
87 self.sign_arbitrary(&content.to_request_id().signable())
88 }
89
90 fn sign_delegation(&self, content: &Delegation) -> Result<Signature, String> {
91 self.sign_arbitrary(&content.signable())
92 }
93
94 fn sign_arbitrary(&self, content: &[u8]) -> Result<Signature, String> {
95 let ecdsa_sig: ecdsa::Signature = self
96 .private_key
97 .try_sign(content)
98 .map_err(|err| format!("Cannot create prime256v1 signature: {err}"))?;
99 let r = ecdsa_sig.r().as_ref().to_bytes();
100 let s = ecdsa_sig.s().as_ref().to_bytes();
101 let mut bytes = [0u8; 64];
102 if r.len() > 32 || s.len() > 32 {
103 return Err("Cannot create prime256v1 signature: malformed signature.".to_string());
104 }
105 bytes[(32 - r.len())..32].clone_from_slice(&r);
106 bytes[32 + (32 - s.len())..].clone_from_slice(&s);
107 let signature = Some(bytes.to_vec());
108 let public_key = self.public_key();
109 Ok(Signature {
110 public_key,
111 signature,
112 delegations: None,
113 })
114 }
115}
116
117#[cfg(feature = "pem")]
118#[cfg(test)]
119mod test {
120 use super::*;
121 use candid::Encode;
122 use p256::{
123 ecdsa::{signature::Verifier, Signature},
124 elliptic_curve::PrimeField,
125 FieldBytes, Scalar,
126 };
127
128 const WRONG_CURVE_IDENTITY_FILE: &str = "\
133-----BEGIN EC PARAMETERS-----
134BgUrgQQAHg==
135-----END EC PARAMETERS-----
136-----BEGIN EC PRIVATE KEY-----
137MFACAQEEFI9cF6zXxMKhtjn1gBD7AHPbzehfoAcGBSuBBAAeoSwDKgAEh5NXszgR
138oGSXVWaGxcQhQWlFG4pbnOG+93xXzfRD7eKWOdmun2bKxQ==
139-----END EC PRIVATE KEY-----
140";
141
142 const WRONG_CURVE_IDENTITY_FILE_NO_PARAMS: &str = "\
147-----BEGIN EC PRIVATE KEY-----
148MFACAQEEFI9cF6zXxMKhtjn1gBD7AHPbzehfoAcGBSuBBAAeoSwDKgAEh5NXszgR
149oGSXVWaGxcQhQWlFG4pbnOG+93xXzfRD7eKWOdmun2bKxQ==
150-----END EC PRIVATE KEY-----
151";
152
153 const IDENTITY_FILE: &str = "\
157-----BEGIN EC PRIVATE KEY-----
158MHcCAQEEIL1ybmbwx+uKYsscOZcv71MmKhrNqfPP0ke1unET5AY4oAoGCCqGSM49
159AwEHoUQDQgAEUbbZV4NerZTPWfbQ749/GNLu8TaH8BUS/I7/+ipsu+MPywfnBFIZ
160Sks4xGbA/ZbazsrMl4v446U5UIVxCGGaKw==
161-----END EC PRIVATE KEY-----
162";
163
164 const DER_ENCODED_PUBLIC_KEY: &str = "3059301306072a8648ce3d020106082a8648ce3d0301070342000451b6d957835ead94cf59f6d0ef8f7f18d2eef13687f01512fc8efffa2a6cbbe30fcb07e70452194a4b38c466c0fd96dacecacc978bf8e3a53950857108619a2b";
168
169 #[test]
170 #[should_panic(expected = "UnsupportedKeyCurve")]
171 fn test_prime256v1_reject_wrong_curve() {
172 Prime256v1Identity::from_pem(WRONG_CURVE_IDENTITY_FILE.as_bytes()).unwrap();
173 }
174
175 #[test]
176 #[should_panic(expected = "KeyMalformed")]
177 fn test_prime256v1_reject_wrong_curve_no_id() {
178 Prime256v1Identity::from_pem(WRONG_CURVE_IDENTITY_FILE_NO_PARAMS.as_bytes()).unwrap();
179 }
180
181 #[test]
182 fn test_prime256v1_public_key() {
183 let identity = Prime256v1Identity::from_pem(IDENTITY_FILE.as_bytes())
185 .expect("Cannot create prime256v1 identity from PEM file.");
186
187 assert!(DER_ENCODED_PUBLIC_KEY == hex::encode(identity.der_encoded_public_key));
189 }
190
191 #[test]
192 fn test_prime256v1_signature() {
193 let identity = Prime256v1Identity::from_pem(IDENTITY_FILE.as_bytes())
195 .expect("Cannot create prime256v1 identity from PEM file.");
196
197 let message = EnvelopeContent::Call {
199 nonce: None,
200 ingress_expiry: 0,
201 sender: identity.sender().unwrap(),
202 canister_id: "bkyz2-fmaaa-aaaaa-qaaaq-cai".parse().unwrap(),
203 method_name: "greet".to_string(),
204 arg: Encode!(&"world").unwrap(),
205 };
206 let signature = identity
207 .sign(&message)
208 .expect("Cannot create prime256v1 signature.")
209 .signature
210 .expect("Cannot find prime256v1 signature bytes.");
211
212 let r: Scalar = Option::from(Scalar::from_repr(*FieldBytes::from_slice(
214 &signature[0..32],
215 )))
216 .expect("Cannot extract r component from prime256v1 signature bytes.");
217 let s: Scalar = Option::from(Scalar::from_repr(*FieldBytes::from_slice(&signature[32..])))
218 .expect("Cannot extract s component from prime256v1 signature bytes.");
219 let ecdsa_sig = Signature::from_scalars(r, s)
220 .expect("Cannot create prime256v1 signature from r and s components.");
221
222 identity
224 ._public_key
225 .verify(&message.to_request_id().signable(), &ecdsa_sig)
226 .expect("Cannot verify prime256v1 signature.");
227 }
228}