#+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:ab:25:2c:59:fb:a1:2f:38:99:e3:8f:e5:4e:
d4:1b
pub:
04:12:b2:a3:f6:de:b2:74:df:7d:fa:6a:9e:6e:13:
de:a9:b9:51:5d:59:0a:66:69:66:de: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:cd: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:ab: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]
- [X] Creating new RSA keys and persisting them in TPM memory
- [X] Using non-persistent RSA keys (that don't use up TPM memory)
- [X] Importing RSA private keys to TPM (for already existing keys)
- [X] Signing using RSA keys in the TPM
- [X] Decryption using RSA keys in the TPM
** Support for Elliptic Curve algorithms [5/5]
- [X] Creating new EC keys and persisting them in TPM memory
- [X] Using non-persistent EC keys (that don't use up TPM memory)
- [X] Importing EC private keys to TPM (for already existing keys)
- [X] Signing using EC keys in the TPM
- [X] Decryption using EC keys in the TPM
** Key migration support [4/4]
- [X] Export of TPM encryption key
- [X] Wrapping user's private key using TPM encryption key
- [X] Import of the wrapper private key to the TPM chip
- [X] 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