1 unstable release

0.1.0 Aug 29, 2024

#2521 in Cryptography

Download history 233/week @ 2024-08-27 31/week @ 2024-09-03 4/week @ 2024-09-10

268 downloads per month
Used in 2 crates (via sequoia-keystore-tpm)

LGPL-2.0-or-later AND Apache-2.0

79KB
1K SLoC

#+TITLE: TPM for OpenPGP #+HTML_HEAD: #+PROPERTY: header-args :tangle yes :exports both

This crate implements bindings so that TPM chips can be used with OpenPGP applications.

  • Basic key usage

First, we assume that we'll use TPM 2 simulator package. If you want to test on real device set TCTI to device:/dev/tpmrm0.

#+begin_src sh set -e set -o pipefail

tpm_server &

sleep 5

tpm2_startup -c -T mssim

TCTI=mssim: PATH=$PATH:./target/debug

Increase verbosity of commands

export RUST_LOG=info #+end_src

To generate a number of random bytes using the specified TPM:

#+begin_src sh :var TCTI="device:/dev/tpmrm0" PATH="./target/debug" :exports both draw-bytes --tcti $TCTI #+end_src

#+RESULTS: : 46d2f84712cefc51c8bc124354f7daa0fecd2f6066963ab15b6b50a63248dd90

** Creating persistent keys

This crate uses descriptive documents for configuring key properties.

*** RSA

The following configuration creates RSA-2048 signing key and persists it at the handle 0x01000027. 123 is used as a sample auth value (PIN).

#+BEGIN_SRC yaml :tangle key.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000027 algo: RSA: bits: 2048 capabilities: - sign auth: 123 #+END_SRC

The key description (key.yml) is being read by the create-key binary that persists that key:

#+BEGIN_SRC sh create-key -f key.yml #+END_SRC

Presence of the key can be checked by using tpm2_getcap handles-persistent command from TSS suite of tools.

And the same file is used to retrieve it again using get-key binary:

#+BEGIN_SRC sh get-key -f key.yml #+END_SRC

#+RESULTS: : public_key: : RSA: : bytes: a6235b59c325e5f92752bf9e30b2b2f4cedab2ed43375e0be4fb0904775d0ef77f8385d338ded56ed4fbfae4edeb17cd56b81db28683a515aef7004a5f39dfe3c2c13d604f93c6345ab209efdb1b6ad1a5949b1d8d195b2854e1c2ee7d975bc5616b98913630c2915ed0a8574e86082deb960fa20f623155fd9c78c4ecb84c70dd05b60900c57397ab77c36fddb83870a65ea3e31d539cf9b9ca82ac6def43e9508e0ff4ecaf6a186974fd226b6d0af3eae2b91330cdc27303dbbca38ecb73b7844ee42c3994b854e6dcfe30a1c433cb5c432a9f261999ff0cffaa38b068c0c1110ad97a99042f6cea0d65c43ff684b7ba45e95ddbb05110b8c9d560cd7f5331 : manu: 1229081888 : name: 000b6c69b7da1d7391d0046fa805915520adb39bb419554c881cfdfee56b69d6d68d

We can turn this into a not quite valid OpenPGP certificate using the get-openpgp-key binary:

#+BEGIN_SRC sh get-openpgp-key -f key.yml #+END_SRC

#+RESULTS: : -----BEGIN PGP PUBLIC KEY BLOCK----- : : xsBNBAAAAAABCADYAHeYhUkqnjcSiAu67+NINl7/ObsPq/kiYuAbPZbvxyOrae7i : 4yoMpGh+BWIYL/Q0PL063lCRvLTiIMAsx0HDl2Xlo4SQxp3ae/rYrgsbUh7Efzf6 : +zsFcGGBVUQe1RbWseDAmpcGWAClTZg3/Eqe87nVaLTvac5Ns2CXwxs1d150PEP+ : 8ZWR8M9ERRH1Yess7MPF88GJEuoGBQACbDOFSGgs6JQxPlSEbg5LPkISUMmoROZ8 : 5HDpCfS3ofBluDw5EWRSj9EuCkWBNAUA4saGM0IwS161bfCecIPAqCaFPD7PQ2KU : 4sBq1wkOdLpveOdFDGAC2A7hXn6h82S8OfBxABEBAAE= : =bZUC : -----END PGP PUBLIC KEY BLOCK-----

Creating decryption key is just as strightforward:

#+BEGIN_SRC yaml :tangle decryption.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000018 algo: RSA: bits: 2048 capabilities: - decrypt auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f decryption.yml #+END_SRC

*** EC: NIST-P256

The following configuration creates NIST-P256 signing key and persists it at the handle 0x01000127. 123 is used as a sample auth value (PIN).

#+BEGIN_SRC yaml :tangle key-nist-p256.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000127 algo: EC: curve: NIST-P256 capabilities: - sign auth: 123 #+END_SRC

