sequoia_keystore_tpm/
lib.rs

1use std::collections::BTreeMap;
2use std::collections::btree_map::Entry;
3use std::fs::File;
4use std::io::Write;
5use std::path::Path;
6use std::sync::Arc;
7use std::sync::Weak;
8use std::time::SystemTime;
9
10use anyhow::Context;
11
12use futures::lock::Mutex;
13
14use sequoia_openpgp as openpgp;
15use openpgp::Cert;
16use openpgp::Fingerprint;
17use openpgp::Packet;
18use openpgp::Result;
19use openpgp::crypto::Password;
20use openpgp::crypto::SessionKey;
21use openpgp::crypto::mpi;
22use openpgp::fmt::hex;
23use openpgp::packet::key::Key4;
24use openpgp::packet::key::PublicParts;
25use openpgp::packet::key::UnspecifiedRole;
26use openpgp::packet;
27use openpgp::serialize::SerializeInto;
28use openpgp::serialize::stream::Armorer;
29use openpgp::serialize::stream::Message;
30use openpgp::types::HashAlgorithm;
31use openpgp::types::PublicKeyAlgorithm;
32
33use sequoia_ipc::Keygrip;
34
35use sequoia_tpm::Description;
36
37use sequoia_keystore_backend as backend;
38use backend::Error;
39use backend::ImportStatus;
40use backend::PasswordSource;
41use backend::Protection;
42use backend::utils::Directory;
43
44mod certd;
45
46#[derive(Clone)]
47pub struct Backend {
48    inner: Arc<Mutex<BackendInternal>>,
49}
50
51struct BackendInternal {
52    home: Directory,
53
54    // OpenPGP TPM don't store the OpenPGP key data structures nor
55    // do they (always) have all the information required to
56    // reconstruct them (in particular, it's missing the ECC algorithm
57    // parameters).  Since looking up a key by subkey means doing a
58    // full scan, we cache the results.
59    certd: Arc<certd::CertD>,
60
61    // One device per TPM.
62    devices: Vec<Device>,
63}
64
65/// A Device exposes the (usable) keys managed by a particular TPM,
66/// e.g., `device:/dev/tpmrm0`.
67///
68/// A key is usable if we have the OpenPGP data structure.  If we
69/// don't have the OpenPGP key, we ignore the key.
70#[derive(Clone)]
71pub struct Device {
72    // Device's URI.
73    id: String,
74    inner: Arc<Mutex<DeviceInternal>>
75}
76
77impl Device {
78    /// Creates a new `WeakDevice` from this `Device`.
79    fn downgrade(&self) -> WeakDevice {
80        WeakDevice {
81            id: self.id.clone(),
82            inner: Arc::downgrade(&self.inner),
83        }
84    }
85}
86
87/// A `Device`, but with a weak reference to the data.
88///
89/// Before you use this, you need to upgrade this to a `Device`.
90#[derive(Clone)]
91pub struct WeakDevice {
92    /// The device's URI, e.g., `device:/dev/tpmrm0`.
93    id: String,
94    inner: Weak<Mutex<DeviceInternal>>,
95}
96
97impl WeakDevice {
98    /// Upgrades the `WeakDevice` to a `Device`, if possible.
99    fn upgrade(&self) -> Option<Device> {
100        Some(Device {
101            id: self.id.clone(),
102            inner: self.inner.upgrade()?,
103        })
104    }
105}
106
107struct DeviceInternal {
108    /// The device's URI, e.g., `device:/dev/tpmrm0`.
109    id: String,
110
111    /// A map from id (fingerprint) to key.
112    keys: BTreeMap<Fingerprint, Key>,
113}
114
115#[derive(Clone)]
116pub struct Key {
117    // The fingerprint is also the id.
118    fpr: Fingerprint,
119    inner: Arc<Mutex<KeyInternal>>,
120}
121
122/// A secret key.
123struct KeyInternal {
124    device: WeakDevice,
125
126    fingerprint: Fingerprint,
127    public_key: packet::Key<packet::key::PublicParts,
128                            packet::key::UnspecifiedRole>,
129
130    desc: Description,
131}
132
133impl Backend {
134    /// Initializes the TPM backend.
135    ///
136    /// `home` is the directory where the backend will look for its
137    /// configuration, e.g., `$HOME/.sq/keystore/tpm`.
138    pub async fn init<P: AsRef<Path>>(home: P, _default: bool)
139        -> Result<Self>
140    {
141        log::trace!("Backend::init");
142
143        let home = Directory::from(home.as_ref());
144
145        let certd = Arc::new(certd::CertD::new()?);
146
147        let mut inner = BackendInternal {
148            home,
149            certd,
150            devices: vec![],
151        };
152
153        let _ = inner.scan_internal(true).await;
154
155        Ok(Backend {
156            inner: Arc::new(Mutex::new(inner)),
157        })
158    }
159
160    /// Initializes an ephemeral TPM backend.
161    ///
162    /// This is primarily useful for testing.
163    pub async fn init_ephemeral() -> Result<Self> {
164        log::trace!("Backend::init_ephemeral");
165
166        let home = Directory::ephemeral()?;
167        Self::init(home, false).await
168    }
169}
170
171fn derive_key(pk: &sequoia_tpm::PublicKeyBytes)
172    -> Result<packet::Key<PublicParts, UnspecifiedRole>>
173{
174    match pk {
175        sequoia_tpm::PublicKeyBytes::RSA(pk) => {
176            let bytes = hex::decode(&pk.bytes);
177            match bytes {
178                Ok(bytes) => {
179                    Key4::import_public_rsa(
180                        &[1, 0, 1], &bytes, SystemTime::UNIX_EPOCH)
181                        .map(|k| packet::Key::V4(k))
182                }
183                Err(err) => {
184                    Err(err.into())
185                }
186            }
187        }
188        _ => {
189            return Err(anyhow::anyhow!("Unsupported algorithm"));
190        }
191    }
192}
193
194impl BackendInternal {
195    async fn scan_internal(&mut self, _force: bool) -> Result<()> {
196        log::trace!("Backend::scan");
197
198        let certd = Arc::clone(&self.certd);
199
200        // let home = &backend.home;
201        let home = &self.home;
202
203        log::debug!("Scanning: {:?}", home.display());
204        for entry in home.read_dir()
205            .with_context(|| format!("{:?}", home.display()))?
206        {
207            let entry = match entry {
208                Ok(entry) => entry,
209                Err(err) => {
210                    log::debug!("While listing {:?}: {}",
211                                home.display(), err);
212                    continue;
213                }
214            };
215
216            let filename = entry.path();
217
218            let read = if let Some(extension) = filename.extension() {
219                if extension == "yml" || extension == "yaml" {
220                    true
221                } else {
222                    false
223                }
224            } else {
225                false
226            };
227
228            if ! read {
229                log::debug!("Ignoring {:?}", filename);
230                continue;
231            }
232            log::debug!("Considering: {:?}", filename);
233
234            let file = match File::open(&filename) {
235                Ok(file) => file,
236                Err(err) => {
237                    log::debug!("Failed to open {}: {}",
238                                filename.display(), err);
239                    continue;
240                }
241            };
242
243            let mut desc: Description = match serde_yaml::from_reader(file) {
244                Ok(desc) => desc,
245                Err(err) => {
246                    log::debug!("Failed to parse {}: {}",
247                                filename.display(), err);
248                    continue;
249                }
250            };
251
252            log::trace!("{}:\n{}",
253                        filename.display(),
254                        serde_yaml::to_string(&desc)?);
255
256            match sequoia_tpm::read_key(&mut desc.spec) {
257                Ok(_key) => (),
258                Err(err) => {
259                    log::debug!("Error configured key ({}) on TPM: {}\n{}",
260                                filename.display(), err,
261                                serde_yaml::to_string(&desc)?);
262                    continue;
263                }
264            }
265
266            let pk = if let Some(pk) = desc.spec.provider.tpm.unique.as_ref() {
267                pk
268            } else {
269                log::debug!("{}: No public key, skipping.",
270                            filename.display());
271                continue;
272            };
273
274            let key = match derive_key(pk) {
275                Ok(key) => key,
276                Err(err) => {
277                    log::debug!("{}: Failed to derive a null OpenPGP key: {}",
278                                filename.display(), err);
279                    continue;
280                }
281            };
282
283            let keygrip = match Keygrip::of(key.mpis()) {
284                Ok(keygrip) => keygrip,
285                Err(err) => {
286                    log::debug!("{}: computing keygrip: {}",
287                                filename.display(), err);
288                    continue;
289                }
290            };
291
292            let key = match certd.find(&keygrip).await {
293                Ok(key) => key,
294                Err(err) => {
295                    log::debug!("{}: looking for OpenPGP key: {}",
296                                filename.display(), err);
297
298                    let packet = Packet::from(
299                        key.role_into_primary());
300
301                    let packet = packet.to_vec()
302                        .expect("serializing to a vec is infallible");
303
304                    let mut bytes = Vec::new();
305                    let message = Message::new(&mut bytes);
306                    let mut message = Armorer::new(message)
307                        .kind(openpgp::armor::Kind::PublicKey)
308                        .build()
309                        .expect("can build armorer");
310                    message.write_all(&packet)
311                        .expect("can serialize to vec");
312                    message.finalize()
313                        .expect("can serialize to vec");
314
315                    log::debug!("Null key:\n{}",
316                                String::from_utf8_lossy(&bytes));
317                    continue;
318                }
319            };
320
321            let fpr = key.fingerprint();
322
323            // Insert the key.
324            let provider = desc.spec.provider.tpm.tcti.clone();
325            let mut device = None;
326            for d in self.devices.iter_mut() {
327                if d.id == provider {
328                    device = Some(d);
329                }
330            }
331
332            let device = if let Some(device) = device {
333                device
334            } else {
335                self.devices.push(Device {
336                    id: provider.clone(),
337                    inner: Arc::new(Mutex::new(DeviceInternal {
338                        id: provider.clone(),
339                        keys: Default::default(),
340                    })),
341                });
342                self.devices.last_mut().unwrap()
343            };
344
345            let mut device_internal = device.inner.lock().await;
346            match device_internal.keys.entry(fpr.clone()) {
347                Entry::Occupied(_oe) => {
348                    // Already have it.
349                }
350                Entry::Vacant(ve) => {
351                    // Need to add it.
352                    log::debug!("Found key {}", fpr);
353
354                    let key = Key {
355                        fpr: fpr.clone(),
356                        inner: Arc::new(Mutex::new(KeyInternal {
357                            device: device.downgrade(),
358                            fingerprint: fpr,
359                            // XXX
360                            public_key: key,
361                            desc,
362                        })),
363                    };
364
365                    ve.insert(key);
366                }
367            }
368        }
369
370        Ok(())
371    }
372}
373
374#[async_trait::async_trait]
375impl backend::Backend for Backend {
376    fn id(&self) -> String {
377        "tpm".into()
378    }
379
380    async fn scan(&mut self) -> Result<()> {
381        let mut backend = self.inner.lock().await;
382        backend.scan_internal(false).await
383    }
384
385    async fn list<'a>(&'a self)
386        -> Box<dyn Iterator<Item=Box<dyn backend::DeviceHandle + Send + Sync + 'a>>
387               + Send + Sync + 'a>
388    {
389        log::trace!("Backend::list");
390
391        let mut backend = self.inner.lock().await;
392
393        if let Err(err) = backend.scan_internal(false).await {
394            log::debug!("Scanning TPMs: {}", err);
395        }
396
397        Box::new(
398            backend.devices.iter()
399                .map(|device| {
400                    Box::new(device.clone())
401                        as Box<dyn backend::DeviceHandle + Send + Sync>
402                })
403                .collect::<Vec<_>>()
404                .into_iter())
405    }
406
407    async fn find_device<'a>(&self, id: &str)
408        -> Result<Box<dyn backend::DeviceHandle + Send + Sync + 'a>>
409    {
410        log::trace!("Backend::find_device");
411
412        let mut backend = self.inner.lock().await;
413
414        // The first time through we look for the key without
415        // scanning.  If we don't find it, then we rescan.
416        for scan in [false, true] {
417            if scan {
418                log::trace!("Rescanning");
419                if let Err(err) = backend.scan_internal(true).await {
420                    log::debug!("Scanning TPMs: {}", err);
421                }
422            }
423
424            for device in backend.devices.iter() {
425                if device.id == id {
426                    return Ok(Box::new(device.clone())
427                              as Box<dyn backend::DeviceHandle + Send + Sync>);
428                }
429            }
430        }
431
432        Err(Error::NotFound(id.into()).into())
433    }
434
435    async fn find_key<'a>(&self, id: &str)
436        -> Result<Box<dyn backend::KeyHandle + Send + Sync + 'a>>
437    {
438        log::trace!("Backend::find_key");
439
440        let mut backend = self.inner.lock().await;
441
442        // The first time through we look for the key without
443        // scanning.  If we don't find it, then we rescan.
444        for scan in [false, true] {
445            if scan {
446                log::trace!("Rescanning");
447                if let Err(err) = backend.scan_internal(true).await {
448                    log::debug!("Scanning TPMs: {}", err);
449                }
450            }
451
452            for device in backend.devices.iter() {
453                let device = device.inner.lock().await;
454
455                for (key_id, key) in device.keys.iter() {
456                    if &key_id.to_string() == id {
457                        return Ok(Box::new(key.clone())
458                                  as Box<dyn backend::KeyHandle + Send + Sync>);
459                    }
460                }
461            }
462        }
463
464        Err(Error::NotFound(id.into()).into())
465    }
466
467    async fn import<'a>(&self, _cert: Cert)
468        -> Result<Vec<(ImportStatus,
469                       Box<dyn backend::KeyHandle + Send + Sync + 'a>)>>
470    {
471        log::trace!("Backend::import");
472
473        Err(Error::ExternalImportRequired(Some(
474            "Use an external tool to manage TPM keys.".into()).into()).into())
475    }
476}
477
478#[async_trait::async_trait]
479impl backend::DeviceHandle for Device {
480    fn id(&self) -> String {
481        log::trace!("Device::id");
482
483        self.id.clone()
484    }
485
486    async fn available(&self) -> bool {
487        log::trace!("Device::available");
488
489        // XXX: Right now, we only support configured and available
490        // keys.
491        true
492    }
493
494    async fn configured(&self) -> bool {
495        log::trace!("Device::configured");
496
497        // XXX: Right now, we only support configured and available
498        // keys.
499        true
500    }
501
502    async fn registered(&self) -> bool {
503        log::trace!("Device::registered");
504
505        // XXX: Right now, we only support configured and available
506        // keys.
507        true
508    }
509
510    async fn lock(&mut self) -> Result<()> {
511        log::trace!("Device::lock");
512
513        // We manage passwords at the key level; a device can't be
514        // locked.
515
516        Ok(())
517    }
518
519    async fn list<'a>(&'a self)
520        -> Box<dyn Iterator<Item=Box<dyn backend::KeyHandle + Send + Sync + 'a>>
521               + Send + Sync + 'a>
522    {
523        log::trace!("Device::list");
524
525        let device = self.inner.lock().await;
526        let keys = device.keys.values()
527            .map(|key| {
528                Box::new(key.clone())
529                    as Box<dyn backend::KeyHandle + Send + Sync>
530            })
531            .collect::<Vec<_>>();
532
533        Box::new(keys.into_iter())
534    }
535}
536
537#[async_trait::async_trait]
538impl backend::KeyHandle for Key {
539    fn id(&self) -> String {
540        log::trace!("Key::id");
541
542        self.fpr.to_string()
543    }
544
545    fn fingerprint(&self) -> Fingerprint {
546        log::trace!("Key::fingerprint");
547
548        self.fpr.clone()
549    }
550
551    async fn device<'a>(&self)
552        -> Box<dyn backend::DeviceHandle + Send + Sync + 'a>
553    {
554        log::trace!("Key::device");
555
556        let key_internal = self.inner.lock().await;
557
558        // Get the device that the key is on.
559        let device = match key_internal.device.upgrade() {
560            Some(device) => device,
561            None => panic!("Device disappeared"),
562        };
563
564        Box::new(device)
565    }
566
567    async fn available(&self) -> bool {
568        log::trace!("Key::available");
569
570        true
571    }
572
573    async fn locked(&self) -> Protection {
574        log::trace!("Key::locked");
575
576        // XXX: We currently only support keys where the password is
577        // embedded in the specficiation file.
578        Protection::Unlocked
579    }
580
581    async fn password_source(&self) -> PasswordSource {
582        log::trace!("Key::password_source");
583
584        PasswordSource::Inline
585    }
586
587    async fn decryption_capable(&self) -> bool {
588        log::trace!("Key::decryption_capable");
589
590        let key_internal = self.inner.lock().await;
591        key_internal.desc.spec.capabilities.contains(
592            &sequoia_tpm::Capability::Decrypt)
593    }
594
595    async fn signing_capable(&self) -> bool {
596        log::trace!("Key::signing_capable");
597
598        let key_internal = self.inner.lock().await;
599        key_internal.desc.spec.capabilities.contains(
600            &sequoia_tpm::Capability::Sign)
601    }
602
603    async fn unlock(&mut self, password: Option<&Password>) -> Result<()> {
604        log::trace!("Key::unlock");
605
606        let key_internal = self.inner.lock().await;
607
608        // We currently only support keys where the password is
609        // embedded in the specficiation file.
610        let _password = if let Some(password) = password {
611            password
612        } else {
613            return Err(Error::NoExternalPassword(None).into());
614        };
615
616        log::trace!("KeyHandle::unlock({}): Already unlocked", self.fpr);
617        Err(Error::AlreadyUnlocked(key_internal.fingerprint.to_string()).into())
618    }
619
620    async fn lock(&mut self) -> Result<()> {
621        log::trace!("Key::lock");
622
623        Ok(())
624    }
625
626    async fn public_key(&self)
627        -> packet::Key<packet::key::PublicParts,
628                       packet::key::UnspecifiedRole>
629    {
630        log::trace!("Key::public_key");
631
632        let key = self.inner.lock().await;
633        key.public_key.clone()
634    }
635
636    async fn decrypt_ciphertext(&mut self,
637                                ciphertext: &mpi::Ciphertext,
638                                _plaintext_len: Option<usize>)
639        -> Result<SessionKey>
640    {
641        log::trace!("Key::decrypt_ciphertext");
642
643        let key_internal = self.inner.lock().await;
644
645        let pk_algo = key_internal.public_key.pk_algo();
646
647        #[allow(deprecated)]
648        let session_key = match (ciphertext, pk_algo) {
649            (mpi::Ciphertext::RSA { c },
650             PublicKeyAlgorithm::RSAEncryptSign
651             | PublicKeyAlgorithm::RSAEncrypt
652             | PublicKeyAlgorithm::RSASign) =>
653            {
654                let session_key = sequoia_tpm::decrypt(&key_internal.desc.spec,
655                                                       c.value())?;
656                SessionKey::from(&session_key[..])
657            }
658            _ => {
659                return Err(Error::OperationNotSupported(
660                    format!("Unsupported public key algorithm: {}", pk_algo)).into());
661            }
662        };
663
664        Ok(session_key)
665    }
666
667    async fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
668        -> Result<(PublicKeyAlgorithm, mpi::Signature)>
669    {
670        log::trace!("Key::sign({})", hash_algo);
671
672        let key_internal = self.inner.lock().await;
673
674        let pk_algo = key_internal.public_key.pk_algo();
675
676        let hash_algo = match hash_algo {
677            HashAlgorithm::SHA1 => sequoia_tpm::HashingAlgorithm::Sha1,
678            HashAlgorithm::SHA256 => sequoia_tpm::HashingAlgorithm::Sha256,
679            HashAlgorithm::SHA384 => sequoia_tpm::HashingAlgorithm::Sha384,
680            HashAlgorithm::SHA512 => sequoia_tpm::HashingAlgorithm::Sha512,
681            _ => {
682                return Err(Error::OperationNotSupported(
683                    format!("Unsupported hash algorithm: {}", hash_algo)).into());
684            }
685        };
686
687        let sig = sequoia_tpm::sign(&key_internal.desc.spec, hash_algo, digest)?;
688        #[allow(deprecated)]
689        let sig = match pk_algo {
690            PublicKeyAlgorithm::RSAEncryptSign
691                | PublicKeyAlgorithm::RSAEncrypt
692                | PublicKeyAlgorithm::RSASign =>
693            {
694                openpgp::crypto::mpi::Signature::RSA { s: sig.into() }
695            }
696            _ => {
697                return Err(Error::OperationNotSupported(
698                    format!("Unsupported public key algorithm: {}", pk_algo)).into());
699            }
700        };
701
702        Ok((pk_algo, sig))
703    }
704
705    async fn export(&mut self)
706        -> Result<openpgp::packet::Key<
707            openpgp::packet::key::SecretParts,
708            openpgp::packet::key::UnspecifiedRole>>
709    {
710        Err(Error::OperationNotSupported(
711            "Use an external tool to manage TPM keys.".into()).into())
712    }
713
714    async fn change_password(&mut self, password: Option<&Password>)
715        -> Result<()>
716    {
717        log::trace!("KeyHandle::change_password({}, {})",
718                    self.fingerprint(),
719                    if let Some(password) = password {
720                        if password.map(|p| p.is_empty()) {
721                            "clear password"
722                        } else {
723                            "set password"
724                        }
725                    } else {
726                        "ask for password"
727                    });
728
729        Err(Error::OperationNotSupported(
730            "Use an external tool to manage TPM keys.".into()).into())
731    }
732
733    async fn delete_secret_key_material(&mut self)
734        -> Result<()>
735    {
736        log::trace!("KeyHandle::delete_secret_key_material");
737
738        Err(Error::OperationNotSupported(
739            "Use an external tool to manage TPM keys.".into()).into())
740    }
741}
742
743#[cfg(test)]
744mod tests {
745    use super::*;
746
747    use openpgp::KeyHandle;
748    use openpgp::parse::Parse;
749    use openpgp::policy::StandardPolicy;
750
751    use backend::test_framework;
752
753    use backend::Backend as _;
754
755    // const TEST_DEVICE_ID: &str = "device:/dev/tpmrm0";
756
757    fn get_test_device() -> Option<()>
758    {
759        // XXX: Connect to a tpm server.
760        None
761    }
762
763    fn preinit() -> bool {
764        // Don't run the tests if the TPM device is not available.
765        get_test_device().is_some()
766    }
767
768    async fn init_backend() -> Backend {
769        let backend = Backend::init_ephemeral().await.expect("can init backend");
770        backend
771    }
772
773    async fn import_cert(backend: &mut Backend, cert: &Cert) {
774        log::debug!("Importing {}", cert.fingerprint());
775
776        // Get the internal device.
777        let mut backend_internal = backend.inner.lock().await;
778        let mut device = None;
779        for d in backend_internal.devices.iter_mut() {
780            if d.id == "internal" {
781                device = Some(d);
782                break;
783            }
784        }
785        let _device = if let Some(device) = device {
786            device
787        } else {
788            backend_internal.devices.push(Device {
789                id: "internal".to_string(),
790                inner: Arc::new(Mutex::new(DeviceInternal {
791                    id: "internal".to_string(),
792                    keys: Default::default(),
793                })),
794            });
795            backend_internal.devices.last_mut().unwrap()
796        };
797
798        // XXX: Convert each secret key into a Specification.
799
800        todo!("import_cert")
801    }
802
803    sequoia_keystore_backend::generate_tests!(
804        preinit,
805        true, // Serialize tests.
806        Backend, init_backend,
807        import_cert,
808        false, // Can import encrypted secret key material.
809        None, // Supported key sets.
810        None, // Default password.
811        false, // Can export.
812        false, // Can change password.
813        false // Can delete secret key material.
814    );
815}