1use std::fmt::{Debug, Formatter};
6use std::sync::Mutex;
7
8use chrono::{DateTime, Utc};
9use openpgp_card::ocard::crypto::Hash;
10use openpgp_card::ocard::KeyType;
11use openpgp_card::state::Transaction;
12use openpgp_card::Card;
13use pgp::crypto::checksum;
14use pgp::crypto::ecc_curve::ECCCurve;
15use pgp::crypto::hash::HashAlgorithm;
16use pgp::crypto::public_key::PublicKeyAlgorithm;
17use pgp::crypto::sym::SymmetricKeyAlgorithm;
18use pgp::packet::PublicKey;
19use pgp::types::{
20 EcdhPublicParams, EcdsaPublicParams, Fingerprint, KeyId, KeyVersion, Mpi, PkeskBytes,
21 PublicKeyTrait, PublicParams, SecretKeyTrait, SignatureBytes,
22};
23use pgp::{Esk, Message, PlainSessionKey};
24use rand::{CryptoRng, Rng};
25
26use crate::rpgp::map_card_err;
27
28pub struct CardSlot<'cs, 't> {
30 tx: Mutex<&'cs mut Card<Transaction<'t>>>,
31
32 key_type: KeyType,
34
35 public_key: PublicKey,
40
41 touch_prompt: &'cs (dyn Fn() + Send + Sync),
42}
43
44impl<'cs, 't> CardSlot<'cs, 't> {
45 pub fn with_public_key(
49 tx: &'cs mut Card<Transaction<'t>>,
50 key_type: KeyType,
51 public_key: PublicKey,
52 touch_prompt: &'cs (dyn Fn() + Send + Sync),
53 ) -> Result<Self, pgp::errors::Error> {
54 Ok(Self {
57 tx: Mutex::new(tx),
58 public_key,
59 key_type,
60 touch_prompt,
61 })
62 }
63
64 pub fn init_from_card(
68 tx: &'cs mut Card<Transaction<'t>>,
69 key_type: KeyType,
70 touch_prompt: &'cs (dyn Fn() + Send + Sync),
71 ) -> Result<Self, pgp::errors::Error> {
72 let pk = crate::rpgp::pubkey_from_card(tx, key_type)?;
73
74 Self::with_public_key(tx, key_type, pk, touch_prompt)
75 }
76}
77
78impl CardSlot<'_, '_> {
79 pub fn public_key(&self) -> &PublicKey {
81 &self.public_key
82 }
83
84 pub fn key_type(&self) -> KeyType {
86 self.key_type
87 }
88
89 fn touch_required(&self, tx: &mut Card<Transaction<'_>>) -> bool {
90 if let Ok(Some(uif)) = tx.user_interaction_flag(self.key_type) {
94 uif.touch_policy().touch_required()
95 } else {
96 false
97 }
98 }
99
100 pub fn decrypt(
101 &self,
102 values: &PkeskBytes,
103 ) -> pgp::errors::Result<(Vec<u8>, SymmetricKeyAlgorithm)> {
104 #[allow(clippy::unwrap_used)]
105 let mut tx = self.tx.lock().unwrap();
106
107 let decrypted_key = match (self.public_key.public_params(), values) {
108 (PublicParams::RSA { n, .. }, PkeskBytes::Rsa { mpi }) => {
109 let mut ciphertext = mpi.to_vec();
110
111 let modulus_len = n.len();
118
119 while modulus_len > ciphertext.len() {
126 ciphertext.insert(0, 0u8);
127 }
128
129 let cryptogram = openpgp_card::ocard::crypto::Cryptogram::RSA(&ciphertext);
130
131 if self.touch_required(&mut tx) {
132 (self.touch_prompt)();
133 }
134
135 tx.card().decipher(cryptogram).map_err(|e| {
136 pgp::errors::Error::Message(format!(
137 "RSA decipher operation on card failed: {}",
138 e
139 ))
140 })?
141 }
142
143 (
144 PublicParams::ECDH(EcdhPublicParams::Known {
145 curve,
146 alg_sym,
147 hash,
148 ..
149 }),
150 PkeskBytes::Ecdh {
151 public_point,
152 encrypted_session_key,
153 },
154 ) => {
155 let ciphertext = public_point.as_bytes();
156
157 let ciphertext = if *curve == ECCCurve::Curve25519 {
158 assert_eq!(
159 ciphertext[0], 0x40,
160 "Unexpected shape of Cv25519 encrypted data"
161 );
162
163 &ciphertext[1..]
165 } else {
166 ciphertext
168 };
169
170 let cryptogram = openpgp_card::ocard::crypto::Cryptogram::ECDH(ciphertext);
171
172 if self.touch_required(&mut tx) {
173 (self.touch_prompt)();
174 }
175
176 let shared_secret: Vec<u8> = tx.card().decipher(cryptogram).map_err(|e| {
177 pgp::errors::Error::Message(format!(
178 "ECDH decipher operation on card failed {}",
179 e
180 ))
181 })?;
182
183 let encrypted_key_len = encrypted_session_key.len();
184
185 let decrypted_key: Vec<u8> = pgp::crypto::ecdh::derive_session_key(
186 &shared_secret,
187 encrypted_session_key,
188 encrypted_key_len,
189 &(curve.clone(), *alg_sym, *hash),
190 self.public_key.fingerprint().as_bytes(),
191 )?;
192
193 decrypted_key
194 }
195
196 pp => {
197 return Err(pgp::errors::Error::Message(format!(
198 "decrypt: Unsupported key type {:?}",
199 pp
200 )));
201 }
202 };
203
204 let dec_len = decrypted_key.len();
206 let (sessionkey, checksum) = (
207 &decrypted_key[1..dec_len - 2],
208 &decrypted_key[dec_len - 2..],
209 );
210
211 checksum::simple(checksum, sessionkey)?;
213
214 let session_key_algorithm = decrypted_key[0].into();
215 Ok((sessionkey.to_vec(), session_key_algorithm))
216 }
217
218 pub fn decrypt_message(&self, message: &Message) -> Result<Message, pgp::errors::Error> {
219 let Message::Encrypted { esk, edata } = message else {
220 return Err(pgp::errors::Error::Message(
221 "message must be Message::Encrypted".to_string(),
222 ));
223 };
224
225 let values = match &esk[0] {
226 Esk::PublicKeyEncryptedSessionKey(ref k) => k.values()?,
227 _ => {
228 return Err(pgp::errors::Error::Message(
229 "Expected PublicKeyEncryptedSessionKey".to_string(),
230 ))
231 }
232 };
233
234 let (session_key, session_key_algorithm) =
235 self.unlock(String::new, |priv_key| priv_key.decrypt(values))?;
236
237 let plain_session_key = PlainSessionKey::V3_4 {
238 key: session_key,
239 sym_alg: session_key_algorithm,
240 };
241
242 let decrypted = edata.decrypt(plain_session_key)?;
243
244 Ok(decrypted)
245 }
246}
247
248impl Debug for CardSlot<'_, '_> {
249 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
250 write!(f, "CardSlot for {:?}", self.public_key)?;
252
253 Ok(())
254 }
255}
256
257impl PublicKeyTrait for CardSlot<'_, '_> {
258 fn version(&self) -> KeyVersion {
259 KeyVersion::V4 }
261
262 fn fingerprint(&self) -> Fingerprint {
263 self.public_key.fingerprint()
264 }
265
266 fn key_id(&self) -> KeyId {
267 self.public_key.key_id()
268 }
269
270 fn algorithm(&self) -> PublicKeyAlgorithm {
271 self.public_key.algorithm()
272 }
273
274 fn created_at(&self) -> &DateTime<Utc> {
275 self.public_key.created_at()
276 }
277
278 fn expiration(&self) -> Option<u16> {
279 None
280 }
281
282 fn verify_signature(
283 &self,
284 hash: HashAlgorithm,
285 data: &[u8],
286 sig: &SignatureBytes,
287 ) -> pgp::errors::Result<()> {
288 self.public_key.verify_signature(hash, data, sig)
289 }
290
291 fn encrypt<R: CryptoRng + Rng>(
292 &self,
293 rng: R,
294 plain: &[u8],
295 typ: pgp::types::EskType,
296 ) -> pgp::errors::Result<PkeskBytes> {
297 self.public_key.encrypt(rng, plain, typ)
298 }
299
300 fn serialize_for_hashing(&self, writer: &mut impl std::io::Write) -> pgp::errors::Result<()> {
301 self.public_key.serialize_for_hashing(writer)
302 }
303
304 fn public_params(&self) -> &PublicParams {
305 self.public_key.public_params()
306 }
307}
308
309impl SecretKeyTrait for CardSlot<'_, '_> {
310 type PublicKey = PublicKey;
313 type Unlocked = Self;
314
315 fn unlock<F, G, T>(&self, _pw: F, work: G) -> pgp::errors::Result<T>
316 where
317 F: FnOnce() -> String,
318 G: FnOnce(&Self::Unlocked) -> pgp::errors::Result<T>,
319 {
320 work(self)
321 }
322
323 fn create_signature<F>(
324 &self,
325 _key_pw: F,
326 hash: HashAlgorithm,
327 data: &[u8],
328 ) -> pgp::errors::Result<SignatureBytes>
329 where
330 F: FnOnce() -> String,
331 {
332 #[allow(clippy::unwrap_used)]
333 let mut tx = self.tx.lock().unwrap();
334
335 let hash = match self.public_key.algorithm() {
336 PublicKeyAlgorithm::RSA => to_hash_rsa(data, hash)?,
337 PublicKeyAlgorithm::ECDSA => Hash::ECDSA({
338 match self.public_key.public_params() {
339 PublicParams::ECDSA(EcdsaPublicParams::P256 { .. }) => &data[..32],
340 PublicParams::ECDSA(EcdsaPublicParams::P384 { .. }) => &data[..48],
341 PublicParams::ECDSA(EcdsaPublicParams::P521 { .. }) => &data[..64],
342 _ => data,
343 }
344 }),
345 PublicKeyAlgorithm::EdDSALegacy => Hash::EdDSA(data),
346
347 _ => {
348 return Err(pgp::errors::Error::Unimplemented(format!(
349 "Unsupported PublicKeyAlgorithm for signature creation: {:?}",
350 self.public_key.algorithm()
351 )))
352 }
353 };
354
355 if self.touch_required(&mut tx) {
356 (self.touch_prompt)();
357 }
358
359 let sig = match self.key_type {
360 KeyType::Signing => tx.card().signature_for_hash(hash).map_err(map_card_err)?,
361 KeyType::Authentication => tx
362 .card()
363 .authenticate_for_hash(hash)
364 .map_err(map_card_err)?,
365 _ => {
366 return Err(pgp::errors::Error::Unimplemented(format!(
367 "Unsupported KeyType for signature creation: {:?}",
368 self.key_type
369 )))
370 }
371 };
372
373 let mpis = match self.public_key.algorithm() {
374 PublicKeyAlgorithm::RSA => vec![Mpi::from_raw(sig)],
375
376 PublicKeyAlgorithm::ECDSA => {
377 let mid = sig.len() / 2;
378
379 vec![Mpi::from_slice(&sig[..mid]), Mpi::from_slice(&sig[mid..])]
380 }
381 PublicKeyAlgorithm::EdDSALegacy => {
382 assert_eq!(sig.len(), 64); vec![Mpi::from_slice(&sig[..32]), Mpi::from_slice(&sig[32..])]
385 }
386
387 alg => {
388 return Err(pgp::errors::Error::Unimplemented(format!(
389 "Unsupported algorithm for signature creation: {:?}",
390 alg
391 )))
392 }
393 };
394
395 Ok(SignatureBytes::Mpis(mpis))
396 }
397
398 fn public_key(&self) -> Self::PublicKey {
399 self.public_key.clone()
400 }
401}
402
403fn to_hash_rsa(data: &[u8], hash: HashAlgorithm) -> pgp::errors::Result<Hash> {
404 match hash {
405 HashAlgorithm::SHA2_256 => {
406 if data.len() == 0x20 {
407 #[allow(clippy::unwrap_used)]
408 Ok(Hash::SHA256(data.try_into().unwrap()))
409 } else {
410 Err(pgp::errors::Error::Message(format!(
411 "Illegal digest len for SHA256: {}",
412 data.len()
413 )))
414 }
415 }
416 HashAlgorithm::SHA2_384 => {
417 if data.len() == 0x30 {
418 #[allow(clippy::unwrap_used)]
419 Ok(Hash::SHA384(data.try_into().unwrap()))
420 } else {
421 Err(pgp::errors::Error::Message(format!(
422 "Illegal digest len for SHA384: {}",
423 data.len()
424 )))
425 }
426 }
427 HashAlgorithm::SHA2_512 => {
428 if data.len() == 0x40 {
429 #[allow(clippy::unwrap_used)]
430 Ok(Hash::SHA512(data.try_into().unwrap()))
431 } else {
432 Err(pgp::errors::Error::Message(format!(
433 "Illegal digest len for SHA512: {}",
434 data.len()
435 )))
436 }
437 }
438 _ => Err(pgp::errors::Error::Message(format!(
439 "Unsupported HashAlgorithm for RSA: {:?}",
440 hash
441 ))),
442 }
443}