The key description (key-nist-p256.yml) is being read by the create-key binary that persists that key:

#+BEGIN_SRC sh create-key -f key-nist-p256.yml #+END_SRC

Presence of the key can be checked by using tpm2_getcap handles-persistent command from TSS suite of tools.

And the same file is used to retrieve it again using get-key binary:

#+BEGIN_SRC sh get-key -f key-nist-p256.yml #+END_SRC

#+RESULTS: : public_key: : EC: : x: b998133e8339fc3680808ef64c41fdceb791ccc0c4e1906b99bfd134e59be38c : y: 830dc6c759441d30c843f1d5e27d5afa65dd6190359498bd57d3b5c984704ae9 : manu: 1229081888 : name: 000b064deda7eaebd1f0ca982fc4adcc20d6c90d64d72de5277f072ba3633de848ba

Creating decryption key is just as strightforward:

#+BEGIN_SRC yaml :tangle decryption-nist-p256.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000118 algo: EC: curve: NIST-P256 capabilities: - decrypt auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f decryption-nist-p256.yml #+END_SRC

*** EC: NIST-P384

The following configuration creates NIST-P384 signing key and persists it at the handle 0x01000227. 123 is used as a sample auth value (PIN).

#+BEGIN_SRC yaml :tangle key-nist-p384.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000227 algo: EC: curve: NIST-P384 capabilities: - sign auth: 123 #+END_SRC

The key description (key-nist-p384.yml) is being read by the create-key binary that persists that key:

#+BEGIN_SRC sh create-key -f key-nist-p384.yml #+END_SRC

Presence of the key can be checked by using tpm2_getcap handles-persistent command from TSS suite of tools.

And the same file is used to retrieve it again using get-key binary:

#+BEGIN_SRC sh get-key -f key-nist-p384.yml #+END_SRC

#+RESULTS: : public_key: : EC: : x: b5d6885b6774c8a1a944b4559f26b931df031c893bc05139fc54c876b01401253ecea26ea17fa70c017bb5b4d6bb5885 : y: d29cda6bc9742e49b030db3ec9004217ba8fd052b7d26fc7bddbbe7cb9854fabf7cdc5978ebb8fed9383d387a07bcdf9 : manu: 1229081888 : name: 000b8e28e3d95570efc686bb21f5329a658fe09321d38b496fe02749251e28a07ef5

Creating decryption key is just as strightforward:

#+BEGIN_SRC yaml :tangle decryption-nist-p384.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000218 algo: EC: curve: NIST-P384 capabilities: - decrypt auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f decryption-nist-p384.yml #+END_SRC

** Creating non-persistent keys

Non persistent keys allow using unlimited number of keys that never use up TPM memory.

*** RSA

Keys need to be wrapped using a key parent that itself needs to be persistent:

#+BEGIN_SRC yaml :tangle parent.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000028 algo: RSA: bits: 2048 capabilities: - decrypt - restrict auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f parent.yml #+END_SRC

Then, we can create non-persistent key:

#+BEGIN_SRC yaml :tangle child.yml spec: provider: tpm: tcti: "mssim:" parent: 0x81000028 algo: RSA: bits: 2048 capabilities: - sign auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f child.yml | tee child-full.yml #+END_SRC

Inspecting child-full.yml reveals that the tpm section has been extended with two new properties: private and unique. This is the private key wrapped (encrypted) using the parent key:

#+BEGIN_SRC yaml spec: provider: tpm: tcti: "mssim:" parent: 0x81000028 private: 002035531cd18d59c7e358b63b1f89ed3b2fdd12176ed5c02f5d68dfbf7f872c65ae00107170ee9bc217b4a7ed59ad11a1387aef195031690b01d6d3acd6b4f63d16006bb33737392dd1ba9753bcf81227e3dffecddc082821994e41c047e325d82ee2c3106e94d5f5bbcd935e6f80e2321f24012a24be73f231c9f6606d927016b3afd73b96df2e3f5a181cfbe436da9cf9bcefa1a1513cb63e8021fb9ad2cc81bce55d9651aa7ed8aeaccba7ba98834d759e9f3b30e21953e65a12742bc253dfbef1e8e158fcc9755acd08e3f4af4183b7b008c4ec0865b48315d346be unique: RSA: bytes: aa79ea1d9800af8b6556562c27dca2be827d7ca1facfd056c4effe79dca366e948e4b0f5253392ce4ea274c84f609e57edfd4848cf10e87e19b22e4bf27fc3560a8e6405a1a339969ce6d00bc4b32e1398be63f59af4c7337b4079817fd231d379dd437cb35910ce13337a6af0877c88ac2f8bc86dd902de3ffd10bdc6c5f284063f95c2c2487942472f34551691fdf8ae0f30a7a188bc73ecb776bef2a959be2cc89b425247030e2a921d505bb71e19100b17028b74e39e673dd1d35603fea424d44913e84c7744128ec2d82853d34062ea9476557a4458c70c05d7efd205ee6b89aa7b0b84daaecbf4075db8fcee2ed622dca2ee8e391e457cc88f3ac39b7d algo: RSA: bits: 2048 capabilities: - sign auth: 123 #+END_SRC

Except for the different configuration this key is perfectly usable in all operations:

#+BEGIN_SRC sh get-key -f child-full.yml #+END_SRC

#+RESULTS: : public_key: : RSA: : bytes: c4c1c097f96afae8de9c3a3ece841f510acca20ed417c890e9626205672fbceaf21bb92ff680897aeb4418c52c146c5f7bab0f44762e64bea6228f7617d493b3399110339da3513f3864acf7f977b092e63200da83a31d8640a6cb50761bf90c868b35240097d85053a55e25043fcab4367c4881050aa7b52c71d2dc0155afbfd3ab50c6223e8dd119d6c7270b0d5e5c672fa8d809a38d53c98b2d126927ad6f29f243247ff56ffe0378a6fcfc09a5ef998e9b31158ae68aa323b4f6f3650c17e5ea82e131f533d3c88c6241421b0998e63e60cec498a150db07f4969430d04700ad41172b3ebc74854223128821cc16d7f7e019269909418cf4a2eff93bc92d : manu: 1398033696 : name: 000bc287d88098837a6fa7732ac5f1735996a4b5e7827fb0e82177b763b31654c77c

*** EC: NIST-P256

Keys need to be wrapped using a key parent that itself needs to be persistent:

#+BEGIN_SRC yaml :tangle parent-nist-p256.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000328 algo: EC: curve: NIST-P256 capabilities: - decrypt - restrict auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f parent-nist-p256.yml #+END_SRC

Then, we can create non-persistent key:

#+BEGIN_SRC yaml :tangle child-nist-p256.yml spec: provider: tpm: tcti: "mssim:" parent: 0x81000328 algo: EC: curve: NIST-P256 capabilities: - sign auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f child-nist-p256.yml | tee child-nist-p256-complete.yml #+END_SRC

Inspecting child-nist-p256-complete.yml reveals that the tpm section has been extended with two new properties: private and unique. This is the private key wrapped (encrypted) using the parent key:

#+BEGIN_SRC yaml spec: provider: tpm: tcti: "mssim:" parent: 0x81000328 private: 0020f02ddfa535dfae96031629c001868c0c28358df4d8d8784536a22faa7fea90020010a27a2d5a839d5bece4c50110e189dbf67d76f6f7f68a71301791fe0db8c187b1495621d1b4776fdc2f8b184451d16fd1aacc8261005df7c86058a7fa1609dce2e5a8ec7c631398b2e57e288dbe99059de30cfabdbcd057c53763 unique: EC: x: 1f93e6eb830bfb22b6ac482f3c41770a65ab6478c5c0c4d0758b250289defc0b y: c211a231b6d5313a8a78af4a621ce7766ca1c000c59e904ed3f1fa38ff54cb72 algo: EC: curve: NIST-P256 capabilities: - sign auth: 123 #+END_SRC

Except for the different configuration this key is perfectly usable in all operations:

#+BEGIN_SRC sh get-key -f child-nist-p256-complete.yml #+END_SRC

#+RESULTS: : public_key: : EC: : x: 1f93e6eb830bfb22b6ac482f3c41770a65ab6478c5c0c4d0758b250289defc0b : y: c211a231b6d5313a8a78af4a621ce7766ca1c000c59e904ed3f1fa38ff54cb72 : manu: 1398033696 : name: 000b1a7ea2e65a4c70d21c2af706ed370b20a56b28f644d19b7501345910a3a3e7cd

*** EC: NIST-P384

Keys need to be wrapped using a key parent that itself needs to be persistent:

#+BEGIN_SRC yaml :tangle parent-nist-p384.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000428 algo: EC: curve: NIST-P384 capabilities: - decrypt - restrict auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f parent-nist-p384.yml #+END_SRC

Then, we can create non-persistent key:

#+BEGIN_SRC yaml :tangle child-nist-p384.yml spec: provider: tpm: tcti: "mssim:" parent: 0x81000428 algo: EC: curve: NIST-P384 capabilities: - sign auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f child-nist-p384.yml | tee child-nist-p384-complete.yml #+END_SRC

Inspecting child-nist-p384-complete.yml reveals that the tpm section has been extended with two new properties: private and unique. This is the private key wrapped (encrypted) using the parent key:

#+BEGIN_SRC yaml spec: provider: tpm: tcti: "mssim:" parent: 0x81000428 private: 0020fb1df208021cab7898d4edba2e87966a62e4e95820ad695b8d5af40351361f9100102e21b1075be238d84a9b98471879ec2cc415b4b0309edf82dd6b5a00fa557b9dc06ea752ea36fb4dea4f9a47c5884e1d43e0fcbd40db477e4147264f202145e1c995f411406c82d444a91d67edf69c824737e32057728d9f04193b469b3759bbb033673fafec5db9fa86 unique: EC: x: cf159a49527490e60ba5cede361ca82a43d41e6754e8ddea1f57fdba9e05bd49ed62bb982994407801f95c366f85ef43 y: 322ee52ffde0fe5f85367f801a0cbc5f05a772e6ac86027eed64a02303683b2caa1adb0674645533cb578284ee86eaab algo: EC: curve: NIST-P384 capabilities: - sign auth: 123 #+END_SRC

Except for the different configuration this key is perfectly usable in all operations:

#+BEGIN_SRC sh get-key -f child-nist-p384-complete.yml #+END_SRC

#+RESULTS: : public_key: : EC: : x: cf159a49527490e60ba5cede361ca82a43d41e6754e8ddea1f57fdba9e05bd49ed62bb982994407801f95c366f85ef43 : y: 322ee52ffde0fe5f85367f801a0cbc5f05a772e6ac86027eed64a02303683b2caa1adb0674645533cb578284ee86eaab : manu: 1398033696 : name: 000b1a7ea2e65a4c70d21c2af706ed370b20a56b28f644d19b7501345910a3a3e7cd

** Importing private keys

It is also possible to import already existing private keys into the TPM.

*** RSA

#+BEGIN_SRC yaml :tangle private-key.yml spec: provider: tpm: tcti: "mssim:" algo: RSA: bits: 1024 private: rsa: prime: f69495352f2ab58db89a0a6ddb060ca0baa5ec190d1d61f0fae32cdfb7516fc9e4968b5c494c057f35dfe69136fe35434f0a3b8979551347c47a357abad0ad0b modulus: bytes: cd1abae5d734341ad373bae4f9ef46b1cf699d4054c859b9c0f0c811ca4d7b1cb03c66ea655156639b78c5db2c2fea42430f417ab3d4aee5f63b881dd106a3c60105bc46bb18c7a794a17f50392405551f77287e61b5f784354cd351021e1853b0cfd3470d4cc9bd9e39836b83c1be6bb200fef56786406e8cd45f73e4a9f523 exponent: 65537 capabilities: - sign auth: 123 #+END_SRC

Using these keys is the same as for any other type of key:

#+BEGIN_SRC sh :results verbatim get-key -f private-key.yml #+END_SRC

#+RESULTS: : --- : public_key: : RSA: : bytes: cd1abae5d734341ad373bae4f9ef46b1cf699d4054c859b9c0f0c811ca4d7b1cb03c66ea655156639b78c5db2c2fea42430f417ab3d4aee5f63b881dd106a3c60105bc46bb18c7a794a17f50392405551f77287e61b5f784354cd351021e1853b0cfd3470d4cc9bd9e39836b83c1be6bb200fef56786406e8cd45f73e4a9f523 : manu: 1398033696 : name: 000bb0369f40552df81f3bda82053e1974ffb5e2ca32999c602ee67428703e8211ad :

*** EC: NIST-P256

To generate a new P-256 key use the following openssl command:

#+BEGIN_SRC sh :results verbatim openssl ecparam -name secp256r1 -genkey -noout | openssl ec -in - -inform engine -text -noout -conv_form uncompressed #+END_SRC

#+RESULTS: #+begin_example Private-Key: (256 bit) priv: 98:20:9e:ea:87:b0:63:ec:a8:51:09:ef:1b:8a:46: c0:97🆎25:2c:59:fb:a1:2f:38:99:e3:8f:e5:4e: d4:1b pub: 04:12:b2:a3:f6🇩🇪b2:74:df:7d:fa:6a:9e:6e:13: de:a9:b9:51:5d:59:0a:66:69:66🇩🇪fe:f5:f8:6d: 1e:ca:df:47:95:cf:10:a8:94:22:6f:17:78:dc:d4: 18:5d:b5:c4:d4:2a:9c:10:59:1a:22:11:81:2e:f5: b6:54:09:4f:81 ASN1 OID: prime256v1 NIST CURVE: P-256 #+end_example

The =ec.parameter= value should reflect the =priv= field with all bytes concatenated. The public part: =ec.points= would be two halves of the =pub= openssl value (omitting first =04= byte).

#+BEGIN_SRC yaml :tangle private-key-nist-p256.yml spec: provider: tpm: tcti: "mssim:" algo: EC: curve: NIST-P256 private: ec: parameter: 98209eea87b063eca85109ef1b8a46c097ab252c59fba12f3899e38fe54ed41b points: x: 12b2a3f6deb274df7dfa6a9e6e13dea9b9515d590a666966defef5f86d1ecadf y: 4795cf10a894226f1778dcd4185db5c4d42a9c10591a2211812ef5b654094f81 capabilities: - sign auth: 123 #+END_SRC

Using these keys is the same as for any other type of key:

#+BEGIN_SRC sh get-key -f private-key-nist-p256.yml #+END_SRC

#+RESULTS: : public_key: : EC: : x: 12b2a3f6deb274df7dfa6a9e6e13dea9b9515d590a666966defef5f86d1ecadf : y: 4795cf10a894226f1778dcd4185db5c4d42a9c10591a2211812ef5b654094f81 : manu: 1229081888 : name: 000b23684f8b125caad589545052d8779253a6ef854f9290f7c8b670cb2c4165aa18

*** EC: NIST-P384

To generate a new P-384 key use the following openssl command:

#+BEGIN_SRC sh :results verbatim openssl ecparam -name secp384r1 -genkey -noout | openssl ec -in - -inform engine -text -noout -conv_form uncompressed #+END_SRC

#+RESULTS: #+begin_example Private-Key: (384 bit) priv: a1:79:12:49:9c:40:12:98:ed:ec:db:89:f7:a3:08: 75:74:6f:0e:fc:44:3e:be:d1:3a:05:3f:1a:2f:c6: 45:97:3d:d3:5a:93:27:1c:6d:7f:25:79:36:95:bd: 1c:4f:be pub: 04:e1:d7:38:d7:54:2b:83:b2:e8:bd:4d💿03:6f: f8:1c:a2:ed:08:30:1c:26:34:d2:c4:24:6c:3e:79: ae:e9:90:36:7c:f7:3b:c2:2c:29:50:da:e9:98:d7: 97:a3:95:75:5e:cc:c5:61:a0:38:fd:76:ce:60:2a: 7a:6c:0e:f4:51:db:3f:75:21:ac🆎96:50:f7:77: 09:b5:32:69:2d:93:23:98:e2:aa:09:ae:18:e4:20: db:16:56:57:12:c3:6f ASN1 OID: secp384r1 NIST CURVE: P-384 #+end_example

The =ec.parameter= value should reflect the =priv= field with all bytes concatenated. The public part: =ec.points= would be two halves of the =pub= openssl value (omitting first =04= byte).

#+BEGIN_SRC yaml :tangle private-key-nist-p384.yml spec: provider: tpm: tcti: "mssim:" algo: EC: curve: NIST-P384 private: ec: parameter: 595e7774730018cc3942e4b713c2a288b8dbcec147ede1ed3c3760553bc39a7a092db968df4da71267c9586e69e6ffc7 points: x: 88eae33668dfc22f1bec8ca87bef7dab67562b1b1bf10101b5a655212b31356d46963624e11f0b30ffb7bc60f315fb09 y: 5c1ec2296140c2404a605e6c65b85c10d3e5807feb4d15f674e4318c7887e03408e98a348c413b16ad615484ed84cf2f capabilities: - sign auth: 123 #+END_SRC

Using these keys is the same as for any other type of key:

#+BEGIN_SRC sh get-key -f private-key-nist-p384.yml #+END_SRC

#+RESULTS: : public_key: : EC: : x: 88eae33668dfc22f1bec8ca87bef7dab67562b1b1bf10101b5a655212b31356d46963624e11f0b30ffb7bc60f315fb09 : y: 5c1ec2296140c2404a605e6c65b85c10d3e5807feb4d15f674e4318c7887e03408e98a348c413b16ad615484ed84cf2f : manu: 1398033696 : name: 000b4c2c9cf06ff4af433703b1459bf1529311fcc7a24c6e407594ba071a7bc82060

** Signing digests

*** RSA

Signing uses raw RSA keys and produces raw PKCS1.5 signatures for now.

Ultimately these raw objects can be wrapped with protocol-specific structures e.g. certificates (for raw RSA keys) or OpenPGP signatures (for raw signatures).

Signing can use any key that has been defined previously:

#+BEGIN_SRC sh :results output echo -n foo | openssl dgst -binary -sha256 | sign-digest -f key.yml | xxd #+END_SRC

#+RESULTS: #+begin_example 00000000: a2a7 066e 813b 0ae9 a978 2f78 dbb1 c25d ...n.;...x/x...] 00000010: 3402 fca2 106e 4052 ef3f e370 399d e95f 4....n@R.?.p9.._ 00000020: 45d3 5f56 f915 5f81 c9e9 6b4b ff27 9529 E.V.....kK.'.) 00000030: 591c 0cf2 6a19 18d5 af6a e2e1 161b b950 Y...j....j.....P 00000040: cbfe 715b 201c e1dc 6691 f862 9e1b ca87 ..q[ ...f..b.... 00000050: 2313 f774 f689 dd5b e28f 9c9b 275c 6432 #..t...[....'\d2 00000060: e491 533a 5509 bd9b 5ddf 8403 81cb e341 ..S:U...]......A 00000070: 2fc7 23e9 9c93 4170 48e7 cdda 3c07 0151 /.#...ApH...<..Q 00000080: dafd 00bb 352e dacc 33a9 a087 9a9d 93cf ....5...3....... 00000090: 4dff d59d 7f19 ca68 3d6e e3e7 26f5 17d4 M......h=n..&... 000000a0: c683 677e c039 dd4e 27ff f2db f354 9fe1 ..g~.9.N'....T.. 000000b0: 6e7a 1ea5 c215 ba4d 44c1 5f72 0bce 1fe9 nz.....MD._r.... 000000c0: 53c2 3cbf 8412 d610 784d 6cf5 aa56 2c87 S.<.....xMl..V,. 000000d0: 48a2 dbdf 3944 9ae3 94ae 2a57 98bb 420c H...9D....W..B. 000000e0: 842e 2aa6 7dd2 1842 7ef4 5208 3b47 d410 ...}..B~.R.;G.. 000000f0: 137f 9292 8d94 d5e3 64c0 2a2b e4e8 4342 ........d.*+..CB #+end_example

*** EC: NIST-P256

Signing uses raw elliptic curve keys and produces a concatenation of R and S values.

Ultimately these raw objects can be wrapped with protocol-specific structures e.g. certificates (for raw RSA keys) or OpenPGP signatures (for raw signatures).

Signing can use any key that has been defined previously:

#+BEGIN_SRC sh :results output echo -n foo | openssl dgst -binary -sha256 | sign-digest -f key-nist-p256.yml | xxd #+END_SRC

#+RESULTS: : 00000000: 2b10 9aca a8ec 800c 4b50 b35a a62e 6f52 +.......KP.Z..oR : 00000010: 5bc3 a3c9 5c68 bd2a 4588 b7e8 94f6 2923 [...\h.*E.....)# : 00000020: f3e4 b073 82a1 42b4 1139 e5d0 d7a3 996d ...s..B..9.....m : 00000030: 8893 a60a 6171 ddc1 ecb6 2992 8382 d8d2 ....aq....).....

*** EC: NIST-P384

Signing uses raw elliptic curve keys and produces a concatenation of R and S values.

Ultimately these raw objects can be wrapped with protocol-specific structures e.g. certificates (for raw RSA keys) or OpenPGP signatures (for raw signatures).

Signing can use any key that has been defined previously:

#+BEGIN_SRC sh :results output echo -n foo | openssl dgst -binary -sha256 | sign-digest -f key-nist-p384.yml | xxd #+END_SRC

#+RESULTS: : 00000000: a33b 5ed4 bbbc f6bd 6297 c696 fc10 5ae4 .;^.....b.....Z. : 00000010: 4a32 d807 a065 ea75 19b9 7d2f 9f05 8e09 J2...e.u..}/.... : 00000020: a6b6 028b 2eb7 9c7f ab6e 8701 61a3 e39d .........n..a... : 00000030: 23db fc1b b859 e2dd 20e5 ebc9 3503 c671 #....Y.. ...5..q : 00000040: 00e2 057c 3b00 86fa 84e8 4152 3b9d 9e70 ...|;.....AR;..p : 00000050: 5a19 05a0 f13f 64f6 ddd7 5edd 764c 6cea Z....?d...^.vLl.

** Decryption

*** RSA

Encryption and decryption works similarily to signing. The plaintext is being passed as standard input to =encrypt-raw= commmand and it outputs the raw cipher text. =decrypt-raw= works in the other direction consuming the cipher text and producing the plain text.

Both of these take the key defintion as a sole argument.

#+BEGIN_SRC sh :results output echo this is a sample encryption message | encrypt-raw -f decryption.yml > encrypted decrypt-raw -f decryption.yml < encrypted #+END_SRC

#+RESULTS: : this is a sample encryption message

*** ECDH

Encryption and decryption with EC keys works a little bit differently. EC key is used to generate two points: one is a public point that will be shared with the other party, the other is used as a symmetric key for encryption of the actual data.

First, we need an EC key with =decrypt= capability. The key cannot be marked as =restrict= as that will prevent decryption.

#+BEGIN_SRC yaml :tangle key-nist-p256-decryption.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000147 algo: EC: curve: NIST-P256 capabilities: - decrypt auth: 123 #+END_SRC

Then we generate two points: public point and the shared secret:

#+BEGIN_SRC sh create-key -f key-nist-p256-decryption.yml

create a shared secret and public point

ecdh-key-gen -f key-nist-p256-decryption.yml --public-point public.bin > shared-secret.bin

echo "Public point:" xxd public.bin

echo "Shared secret:" xxd shared-secret.bin #+END_SRC

And we use the shared secret to encrypt the message. Shared secret is removed as it is no longer necessary. The public point, along with the encrypted message, is transferred.

#+BEGIN_SRC sh

encrypt the message using shared secret

echo this is a sample encryption message | openssl aes-256-cbc -e -kfile shared-secret.bin > encrypted

remove shared secret, move public point and encrypted message to the other party

rm shared-secret.bin #+END_SRC

The decrypting party first recovers the shared secret using the public point. Then the shared secret is passed to symmetric algorithm as a key (here using OpenSSL):

#+BEGIN_SRC sh

recover shared secret using private key and the public point

ecdh-recover -f key-nist-p256-decryption.yml --public-point public.bin > shared-secret.bin

decrypt the message

openssl aes-256-cbc -d -kfile shared-secret.bin -in encrypted #+END_SRC

#+RESULTS: : this is a sample encryption message

This concludes our basic TPM usage section. TPM will be cleared discarding all keys:

#+BEGIN_SRC sh tpm2_clear #+END_SRC

  • Key duplication

Key duplication allows secure private key material transfer from one machine (e.g. offline computer) to the other (e.g. online computer).

The main benefit is that the online computer never sees private key bits in plain. They are encrypted to the storage key that is stored in the TPM chip. The encrypted private key is decrypted by the TPM during import.

On online laptop, export the TPM key that will serve as parent for the imported key. This parent key needs to have =decrypt= and =restrict= capabilities.

#+BEGIN_SRC yaml :tangle duplication-parent.yml spec: provider: tpm: tcti: "mssim:" handle: 0x81000027 algo: RSA: bits: 2048 capabilities: - decrypt - restrict auth: 123 #+END_SRC

#+BEGIN_SRC sh create-key -f duplication-parent.yml #+END_SRC

Retrieve the key's public key bits and transfer them to the offline computer:

#+BEGIN_SRC sh get-key -f duplication-parent.yml | tee duplication-parent-full.yml #+END_SRC

#+RESULTS: #+begin_example spec: provider: tpm: tcti: "device:/dev/tpmrm0" handle: 2164260888 parent: ~ private: ~ unique: RSA: bytes: bd1fdfb6ad445dd24bd9150886a7ea392863bf2864f8105bca870349150691309581f08271d93a7286e6d8126df38ca51b4d5366a867461743c842d0e4d6867cc81e8d8a96b6c7b01d702d1674d6432ac686d9e1d9b767b46d93d640c9ddcf952c46690231711ccd040d7b85453acb9a857040f49208823315b970e0ec3c15f31a1d6d17238a6b1e717020946ba2e8591f5aa36a3d65b4ac166755e54609355c2517dafc6c545f322093dd6ad01b33931c9f25ef3e47e61bf5d2a2b553af3fef8c2180267b76857768d38e5954b90362923df57ded9a9264cc56a120c48d2f47e6d7dc7a069f2a2c7b4d4079a599df8e672bca9540dcd024bcdd45cfc6450653 algo: RSA: bits: 2048 exponent: ~ private: ~ capabilities: - decrypt - restrict auth: "123" #+end_example

Now, taking the private key and wrapping it with the parent's key:

#+BEGIN_SRC sh wrap --parent duplication-parent-full.yml -f private-key.yml | tee key-to-import.yml #+END_SRC

#+RESULTS: #+begin_example spec: provider: tpm: tcti: "mssim:" parent: 2164260903 unique: RSA: bytes: cd1abae5d734341ad373bae4f9ef46b1cf699d4054c859b9c0f0c811ca4d7b1cb03c66ea655156639b78c5db2c2fea42430f417ab3d4aee5f63b881dd106a3c60105bc46bb18c7a794a17f50392405551f77287e61b5f784354cd351021e1853b0cfd3470d4cc9bd9e39836b83c1be6bb200fef56786406e8cd45f73e4a9f523 wrapped: secret: 41ccb56cfbfc120d0aa54bfd01c29e827bf58f70010cbc035eac87939eb0928c40e3a38a397fd03ad7c1b105beba154e2687ed40125e77c32f2979725940619cb5f2ae0f9238f10e593baabdf86a8ab02724c45d3d32bee36f18899387b91102d92d7fcc434d3b19599ad1ba417f3be30a1e2c4a686c472b34e3052193d8b33d94ecf5b10de590b2a275c443fdc31fa65558f074320aabe9bf79c3d8db34d108b026c4803ea342f179cebff89c84cb172127b6b517c1537bbcd05d551016dc886f7115b3a74265df5332da70a49e6981ab1a441307bcac0ba54a2505e74bd6df490075795bb39f9c63abbe02c632020786d6d89a88e4dc32b8f553f5805b3e0d private: 0020dccc065f0fc4c8db35c0a26f7742df8efa0fa7dfe7964d6e5f004405aa1fff280abec442094816fc2b51f58ea277ab49b61db7ca007f4387f251a8f1db2af77cf7fdd6ded9d793d91891c9cd7e47e18bffc3b2280bbd0e3a5b8685cfe934a199d57db474592194cd29f5701f8042318062685103e987caa88b7e8ba5cc741c576f37b4d545d0d3ed2fa2 data: "" policy: 09bd2ec618ec5d4688b2861cd8aedbbce1c1dd0b9e31e4a12f837750b33831e2 algo: RSA: bits: 1024 capabilities: - sign auth: "123" #+end_example

The duplicated key can then be imported:

#+BEGIN_SRC sh create-key -f key-to-import.yml | tee duplicated-key.yml #+END_SRC

#+RESULTS: #+begin_example spec: provider: tpm: tcti: "mssim:" parent: 2164260903 private: 00202a2ee14685008e505bf6284cd2250f4071d6cfa06e344d5e1462acb8cad0cdba0010058b8588770859c9271d31729a56b3f4d9d2b01e45e9209cbeecb7b95aa7ff86edbc8ca350abcbd28391bea433ec9eb82e8490821669d34d2362b4558c034305b5c8d51417751efad8d414e0df781785e56f8bab9395655d0a753fdf17efe0f4e6df85b8958e9df7bb29371ab22cd49e82bd1da05d4e15d971ae unique: RSA: bytes: cd1abae5d734341ad373bae4f9ef46b1cf699d4054c859b9c0f0c811ca4d7b1cb03c66ea655156639b78c5db2c2fea42430f417ab3d4aee5f63b881dd106a3c60105bc46bb18c7a794a17f50392405551f77287e61b5f784354cd351021e1853b0cfd3470d4cc9bd9e39836b83c1be6bb200fef56786406e8cd45f73e4a9f523 policy: 09bd2ec618ec5d4688b2861cd8aedbbce1c1dd0b9e31e4a12f837750b33831e2 algo: RSA: bits: 1024 capabilities: - sign auth: "123" #+end_example

Note that the imported key has =wrapped= key set. Import procedure checks the integrity of the key and if the encrypted seed can be successfully imported the =wrapped= key is removed and a regular =private= value is being inserted.

The duplicated key can be inspected for public key:

#+BEGIN_SRC sh get-key -f duplicated-key.yml #+END_SRC

It also works the same way as any other key:

#+BEGIN_SRC sh echo -n foo | openssl dgst -binary -sha256 | sign-digest -f duplicated-key.yml | xxd #+END_SRC

#+RESULTS: #+begin_example 00000000: ba01 7074 b3b4 07bd 9ea5 28fb a04f 1f83 ..pt......(..O.. 00000010: 8fa5 6965 e2de 71cb d320 4332 60a9 f088 ..ie..q.. C2`... 00000020: 7725 5145 5688 9a12 97fa 5ad8 7c6f 3213 w%QEV.....Z.|o2. 00000030: d21d 4c84 4888 e3f6 4eab 988c 5b72 eb65 ..L.H...N...[r.e 00000040: d88b 16b3 473b 91d8 053e 05de 5733 208e ....G;...>..W3 . 00000050: e8ad 6a3e 22eb 349d 1798 ef0e 8924 2f35 ..j>".4......$/5 00000060: 2dce 3af5 e1d3 47b9 cb9e 9ccd bc63 7e91 -.:...G......c~. 00000070: 23e6 cac0 83e9 10a8 0aaa 7a06 6579 87ee #.........z.ey.. #+end_example

  • Work plan

Work on this project is being sponsored by NLnet. See https://nlnet.nl/project/Sequoia-TPM/ for details.

** Signing and decryption using RSA keys [5/5]

  • Creating new RSA keys and persisting them in TPM memory
  • Using non-persistent RSA keys (that don't use up TPM memory)
  • Importing RSA private keys to TPM (for already existing keys)
  • Signing using RSA keys in the TPM
  • Decryption using RSA keys in the TPM

** Support for Elliptic Curve algorithms [5/5]

  • Creating new EC keys and persisting them in TPM memory
  • Using non-persistent EC keys (that don't use up TPM memory)
  • Importing EC private keys to TPM (for already existing keys)
  • Signing using EC keys in the TPM
  • Decryption using EC keys in the TPM

** Key migration support [4/4]

  • Export of TPM encryption key
  • Wrapping user's private key using TPM encryption key
  • Import of the wrapper private key to the TPM chip
  • PR to the upstream rust-tss-esapi crate

** Design and implementation of private key store crate [/]

  • Implementation of Sequoia's Decryptor and Signer traits for TPM keys
  • API for managing TPM keys
  • API for key migration

** Test harness using a TPM simulator [/]

  • Integration tests for creating, importing keys
  • Test cases for encryption (using Sequoia) and decryption (using TPM crate)
  • Tests for key migration

** Extending Sequoia's CLI to support private key store [/]

  • Extension to the CLI to allow specifying the location of the private key store
  • Modification to the sourec code not to rely on software private keys

** Documentation for tools and the API [/]

  • Making sure all functions and items are documented
  • Including README and end-user documentation on how to use the project
  • Adding best practices document

Dependencies

~19–27MB
~399K SLoC