├── testdata ├── pk11.config ├── pk11.abskeyfile.config ├── pk11.badkeyfile.config ├── pk11.missingkeyfile.config ├── broken.key └── correct.key ├── bootstrap.sh ├── testscripts ├── all.sh ├── simple-key.exp └── pin-key.exp ├── FAQ ├── .travis.yml ├── .gitignore ├── doc ├── Makefile.am ├── stpm-verify.1.yodl ├── stpm-verify.1 ├── stpm-sign.1 ├── stpm-sign.1.yodl ├── stpm-exfiltrate.1.yodl ├── stpm-exfiltrate.1 ├── simple-tpm-pk11.7 ├── simple-tpm-pk11.7.yodl ├── stpm-keygen.1 └── stpm-keygen.1.yodl ├── LICENSE ├── src ├── libgtest.cc ├── common_test.cc ├── internal.h ├── verify_test.cc ├── test_util.h ├── wrap_main.cc ├── tspiwrap.h ├── tspiwrap.cc ├── exfiltrate.cc ├── verify.cc ├── session.h ├── sign.cc ├── keygen.cc ├── keygen_test.cc ├── sign_test.cc ├── common.h ├── pk11_test.cc ├── fake_tspi.cc ├── pk11.cc ├── session.cc └── common.cc ├── TPM-TROUBLESHOOTING ├── configure.ac ├── Makefile.am ├── README.md └── check-srk └── check-srk.cc /testdata/pk11.config: -------------------------------------------------------------------------------- 1 | key correct.key 2 | -------------------------------------------------------------------------------- /testdata/pk11.abskeyfile.config: -------------------------------------------------------------------------------- 1 | key /dev/null 2 | -------------------------------------------------------------------------------- /testdata/pk11.badkeyfile.config: -------------------------------------------------------------------------------- 1 | key broken.key 2 | -------------------------------------------------------------------------------- /testdata/pk11.missingkeyfile.config: -------------------------------------------------------------------------------- 1 | key missing-file 2 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p m4 3 | autoreconf -i 4 | -------------------------------------------------------------------------------- /testdata/broken.key: -------------------------------------------------------------------------------- 1 | # comment here 2 | typo 010001 3 | mod 010203 4 | blob 010203040506 5 | -------------------------------------------------------------------------------- /testdata/correct.key: -------------------------------------------------------------------------------- 1 | # comment here 2 | exp 010001 3 | mod 010203 4 | blob 010203040506 5 | -------------------------------------------------------------------------------- /testscripts/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | "$(dirname "$0")/simple-key.exp" 6 | "$(dirname "$0")/pin-key.exp" 7 | 8 | echo "========================" 9 | echo "=== All tests passed ===" 10 | -------------------------------------------------------------------------------- /FAQ: -------------------------------------------------------------------------------- 1 | Q: Will this work on Macs? 2 | A: No. Macs do not have TPM chips. Please complain to your Apple dealer. 3 | --- 4 | Q: Should I generate keys on the TPM chip, or import software generated keys? 5 | A: Generate on the TPM chip. See: 6 | http://blog.habets.se/2013/11/Should-I-generate-my-keys-in-software-or-hardware 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | arch: 2 | - amd64 3 | - ppc64le 4 | language: cpp 5 | compiler: 6 | - clang 7 | - gcc 8 | script: ./bootstrap.sh && ./configure && make && make check 9 | before_install: 10 | - sudo apt-get update -qq 11 | - sudo apt-get install -qq tpm-tools libtspi-dev libopencryptoki-dev libssl-dev libgtest-dev 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.lo 4 | *.la 5 | *.in 6 | Makefile 7 | aclocal.m4 8 | autom4te.cache 9 | config.h 10 | config.log 11 | config.status 12 | configure 13 | libtool 14 | m4 15 | doc/Makefile.in 16 | .deps 17 | .libs 18 | stamp-h1 19 | *_test 20 | .dirstamp 21 | stpm-keygen 22 | stpm-sign 23 | stpm-exfiltrate 24 | stpm-verify 25 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS=foreign 2 | DISTCLEANFILES=*~ 3 | man_MANS=stpm-keygen.1 stpm-sign.1 stpm-verify.1 stpm-exfiltrate.1 simple-tpm-pk11.7 4 | 5 | EXTRA_DIST=$(man_MANS) 6 | 7 | # Generated manpage files are checked in, and are normally not built. 8 | # That's why there's nothing on the right hand side of this rule. 9 | # To re-generate, delete the target files and run 'make'. 10 | %.1 %.7: 11 | yodl2man -o $@.tmp $@.yodl 12 | perl -ne 's/(^|(?<=[^\\\w]))-/\\-/g;print' < $@.tmp > $@ 13 | rm -f $@.tmp 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /testscripts/simple-key.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | 3 | set timeout -1 4 | set tmpdir "test-tmp" 5 | 6 | spawn "mkdir" "-p" "$tmpdir" 7 | expect eof 8 | 9 | send_user "=== Generating key ===\n" 10 | spawn "./stpm-keygen" "-o" "$tmpdir/simple-key" 11 | expect "Modulus size: 256" 12 | expect "Exponent size: 3" 13 | expect "Size: 2048" 14 | expect "Blob size: 559" 15 | expect eof 16 | 17 | send_user "=== Signing with key ===\n" 18 | spawn "dd" "if=/dev/urandom" "of=$tmpdir/to-sign" "count=35" "bs=1" 19 | expect eof 20 | spawn "./stpm-sign" "-k" "$tmpdir/simple-key" "-f" "$tmpdir/to-sign" 21 | expect "Loaded key:" 22 | expect -- "--- Signature ---" 23 | expect eof 24 | -------------------------------------------------------------------------------- /src/libgtest.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #ifndef PRECOMPILED_GTEST 20 | #include"/usr/src/gtest/src/gtest_main.cc" 21 | #include"/usr/src/gtest/src/gtest-all.cc" 22 | #endif 23 | 24 | std::string argv0base = "test-binary"; 25 | -------------------------------------------------------------------------------- /testscripts/pin-key.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | # 3 | # Warning: this script will try the wrong PIN and could trigger dictionary 4 | # attack mode in the TPM. 5 | # 6 | set timeout -1 7 | set tmpdir "test-tmp" 8 | 9 | spawn "mkdir" "-p" "$tmpdir" 10 | expect eof 11 | 12 | send_user "=== Generating key with PIN ===\n" 13 | spawn "./stpm-keygen" "-po" "$tmpdir/pin-key" 14 | expect "Enter key PIN: " 15 | send "12345678\r" 16 | expect "Modulus size: 256" 17 | expect "Exponent size: 3" 18 | expect "Size: 2048" 19 | expect "Blob size: 559" 20 | expect eof 21 | 22 | send_user "=== Signing with key with PIN ===\n" 23 | spawn "dd" "if=/dev/urandom" "of=$tmpdir/to-sign" "count=35" "bs=1" 24 | expect eof 25 | spawn "./stpm-sign" "-k" "$tmpdir/pin-key" "-f" "$tmpdir/to-sign" 26 | expect "Enter key PIN: " 27 | send "12345678\r" 28 | expect "Loaded key:" 29 | expect -- "--- Signature ---" 30 | expect eof 31 | 32 | send_user "=== Signing with key with wrong PIN ===\n" 33 | spawn "dd" "if=/dev/urandom" "of=$tmpdir/to-sign" "count=35" "bs=1" 34 | expect eof 35 | spawn "./stpm-sign" "-k" "$tmpdir/pin-key" "-f" "$tmpdir/to-sign" 36 | expect "Enter key PIN: " 37 | send "87654321\r" 38 | expect "Authentication failed" 39 | expect eof 40 | 41 | -------------------------------------------------------------------------------- /src/common_test.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include"gtest/gtest.h" 17 | 18 | #include"common.h" 19 | 20 | TEST(Common, ToHex) 21 | { 22 | std::vector> tests = { 23 | {"",""}, 24 | {"414243", "ABC"}, 25 | {"616263", "abc"}, 26 | {"303132", "012"}, 27 | {"20217b7dff01", " !{}\xff\x01"}, 28 | {"00414243006162630100", std::string("\0ABC\0abc\x1\0", 10)}, 29 | }; 30 | for (auto& test : tests) { 31 | EXPECT_EQ(test.first, stpm::to_hex(test.second)); 32 | EXPECT_EQ(test.second, stpm::to_bin(stpm::to_hex(test.second))); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/internal.h: -------------------------------------------------------------------------------- 1 | /** -*- c++ -*- 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // Internal include to not pollute non-namespace parts. 17 | #ifndef __INCLUDE__SIMPLE_TPM_PK11_INTERNAL_H__ 18 | #define __INCLUDE__SIMPLE_TPM_PK11_INTERNAL_H__ 19 | #include 20 | 21 | #include"tss/tspi.h" 22 | 23 | #define BEGIN_NAMESPACE(x) namespace x { 24 | #define END_NAMESPACE(x) } 25 | 26 | // Jumpgate to tscall() 27 | #define TSCALL(x, ...) tscall(#x, [&]()->TSS_RESULT{return x(__VA_ARGS__);}) 28 | 29 | BEGIN_NAMESPACE(stpm); 30 | extern const TSS_UUID srk_uuid; 31 | 32 | TSS_RESULT tscall(const std::string& name, std::function func); 33 | 34 | END_NAMESPACE(stpm); 35 | #endif 36 | /* ---- Emacs Variables ---- 37 | * Local Variables: 38 | * c-basic-offset: 8 39 | * indent-tabs-mode: nil 40 | * End: 41 | */ 42 | -------------------------------------------------------------------------------- /src/verify_test.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * TODO: Add more tests. 17 | */ 18 | #include"config.h" 19 | #include 20 | #include"gtest/gtest.h" 21 | 22 | #include"test_util.h" 23 | 24 | static void 25 | reset_getopt() 26 | { 27 | #if HAVE_DECL_OPTRESET 28 | optreset = 1; 29 | #endif 30 | optind = 1; 31 | } 32 | 33 | extern int wrapped_main(int, char**); 34 | 35 | TEST(Usage, NoOpts) 36 | { 37 | CaptureStreams s; 38 | reset_getopt(); 39 | char *argv[] = { 40 | (char*)"sign", 41 | NULL, 42 | }; 43 | EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 44 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 45 | EXPECT_EQ("test-binary: Need to specify keyfile, data file, and signature file.\n", s.stderr()); 46 | EXPECT_EQ("", s.stdlog()); 47 | } 48 | /* ---- Emacs Variables ---- 49 | * Local Variables: 50 | * c-basic-offset: 2 51 | * indent-tabs-mode: nil 52 | * End: 53 | */ 54 | -------------------------------------------------------------------------------- /doc/stpm-verify.1.yodl: -------------------------------------------------------------------------------- 1 | manpage(stpm-verify)(1)(1th December, 2013)(simple-tpm-pk11)() 2 | manpagename(stpm-verify)(Verify data using the TPM chip) 3 | 4 | manpagesynopsis() 5 | bf(stpm-verify) [ -hq ] -f em(data) -s em(sig file) -k 6 | 7 | manpagedescription() 8 | em(stpm-verify) verifies data signed by em(stpm-sign). 9 | 10 | This program is mostly made for debugging, to make sure that the TPM 11 | is set up correctly and a valid key was generated. 12 | 13 | manpageoptions() 14 | startdit() 15 | dit(-h) Show usage info. 16 | dit(-f em(data file)) File containing data to be verified. 17 | dit(-s em(sig file)) File containing signature from em(stpm-sign). 18 | dit(-k em(key file)) File containing the encrypted key blob. 19 | enddit() 20 | 21 | manpagesection(EXAMPLES) 22 | mancommand(.nf) 23 | mancommand(.sp) 24 | dd if=/dev/urandom of=to-sign bs=1 count=35 25 | stpm-sign -k ~/.simple-tpm-pk11/my.key -f to-sign -r > to-sign.sig 26 | stpm-verify -f to-sign -k ~/.simple-tpm-pk11/my.key -s to-sign.sig 27 | 28 | mancommand(.fi) 29 | mancommand(.in) 30 | 31 | manpagediagnostics() 32 | Most errors will probably be related to interacting with the TPM chip. 33 | Resetting the TPM chip and taking ownership should take care of most 34 | of them. See the em(TPM-TROUBLESHOOTING) section of 35 | bf(simple-tpm-pk11(7)). 36 | 37 | manpageseealso() 38 | bf(simple-tpm-pk11(7)), bf(stpm-keygen(1)), bf(stpm-sign(1)). 39 | 40 | manpageauthor() 41 | Simple-TPM-PK11 was written By Thomas Habets 42 | / . 43 | 44 | git clone https://github.com/ThomasHabets/simple-tpm-pk11.git 45 | -------------------------------------------------------------------------------- /doc/stpm-verify.1: -------------------------------------------------------------------------------- 1 | .TH "stpm\-verify" "1" "1th December, 2013" "simple\-tpm\-pk11" "" 2 | .SH "NAME" 3 | stpm\-verify \- Verify data using the TPM chip 4 | .PP 5 | .SH "SYNOPSIS" 6 | \fBstpm\-verify\fP [ \-hq ] \-f \fIdata\fP \-s \fIsig file\fP \-k 7 | .PP 8 | .SH "DESCRIPTION" 9 | \fIstpm\-verify\fP verifies data signed by \fIstpm\-sign\fP\&. 10 | .PP 11 | This program is mostly made for debugging, to make sure that the TPM 12 | is set up correctly and a valid key was generated\&. 13 | .PP 14 | .SH "OPTIONS" 15 | .IP "\-h" 16 | Show usage info\&. 17 | .IP "\-f \fIdata file\fP" 18 | File containing data to be verified\&. 19 | .IP "\-s \fIsig file\fP" 20 | File containing signature from \fIstpm\-sign\fP\&. 21 | .IP "\-k \fIkey file\fP" 22 | File containing the encrypted key blob\&. 23 | 24 | .PP 25 | .SH "EXAMPLES" 26 | .nf 27 | .sp 28 | dd if=/dev/urandom of=to\-sign bs=1 count=35 29 | stpm\-sign \-k ~/\&.simple\-tpm\-pk11/my\&.key \-f to\-sign \-r > to\-sign\&.sig 30 | stpm\-verify \-f to\-sign \-k ~/\&.simple\-tpm\-pk11/my\&.key \-s to\-sign\&.sig 31 | .PP 32 | .fi 33 | .in 34 | .PP 35 | .SH "DIAGNOSTICS" 36 | Most errors will probably be related to interacting with the TPM chip\&. 37 | Resetting the TPM chip and taking ownership should take care of most 38 | of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of 39 | \fBsimple\-tpm\-pk11(7)\fP\&. 40 | .PP 41 | .SH "SEE ALSO" 42 | \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-keygen(1)\fP, \fBstpm\-sign(1)\fP\&. 43 | .PP 44 | .SH "AUTHOR" 45 | Simple\-TPM\-PK11 was written By Thomas Habets 46 | / \&. 47 | .PP 48 | git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git 49 | -------------------------------------------------------------------------------- /src/test_util.h: -------------------------------------------------------------------------------- 1 | /** -*- c++ -*- 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef __INCLUDE__SIMPLE_TPM_PK11_TEST_UTIL_H__ 17 | #define __INCLUDE__SIMPLE_TPM_PK11_TEST_UTIL_H__ 18 | #include 19 | #include 20 | 21 | struct CaptureStreams { 22 | CaptureStreams() 23 | :stopped_(false), 24 | outold_(std::cout.rdbuf(outbuf_.rdbuf())), 25 | errold_(std::cerr.rdbuf(errbuf_.rdbuf())), 26 | logold_(std::clog.rdbuf(logbuf_.rdbuf())) 27 | { 28 | } 29 | 30 | void stop() 31 | { 32 | if (!stopped_) { 33 | std::cout.rdbuf(outold_); 34 | std::cerr.rdbuf(errold_); 35 | std::clog.rdbuf(logold_); 36 | stopped_ = true; 37 | } 38 | } 39 | std::string stdout() const { return outbuf_.str(); } 40 | std::string stderr() const { return errbuf_.str(); } 41 | std::string stdlog() const { return logbuf_.str(); } 42 | 43 | ~CaptureStreams() 44 | { 45 | stop(); 46 | } 47 | 48 | private: 49 | bool stopped_; 50 | std::stringstream outbuf_, errbuf_, logbuf_; 51 | std::streambuf *outold_, *errold_, *logold_; 52 | }; 53 | #endif 54 | /* ---- Emacs Variables ---- 55 | * Local Variables: 56 | * c-basic-offset: 2 57 | * indent-tabs-mode: nil 58 | * End: 59 | */ 60 | -------------------------------------------------------------------------------- /TPM-TROUBLESHOOTING: -------------------------------------------------------------------------------- 1 | Problem: 2 | tpm_clear --force 3 | [...] TPM is disabled 4 | Solution: 5 | Go into BIOS and enable the TPM chip. 6 | --- 7 | Problem: 8 | tpm_clear --force 9 | TPM Successfully Cleared. You need to reboot to complete this operation. 10 | After reboot the TPM will be in the default state: unowned, 11 | disabled and inactive. 12 | Solution: 13 | Reboot. 14 | --- 15 | Problem 16 | tpm_clear --force 17 | [...] Bad physical presence value 18 | Solution 19 | Reset using a cold boot and the BIOS. See below. 20 | --- 21 | Problem 22 | stpm-keygen -o my.key 23 | [...] TPM is defending against dictionary attacks and is in some time-out period 24 | Solution 25 | tpm_resetdalock 26 | --- 27 | Problem 28 | One of the solutions assumes I know the owner password, and I don't. 29 | Solution 30 | 1) Shut off the machine. Reboot will not do. Power it down. 31 | 2) Boot the machine and enter the BIOS. 32 | 3) In the BIOS, find "Clear TPM chip" and run that. 33 | 4) Boot the OS and start from scratch with tpm_takeownership. 34 | --- 35 | Problem 36 | Key not found in persistent storage. 37 | Solution 38 | Did you reboot after clearing/taking ownership? Try that first. 39 | --- 40 | Problem 41 | stpm-keygen -o my.key 42 | Tspi_Context_LoadKeyByUUID: Code=0x00002020: tcs: Key not found in persistent storage 43 | Solution 44 | Is the TPM chip active? Check with tpm_getpubek. If it says it's 45 | inactive you may need to turn it on in the BIOS. If that doesn't 46 | work, try clearing it and starting from scratch. 47 | --- 48 | Problem 49 | After re-installation of the operating system the key is not found. 50 | $ ssh server 51 | C_GetTokenInfo failed: 6 no keys 52 | Solution 53 | The base library trousers saves some part of the key on the local filesystem. 54 | https://sourceforge.net/p/trousers/mailman/message/28828649/ 55 | Copy the file /var/lib/tpm/system.data as root to your new system. 56 | -------------------------------------------------------------------------------- /src/wrap_main.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include"common.h" 22 | 23 | std::string argv0base = "unknown-binary"; 24 | 25 | extern int wrapped_main(int argc, char **argv); 26 | 27 | int 28 | main(int argc, char **argv) 29 | { 30 | argv0base = stpm::xbasename(argv[0]); 31 | try { 32 | // Support --help without relying on getopt_long. 33 | for (int c = 1; c < argc; c++) { 34 | if (!strcmp(argv[c], "--help")) { 35 | char *a[] = { 36 | argv[0], 37 | (char*)"-h", 38 | NULL, 39 | }; 40 | return wrapped_main(2, a); 41 | } 42 | } 43 | 44 | return wrapped_main(argc, argv); 45 | } catch (const stpm::TSPIException& e) { 46 | std::cerr << argv0base << ": Exception:\n " << e.what() << std::endl; 47 | if (!e.extra().empty()) { 48 | std::cerr << e.extra() << std::endl; 49 | } 50 | } catch (const std::exception& e) { 51 | std::cerr << argv0base << ": Exception: " << e.what() << std::endl; 52 | } catch (...) { 53 | // Shouldn't happen. 54 | std::cerr << argv0base << ": Exception of unknown type!\n"; 55 | } 56 | return 1; 57 | } 58 | /* ---- Emacs Variables ---- 59 | * Local Variables: 60 | * c-basic-offset: 2 61 | * indent-tabs-mode: nil 62 | * End: 63 | */ 64 | -------------------------------------------------------------------------------- /doc/stpm-sign.1: -------------------------------------------------------------------------------- 1 | .TH "stpm\-sign" "1" "1th December, 2013" "simple\-tpm\-pk11" "" 2 | .SH "NAME" 3 | stpm\-sign \- Sign data using the TPM chip 4 | .PP 5 | .SH "SYNOPSIS" 6 | \fBstpm\-sign\fP [ \-hs ] \-k \fIkey file\fP \-f \fIinput file\fP 7 | .PP 8 | .SH "DESCRIPTION" 9 | \fIstpm\-sign\fP takes the SRK\-encrypted key blob and has the TPM sign the 10 | contents of \fIinput file\fP using the key\&. 11 | .PP 12 | This program is mostly made for debugging, to make sure that the TPM 13 | is set up correctly and a valid key was generated\&. 14 | .PP 15 | .SH "OPTIONS" 16 | .IP "\-h" 17 | Show usage info\&. 18 | .IP "\-f \fIinput file\fP" 19 | File containing data to be signed\&. 20 | .IP "\-k" 21 | Key to sign with\&. The key is generated with \fIstpm\-keygen\fP\&. 22 | .IP "\-s" 23 | Ask for the SRK password interactively\&. By default the 24 | \(dq\&Well Known Secret\(dq\& (20 nulls) is used\&. The SRK password is an 25 | access token that must be presented for the TPM to perform any 26 | operation that involves the TPM, and an actual secret password 27 | is usually not required or useful\&. 28 | 29 | .PP 30 | .SH "EXAMPLES" 31 | .nf 32 | .sp 33 | .PP 34 | stpm\-sign \-k ~/\&.simple\-tpm\-pk11/my\&.key \-f my\-data\-here 35 | .PP 36 | stpm\-sign \-k ~/\&.simple\-tpm\-pk11/my\-PIN\-key\&.key \-f my\-data\-here 37 | Enter key PIN: my secret password here 38 | .PP 39 | stpm\-sign \-sk ~/\&.simple\-tpm\-pk11/my\-PIN\-key\&.key \-f my\-data\-here 40 | Enter SRK PIN: 12345678 41 | Enter key PIN: my secret password here 42 | .fi 43 | .in 44 | .PP 45 | .SH "DIAGNOSTICS" 46 | Most errors will probably be related to interacting with the TPM chip\&. 47 | Resetting the TPM chip and taking ownership should take care of most 48 | of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of 49 | \fBsimple\-tpm\-pk11(7)\fP\&. 50 | .PP 51 | .SH "SEE ALSO" 52 | \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-keygen(1)\fP, \fBstpm\-verify(1)\fP\&. 53 | .PP 54 | .SH "AUTHOR" 55 | Simple\-TPM\-PK11 was written By Thomas Habets 56 | / \&. 57 | .PP 58 | git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git 59 | -------------------------------------------------------------------------------- /doc/stpm-sign.1.yodl: -------------------------------------------------------------------------------- 1 | manpage(stpm-sign)(1)(1th December, 2013)(simple-tpm-pk11)() 2 | manpagename(stpm-sign)(Sign data using the TPM chip) 3 | 4 | manpagesynopsis() 5 | bf(stpm-sign) [ -hs ] -k em(key file) -f em(input file) 6 | 7 | manpagedescription() 8 | em(stpm-sign) takes the SRK-encrypted key blob and has the TPM sign the 9 | contents of em(input file) using the key. 10 | 11 | This program is mostly made for debugging, to make sure that the TPM 12 | is set up correctly and a valid key was generated. 13 | 14 | manpageoptions() 15 | startdit() 16 | dit(-h) Show usage info. 17 | dit(-f em(input file)) File containing data to be signed. 18 | dit(-k) Key to sign with. The key is generated with em(stpm-keysign). 19 | dit(-s) Ask for the SRK password interactively. By default the 20 | "Well Known Secret" (20 nulls) is used. The SRK password is an 21 | access token that must be presented for the TPM to perform any 22 | operation that involves the TPM, and an actual secret password 23 | is usually not required or useful. 24 | enddit() 25 | 26 | manpagesection(EXAMPLES) 27 | mancommand(.nf) 28 | mancommand(.sp) 29 | 30 | stpm-sign -k ~/.simple-tpm-pk11/my.key -f my-data-here 31 | 32 | stpm-sign -k ~/.simple-tpm-pk11/my-PIN-key.key -f my-data-here 33 | Enter key PIN: my secret password here 34 | 35 | stpm-sign -sk ~/.simple-tpm-pk11/my-PIN-key.key -f my-data-here 36 | Enter SRK PIN: 12345678 37 | Enter key PIN: my secret password here 38 | mancommand(.fi) 39 | mancommand(.in) 40 | 41 | manpagediagnostics() 42 | Most errors will probably be related to interacting with the TPM chip. 43 | Resetting the TPM chip and taking ownership should take care of most 44 | of them. See the em(TPM-TROUBLESHOOTING) section of 45 | bf(simple-tpm-pk11(7)). 46 | 47 | manpageseealso() 48 | bf(simple-tpm-pk11(7)), bf(stpm-keygen(1)), bf(stpm-verify(1)). 49 | 50 | manpageauthor() 51 | Simple-TPM-PK11 was written By Thomas Habets 52 | / . 53 | 54 | git clone https://github.com/ThomasHabets/simple-tpm-pk11.git 55 | -------------------------------------------------------------------------------- /doc/stpm-exfiltrate.1.yodl: -------------------------------------------------------------------------------- 1 | manpage(stpm-exfiltrate)(1)(16th Febrary, 2014)(simple-tpm-pk11)() 2 | manpagename(stpm-exfiltrate)(Extract key from TPM chip) 3 | 4 | manpagesynopsis() 5 | bf(stpm-exfiltrate) [ -hOps ] -k em(key file) 6 | 7 | manpagedescription() 8 | em(stpm-exfiltrate) extracts a key that is otherwise protected by 9 | the TPM chip. This only works if the key is "migratable" (meaning it was 10 | generated in software), and the TPM owner password is known. 11 | 12 | This is why you should generate keys in hardware (the default) with 13 | stpm-keygen and not use its -S option. 14 | 15 | manpageoptions() 16 | startdit() 17 | dit(-h) Show usage info. 18 | dit(-k em(key file)) Key blob file to read. 19 | dit(-O) Use Well Known Secret for owner password. Default is ask. 20 | dit(-p) Ask for key PIN / password. Default is Well Known Secret. 21 | dit(-o) Ask for SRK PIN / password. Default is Well Known Secret. 22 | enddit() 23 | 24 | manpagesection(EXAMPLES) 25 | mancommand(.nf) 26 | mancommand(.sp) 27 | 28 | stpm-exfiltrate -k ~/.simple-tpm-pk11/my.key 29 | Enter owner password: blah blah 30 | [ ... key data here ...] 31 | 32 | stpm-exfiltrate -p -k ~/.simple-tpm-pk11/my.key 33 | Enter owner password: blah blah 34 | Enter key PIN: my secret password here 35 | [ ... key data here ...] 36 | 37 | stpm-exfiltrate -sp -k ~/.simple-tpm-pk11/my.key 38 | Enter owner password: blah blah 39 | Enter key PIN: my secret password here 40 | Enter SRK PIN: 12345678 41 | [ ... key data here ...] 42 | mancommand(.fi) 43 | mancommand(.in) 44 | 45 | manpagediagnostics() 46 | Most errors will probably be related to interacting with the TPM chip. 47 | Resetting the TPM chip and taking ownership should take care of most 48 | of them. See the em(TPM-TROUBLESHOOTING) section of 49 | bf(simple-tpm-pk11(7)). 50 | 51 | manpageseealso() 52 | bf(simple-tpm-pk11(7)), bf(stpm-sign(1)), bf(stpm-keygen). 53 | 54 | manpageauthor() 55 | Simple-TPM-PK11 was written By Thomas Habets 56 | / . 57 | 58 | git clone https://github.com/ThomasHabets/simple-tpm-pk11.git 59 | -------------------------------------------------------------------------------- /doc/stpm-exfiltrate.1: -------------------------------------------------------------------------------- 1 | .TH "stpm\-exfiltrate" "1" "16th Febrary, 2014" "simple\-tpm\-pk11" "" 2 | .SH "NAME" 3 | stpm\-exfiltrate \- Extract key from TPM chip 4 | .PP 5 | .SH "SYNOPSIS" 6 | \fBstpm\-exfiltrate\fP [ \-hOps ] \-k \fIkey file\fP 7 | .PP 8 | .SH "DESCRIPTION" 9 | \fIstpm\-exfiltrate\fP extracts a key that is otherwise protected by 10 | the TPM chip\&. This only works if the key is \(dq\&migratable\(dq\& (meaning it was 11 | generated in software), and the TPM owner password is known\&. 12 | .PP 13 | This is why you should generate keys in hardware (the default) with 14 | stpm\-keygen and not use its \-S option\&. 15 | .PP 16 | .SH "OPTIONS" 17 | .IP "\-h" 18 | Show usage info\&. 19 | .IP "\-k \fIkey file\fP" 20 | Key blob file to read\&. 21 | .IP "\-O" 22 | Use Well Known Secret for owner password\&. Default is ask\&. 23 | .IP "\-p" 24 | Ask for key PIN / password\&. Default is Well Known Secret\&. 25 | .IP "\-o" 26 | Ask for SRK PIN / password\&. Default is Well Known Secret\&. 27 | 28 | .PP 29 | .SH "EXAMPLES" 30 | .nf 31 | .sp 32 | .PP 33 | stpm\-exfiltrate \-k ~/\&.simple\-tpm\-pk11/my\&.key 34 | Enter owner password: blah blah 35 | [ \&.\&.\&. key data here \&.\&.\&.] 36 | .PP 37 | stpm\-exfiltrate \-p \-k ~/\&.simple\-tpm\-pk11/my\&.key 38 | Enter owner password: blah blah 39 | Enter key PIN: my secret password here 40 | [ \&.\&.\&. key data here \&.\&.\&.] 41 | .PP 42 | stpm\-exfiltrate \-sp \-k ~/\&.simple\-tpm\-pk11/my\&.key 43 | Enter owner password: blah blah 44 | Enter key PIN: my secret password here 45 | Enter SRK PIN: 12345678 46 | [ \&.\&.\&. key data here \&.\&.\&.] 47 | .fi 48 | .in 49 | .PP 50 | .SH "DIAGNOSTICS" 51 | Most errors will probably be related to interacting with the TPM chip\&. 52 | Resetting the TPM chip and taking ownership should take care of most 53 | of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of 54 | \fBsimple\-tpm\-pk11(7)\fP\&. 55 | .PP 56 | .SH "SEE ALSO" 57 | \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-sign(1)\fP, \fBstpm\-keygen\fP\&. 58 | .PP 59 | .SH "AUTHOR" 60 | Simple\-TPM\-PK11 was written By Thomas Habets 61 | / \&. 62 | .PP 63 | git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git 64 | -------------------------------------------------------------------------------- /src/tspiwrap.h: -------------------------------------------------------------------------------- 1 | /** -*- c++ -*- 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // Wrapping classes to make cleanup easier. 17 | #ifndef __INCLUDE__SIMPLE_TPM_PK11_TSPIWRAP_H__ 18 | #define __INCLUDE__SIMPLE_TPM_PK11_TSPIWRAP_H__ 19 | 20 | #include 21 | 22 | #include"tss/tspi.h" 23 | 24 | namespace stpm { 25 | #if 0 26 | } 27 | #endif 28 | 29 | class TspiContext { 30 | public: 31 | TspiContext(const TspiContext&) = delete; 32 | TspiContext& operator=(const TspiContext&) = delete; 33 | TspiContext(); 34 | ~TspiContext(); 35 | 36 | TSS_HCONTEXT ctx() const { return ctx_; } 37 | private: 38 | TSS_HCONTEXT ctx_; 39 | }; 40 | 41 | class TspiTPM { 42 | public: 43 | TspiTPM(const TspiTPM&) = delete; 44 | TspiTPM& operator=(const TspiTPM&) = delete; 45 | TspiTPM(TspiContext&ctx); 46 | ~TspiTPM(); 47 | 48 | TSS_HTPM tpm() { return tpm_; } 49 | private: 50 | TSS_HTPM tpm_; 51 | }; 52 | 53 | class TspiKey { 54 | public: 55 | TspiKey(const TspiKey&) = delete; 56 | TspiKey& operator=(const TspiKey&) = delete; 57 | TspiKey(TspiContext&, TSS_UUID uuid, const std::string* pin); 58 | ~TspiKey(); 59 | 60 | TSS_HKEY key() const { return key_; } 61 | private: 62 | TspiContext& ctx_; 63 | TSS_HKEY key_; 64 | TSS_HPOLICY policy_; 65 | 66 | void destroy(); 67 | }; 68 | 69 | class TPMStuff { 70 | public: 71 | TPMStuff(const TPMStuff&) = delete; 72 | TPMStuff& operator=(const TPMStuff&) = delete; 73 | TPMStuff(const std::string* srk_pin); 74 | 75 | TSS_HCONTEXT ctx() { return ctx_.ctx(); } 76 | TSS_HTPM tpm() { return tpm_.tpm(); } 77 | TSS_HKEY srk() { return srk_.key(); } 78 | private: 79 | // Order matters. Do not change. 80 | TspiContext ctx_; 81 | TspiTPM tpm_; 82 | TspiKey srk_; 83 | }; 84 | } // namespace stpm 85 | #endif 86 | /* ---- Emacs Variables ---- 87 | * Local Variables: 88 | * c-basic-offset: 2 89 | * indent-tabs-mode: nil 90 | * End: 91 | */ 92 | -------------------------------------------------------------------------------- /doc/simple-tpm-pk11.7: -------------------------------------------------------------------------------- 1 | .TH "simple\-tpm\-pk11" "7" "1th December, 2013" "simple\-tpm\-pk11" "" 2 | .SH "NAME" 3 | simple\-tpm\-pk11 \- Simple PKCS11 provider for TPM chips 4 | .PP 5 | .SH "DESCRIPTION" 6 | \fIsimple\-tpm\-pk11\fP Is a PKCS11 provider for TPM chips\&. Its primary 7 | purpose is to protect SSH client keys so that they can\(cq\&t be copied or 8 | stolen if the machine they\(cq\&re on gets compromised\&. 9 | .PP 10 | .SH "OPTIONS" 11 | Since PKCS11 modules are \&.so files loaded by other binaries, they don\(cq\&t 12 | take command line options\&. Instead \fIsimple\-tpm\-pk11\fP options can be 13 | set up environment variables\&. 14 | .IP "\fBSIMPLE_TPM_PK11_DEBUG\fP" 15 | If set, enables debug level logging\&. 16 | .IP "\fBSIMPLE_TPM_PK11_CONFIG\fP=/path/to/config" 17 | Override default config location\&. Default is ~/\&.simple\-tpm\-pk11/config\&. 18 | .IP "\fBSIMPLE_TPM_PK11_LOG_STDERR\fP" 19 | If set, copies all log output to STDERR\&. 20 | 21 | .PP 22 | .SH "CONFIGURATION FILE" 23 | Configuration options are of the key/value variety, with comments lines 24 | starting with \(dq\&#\(dq\&\&. 25 | .IP "key \fIkey file\fP" 26 | Full path to key file, or relative to ~/\&.simple\-tpm\-pk11\&. 27 | This the only required configuration option\&. 28 | .IP "debug" 29 | Enable debug level logging\&. 30 | .IP "srk_pin \fIPIN\fP" 31 | Set SRK PIN\&. Default is the Well Known Secret (20 nulls)\&. 32 | .IP "key_pin \fIPIN\fP" 33 | Set key PIN\&. 34 | .IP "log \fIlog file\fP" 35 | Full path to log file, or relative to ~/\&.simple\-tpm\-pk11\&. 36 | 37 | .PP 38 | .SH "EXAMPLES" 39 | .nf 40 | .sp 41 | # Load key from ~/\&.simple\-tpm\-pk11/my\&.key\&. 42 | key my\&.key 43 | .PP 44 | # Load key from /keys/foo/my\&.key, and the empty string as SRK PIN\&. 45 | key /keys/foo/my\&.key 46 | srk_pin 47 | .fi 48 | .in 49 | .PP 50 | .SH "TPM\-TROUBLESHOOTING" 51 | TODO\&. 52 | .PP 53 | .SH "DIAGNOSTICS" 54 | Most errors will probably be related to interacting with the TPM chip\&. 55 | Resetting the TPM chip and taking ownership should take care of most 56 | of them\&. See the \fITPM\-TROUBLESHOOTING\fP section\&. 57 | .PP 58 | .SH "BUGS" 59 | The password is read from stdin without turning off echo\&. It should be 60 | read from the terminal without echo\&. 61 | .PP 62 | .SH "SEE ALSO" 63 | \fBstpm\-keygen(1)\fP, \fBstpm\-sign(1)\fP 64 | .PP 65 | .SH "AUTHOR" 66 | Simple\-TPM\-PK11 was written By Thomas Habets 67 | / \&. 68 | .PP 69 | git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git 70 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.61) 2 | AC_INIT([simple-tpm-pk11], [0.07], [thomas@habets.se]) 3 | AC_CONFIG_AUX_DIR([m4]) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 6 | AM_MAINTAINER_MODE 7 | LT_INIT([dlopen]) 8 | 9 | AC_CONFIG_SRCDIR([src/common.cc]) 10 | AC_CONFIG_HEADER(config.h) 11 | 12 | AC_CHECK_HEADERS([tss/tspi.h], [], [ 13 | [echo "Cannot continue:"] 14 | [echo " libtspi headers are missing; please install the package providing tss/tspi.h,"] 15 | [echo " which is libtspi-dev for Debian derivatives."] 16 | [exit 1] 17 | ]) 18 | AC_CHECK_LIB([tspi], [Tspi_GetAttribUint32], [], [ 19 | [echo "Cannot continue:"] 20 | [echo " libtspi is missing the required function Tspi_GetAttribUint32."] 21 | [exit 1] 22 | ]) 23 | AC_CHECK_DECLS([optarg, optind, optreset], [:], [:], [ 24 | #include 25 | #include 26 | #include 27 | ]) 28 | AC_CHECK_LIB([crypto], [BN_new], [], [ 29 | [echo "Cannot continue:"] 30 | [echo " libcrypto is missing the required function BN_new."] 31 | [exit 1] 32 | ]) 33 | AC_CHECK_HEADERS([opencryptoki/pkcs11.h], [], [ 34 | [echo "Cannot continue:"] 35 | [echo " opencryptoki headers are missing; please install the package providing"] 36 | [echo " opencryptoki/pkcs11.h, which is libopencryptoki-dev for Debian derivatives."] 37 | [exit 1] 38 | ]) 39 | 40 | AC_ARG_WITH(precompiled-gtest, 41 | [ --with-precompiled-gtest Use a system-provided precompiled version of gtest], 42 | [case "${withval}" in 43 | yes | no ) WITH_PRECOMPILED_GTEST="${withval}" ;; 44 | *) AC_MSG_ERROR(bad value ${withval} for --with-precompiled-gtest) ;; 45 | esac], 46 | [WITH_PRECOMPILED_GTEST="no"] 47 | ) 48 | AM_CONDITIONAL([WITH_PRECOMPILED_GTEST], [test "x$WITH_PRECOMPILED_GTEST" = "xyes"]) 49 | AS_IF([test "x$WITH_PRECOMPILED_GTEST" = "xyes"], [ 50 | AC_DEFINE([PRECOMPILED_GTEST], [], ["build using precompiled gtest library"]) 51 | ]) 52 | 53 | AC_PROG_CXX 54 | AC_PROG_INSTALL 55 | AC_SUBST([AM_CXXFLAGS]) 56 | AC_SUBST([AM_LDFLAGS]) 57 | 58 | # Library stuff. 59 | AC_ENABLE_SHARED 60 | AC_DISABLE_STATIC 61 | LT_INIT(libtool) 62 | 63 | AC_CHECK_FUNCS([RSA_get0_key RSA_set0_key RSA_get0_factors]) 64 | 65 | CXXFLAGS="-Wall $CXXFLAGS" 66 | 67 | 68 | AC_CONFIG_FILES([Makefile]) 69 | AC_CONFIG_FILES([doc/Makefile]) 70 | AC_OUTPUT 71 | 72 | echo " 73 | $PACKAGE_NAME version $PACKAGE_VERSION 74 | Prefix.........: $prefix 75 | Debug Build....: $debug 76 | C Compiler.....: $CC $CFLAGS $CPPFLAGS 77 | C++ Compiler...: $CXX $CXXFLAGS $CPPFLAGS 78 | Linker.........: $LD $LDFLAGS $LIBS 79 | " 80 | -------------------------------------------------------------------------------- /doc/simple-tpm-pk11.7.yodl: -------------------------------------------------------------------------------- 1 | manpage(simple-tpm-pk11)(7)(1th December, 2013)(simple-tpm-pk11)() 2 | manpagename(simple-tpm-pk11)(Simple PKCS11 provider for TPM chips) 3 | 4 | manpagedescription() 5 | em(simple-tpm-pk11) Is a PKCS11 provider for TPM chips. Its primary 6 | purpose is to protect SSH client keys so that they can't be copied or 7 | stolen if the machine they're on gets compromised. 8 | 9 | manpageoptions() 10 | Since PKCS11 modules are .so files loaded by other binaries, they don't 11 | take command line options. Instead em(simple-tpm-pk11) options can be 12 | set up environment variables. 13 | startdit() 14 | dit(bf(SIMPLE_TPM_PK11_DEBUG)) 15 | If set, enables debug level logging. 16 | dit(bf(SIMPLE_TPM_PK11_CONFIG)=/path/to/config) 17 | Override default config location. Default is ~/.simple-tpm-pk11/config. 18 | dit(bf(SIMPLE_TPM_PK11_LOG_STDERR)) 19 | If set, copies all log output to STDERR. 20 | enddit() 21 | 22 | manpagesection(CONFIGURATION FILE) 23 | Configuration options are of the key/value variety, with comments lines 24 | starting with "#". 25 | startdit() 26 | dit(key em(key file)) 27 | Full path to key file, or relative to ~/.simple-tpm-pk11. 28 | This the only required configuration option. 29 | dit(debug) 30 | Enable debug level logging. 31 | dit(srk_pin em(PIN)) 32 | Set SRK PIN. Default is the Well Known Secret (20 nulls). 33 | dit(key_pin em(PIN)) 34 | Set key PIN. 35 | dit(log em(log file)) 36 | Full path to log file, or relative to ~/.simple-tpm-pk11. 37 | enddit() 38 | 39 | manpagesection(EXAMPLES) 40 | mancommand(.nf) 41 | mancommand(.sp) 42 | # Load key from ~/.simple-tpm-pk11/my.key. 43 | key my.key 44 | 45 | # Load key from /keys/foo/my.key, and the empty string as SRK PIN. 46 | key /keys/foo/my.key 47 | srk_pin 48 | mancommand(.fi) 49 | mancommand(.in) 50 | 51 | manpagesection(TPM-TROUBLESHOOTING) 52 | TODO. 53 | 54 | manpagediagnostics() 55 | Most errors will probably be related to interacting with the TPM chip. 56 | Resetting the TPM chip and taking ownership should take care of most 57 | of them. See the em(TPM-TROUBLESHOOTING) section. 58 | 59 | manpagebugs() 60 | The password is read from stdin without turning off echo. It should be 61 | read from the terminal without echo. 62 | 63 | manpageseealso() 64 | bf(stpm-keygen(1)), bf(stpm-sign(1)) 65 | 66 | manpageauthor() 67 | Simple-TPM-PK11 was written By Thomas Habets 68 | / . 69 | 70 | git clone https://github.com/ThomasHabets/simple-tpm-pk11.git 71 | -------------------------------------------------------------------------------- /doc/stpm-keygen.1: -------------------------------------------------------------------------------- 1 | .TH "stpm\-keygen" "1" "1th December, 2013" "simple\-tpm\-pk11" "" 2 | .SH "NAME" 3 | stpm\-keygen \- Generate key pair for use with simple\-tpm\-pk11 4 | .PP 5 | .SH "SYNOPSIS" 6 | \fBstpm\-keygen\fP [ \-hps ] \-o \fIoutput file\fP 7 | .PP 8 | .SH "DESCRIPTION" 9 | \fIstpm\-keygen\fP generates a 2048 RSA key inside the TPM chip, and saves 10 | the public key and the SRK\-encrypted private key (the \(dq\&blob\(dq\&) in the 11 | \fIoutput file\fP\&. 12 | .PP 13 | .SH "OPTIONS" 14 | .IP "\-h" 15 | Show usage info\&. 16 | .IP "\-o \fIoutput file\fP" 17 | Output file, where the public key and key blob 18 | will be written\&. 19 | .IP "\-p" 20 | Create the key with a PIN / password\&. The password will 21 | be prompted for inteactively\&. 22 | .IP "\-s" 23 | Ask for the SRK password interactively\&. By default the 24 | \(dq\&Well Known Secret\(dq\& (20 nulls) is used\&. The SRK password is an 25 | access token that must be presented for the TPM to perform any 26 | operation that involves the TPM, and an actual secret password 27 | is usually not required or useful\&. 28 | .IP "\-S" 29 | Generate key in software instead of hardware\&. 30 | The choice between generating the key in software and hardware is 31 | not an obvious one\&. It\(cq\&s hard to verify the quality of keys generated 32 | in hardware (e\&.g\&. bugs or backdoors), but software keys have existed 33 | in RAM at some point\&. And because software generated keys have to be generated as 34 | migratable keys, they can be extracted by someone who knows the TPM owner 35 | password\&. The recommended choice is to generate in hardware, which 36 | is also the default\&. 37 | 38 | .PP 39 | .SH "EXAMPLES" 40 | .nf 41 | .sp 42 | .PP 43 | stpm\-keygen \-o ~/\&.simple\-tpm\-pk11/my\&.key 44 | .PP 45 | stpm\-keygen \-p \-o ~/\&.simple\-tpm\-pk11/my\&.key 46 | Enter key PIN: my secret password here 47 | .PP 48 | stpm\-keygen \-sp \-o ~/\&.simple\-tpm\-pk11/my\&.key 49 | Enter SRK PIN: 12345678 50 | Enter key PIN: my secret password here 51 | .fi 52 | .in 53 | .PP 54 | .SH "DIAGNOSTICS" 55 | Most errors will probably be related to interacting with the TPM chip\&. 56 | Resetting the TPM chip and taking ownership should take care of most 57 | of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of 58 | \fBsimple\-tpm\-pk11(7)\fP\&. 59 | .PP 60 | .SH "SEE ALSO" 61 | \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-sign(1)\fP\&. 62 | .PP 63 | http://blog\&.habets\&.se/2013/11/Should\-I\-generate\-my\-keys\-in\-software\-or\-hardware 64 | .PP 65 | .SH "AUTHOR" 66 | Simple\-TPM\-PK11 was written By Thomas Habets 67 | / \&. 68 | .PP 69 | git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git 70 | -------------------------------------------------------------------------------- /doc/stpm-keygen.1.yodl: -------------------------------------------------------------------------------- 1 | manpage(stpm-keygen)(1)(1th December, 2013)(simple-tpm-pk11)() 2 | manpagename(stpm-keygen)(Generate key pair for use with simple-tpm-pk11) 3 | 4 | manpagesynopsis() 5 | bf(stpm-keygen) [ -hps ] -o em(output file) 6 | 7 | manpagedescription() 8 | em(stpm-keygen) generates a 2048 RSA key inside the TPM chip, and saves 9 | the public key and the SRK-encrypted private key (the "blob") in the 10 | em(output file). 11 | 12 | manpageoptions() 13 | startdit() 14 | dit(-h) Show usage info. 15 | dit(-o em(output file)) Output file, where the public key and key blob 16 | will be written. 17 | dit(-p) Create the key with a PIN / password. The password will 18 | be prompted for inteactively. 19 | dit(-s) Ask for the SRK password interactively. By default the 20 | "Well Known Secret" (20 nulls) is used. The SRK password is an 21 | access token that must be presented for the TPM to perform any 22 | operation that involves the TPM, and an actual secret password 23 | is usually not required or useful. 24 | dit(-S) Generate key in software instead of hardware. 25 | The choice between generating the key in software and hardware is 26 | not an obvious one. It's hard to verify the quality of keys generated 27 | in hardware (e.g. bugs or backdoors), but software keys have existed 28 | in RAM at some point. And because software generated keys have to be generated as 29 | migratable keys, they can be extracted by someone who knows the TPM owner 30 | password. The recommended choice is to generate in hardware, which 31 | is also the default. 32 | enddit() 33 | 34 | manpagesection(EXAMPLES) 35 | mancommand(.nf) 36 | mancommand(.sp) 37 | 38 | stpm-keygen -o ~/.simple-tpm-pk11/my.key 39 | 40 | stpm-keygen -p -o ~/.simple-tpm-pk11/my.key 41 | Enter key PIN: my secret password here 42 | 43 | stpm-keygen -sp -o ~/.simple-tpm-pk11/my.key 44 | Enter SRK PIN: 12345678 45 | Enter key PIN: my secret password here 46 | mancommand(.fi) 47 | mancommand(.in) 48 | 49 | manpagediagnostics() 50 | Most errors will probably be related to interacting with the TPM chip. 51 | Resetting the TPM chip and taking ownership should take care of most 52 | of them. See the em(TPM-TROUBLESHOOTING) section of 53 | bf(simple-tpm-pk11(7)). 54 | 55 | manpageseealso() 56 | bf(simple-tpm-pk11(7)), bf(stpm-sign(1)). 57 | 58 | http://blog.habets.se/2013/11/Should-I-generate-my-keys-in-software-or-hardware 59 | 60 | manpageauthor() 61 | Simple-TPM-PK11 was written By Thomas Habets 62 | / . 63 | 64 | git clone https://github.com/ThomasHabets/simple-tpm-pk11.git 65 | -------------------------------------------------------------------------------- /src/tspiwrap.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include"tspiwrap.h" 17 | 18 | #include"common.h" 19 | #include"internal.h" 20 | 21 | BEGIN_NAMESPACE(stpm); 22 | 23 | TspiContext::TspiContext() 24 | :ctx_(0) 25 | { 26 | TSCALL(Tspi_Context_Create, &ctx_); 27 | try { 28 | TSCALL(Tspi_Context_Connect, ctx_, NULL); 29 | } catch (...) { 30 | TSCALL(Tspi_Context_FreeMemory, ctx_, NULL); 31 | TSCALL(Tspi_Context_Close, ctx_); 32 | throw; 33 | } 34 | } 35 | 36 | TspiContext::~TspiContext() 37 | { 38 | Tspi_Context_FreeMemory(ctx_, NULL); 39 | Tspi_Context_Close(ctx_); 40 | } 41 | 42 | TspiTPM::TspiTPM(TspiContext&ctx) 43 | :tpm_(0) 44 | { 45 | TSCALL(Tspi_Context_GetTpmObject, ctx.ctx(), &tpm_); 46 | } 47 | 48 | TspiTPM::~TspiTPM() 49 | { 50 | // TODO: Something should be freed here, right? 51 | } 52 | 53 | TspiKey::TspiKey(TspiContext& ctx, TSS_UUID uuid, const std::string* pin) 54 | :ctx_(ctx), 55 | key_(0), 56 | policy_(0) 57 | { 58 | try { 59 | TSCALL(Tspi_Context_CreateObject, ctx_.ctx(), 60 | TSS_OBJECT_TYPE_RSAKEY, TSS_KEY_TSP_SRK, &key_); 61 | 62 | TSCALL(Tspi_Context_LoadKeyByUUID, ctx_.ctx(), 63 | TSS_PS_TYPE_SYSTEM, uuid, &key_); 64 | 65 | TSCALL(Tspi_Context_CreateObject, ctx_.ctx(), 66 | TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy_); 67 | 68 | set_policy_secret(policy_, pin); 69 | TSCALL(Tspi_Policy_AssignToObject, policy_, key_); 70 | } catch (...) { 71 | destroy(); 72 | throw; 73 | } 74 | } 75 | 76 | TspiKey::~TspiKey() 77 | { 78 | destroy(); 79 | } 80 | void TspiKey::destroy() 81 | { 82 | if (policy_) { 83 | Tspi_Context_CloseObject(ctx_.ctx(), policy_); 84 | } 85 | if (key_) { 86 | Tspi_Context_CloseObject(ctx_.ctx(), key_); 87 | } 88 | } 89 | 90 | TPMStuff::TPMStuff(const std::string* srk_pin) 91 | :tpm_(ctx_), 92 | srk_(ctx_, srk_uuid, srk_pin) 93 | { 94 | } 95 | END_NAMESPACE(stpm); 96 | /* ---- Emacs Variables ---- 97 | * Local Variables: 98 | * c-basic-offset: 2 99 | * indent-tabs-mode: nil 100 | * End: 101 | */ 102 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS=-I m4 2 | 3 | SUBDIRS=doc 4 | 5 | bin_PROGRAMS=stpm-keygen stpm-sign stpm-exfiltrate stpm-verify 6 | TESTS=stpm-keygen_test stpm-sign_test common_test pk11_test stpm-verify_test 7 | check_PROGRAMS=$(TESTS) 8 | 9 | common_test_SOURCES=\ 10 | src/common_test.cc \ 11 | src/common.cc \ 12 | src/tspiwrap.cc \ 13 | src/fake_tspi.cc \ 14 | src/libgtest.cc 15 | common_test_CXXFLAGS=-I/usr/src/gtest 16 | common_test_LDFLAGS= 17 | common_test_LDADD=-lpthread 18 | 19 | pk11_test_SOURCES=\ 20 | src/pk11_test.cc \ 21 | src/pk11.cc \ 22 | src/common.cc \ 23 | src/session.cc \ 24 | src/tspiwrap.cc \ 25 | src/fake_tspi.cc \ 26 | src/libgtest.cc 27 | pk11_test_CXXFLAGS=-I/usr/src/gtest 28 | pk11_test_LDFLAGS= 29 | pk11_test_LDADD=-lpthread 30 | 31 | stpm_keygen_SOURCES=\ 32 | src/keygen.cc \ 33 | src/common.cc \ 34 | src/tspiwrap.cc \ 35 | src/wrap_main.cc 36 | stpm_keygen_test_SOURCES=\ 37 | src/keygen.cc \ 38 | src/keygen_test.cc \ 39 | src/common.cc \ 40 | src/tspiwrap.cc \ 41 | src/fake_tspi.cc \ 42 | src/libgtest.cc 43 | stpm_keygen_test_CXXFLAGS=-I/usr/src/gtest 44 | stpm_keygen_test_LDFLAGS= 45 | stpm_keygen_test_LDADD=-lpthread 46 | 47 | stpm_exfiltrate_SOURCES=\ 48 | src/wrap_main.cc \ 49 | src/common.cc \ 50 | src/tspiwrap.cc \ 51 | src/exfiltrate.cc 52 | 53 | stpm_sign_SOURCES=\ 54 | src/wrap_main.cc \ 55 | src/common.cc \ 56 | src/tspiwrap.cc \ 57 | src/sign.cc 58 | stpm_sign_test_SOURCES=\ 59 | src/sign.cc \ 60 | src/sign_test.cc \ 61 | src/common.cc \ 62 | src/tspiwrap.cc \ 63 | src/fake_tspi.cc \ 64 | src/libgtest.cc 65 | stpm_sign_test_CXXFLAGS=-I/usr/src/gtest 66 | stpm_sign_test_LDFLAGS= 67 | stpm_sign_test_LDADD=-lpthread 68 | 69 | stpm_verify_SOURCES=\ 70 | src/wrap_main.cc \ 71 | src/common.cc \ 72 | src/tspiwrap.cc \ 73 | src/verify.cc 74 | stpm_verify_test_SOURCES=\ 75 | src/verify.cc \ 76 | src/verify_test.cc \ 77 | src/common.cc \ 78 | src/tspiwrap.cc \ 79 | src/fake_tspi.cc \ 80 | src/libgtest.cc 81 | stpm_verify_test_CXXFLAGS=-I/usr/src/gtest 82 | stpm_verify_test_LDFLAGS= 83 | stpm_verify_test_LDADD=-lpthread 84 | 85 | lib_LTLIBRARIES=libsimple-tpm-pk11.la 86 | # Workaround for "object `...' created both with libtool and without". 87 | libsimple_tpm_pk11_la_CXXFLAGS = $(AM_CXXFLAGS) 88 | libsimple_tpm_pk11_la_SOURCES=\ 89 | src/pk11.cc \ 90 | src/session.cc \ 91 | src/tspiwrap.cc \ 92 | src/common.cc 93 | libsimple_tpm_pk11_la_LDFLAGS=-version-info 0:0:0 94 | 95 | if WITH_PRECOMPILED_GTEST 96 | common_test_LDADD+=-lgtest -lgtest_main 97 | pk11_test_LDADD+=-lgtest -lgtest_main 98 | stpm_keygen_test_LDADD+=-lgtest -lgtest_main 99 | stpm_sign_test_LDADD+=-lgtest -lgtest_main 100 | stpm_verify_test_LDADD+=-lgtest -lgtest_main 101 | endif 102 | 103 | check-tpm: 104 | ./testscripts/all.sh 105 | 106 | mrproper: maintainer-clean 107 | rm -f aclocal.m4 configure.scan depcomp missing install-sh config.h.in 108 | rm -fr config.guess config.sub build-stamp autom4te.cache/ 109 | rm -f Makefile.in configure autoscan*.log debian/debhelper.log 110 | rm -f debian/substvars debian/files 111 | rm -fr debian/tmp 112 | rm -fr m4 113 | rm -f doc/Makefile.in 114 | -------------------------------------------------------------------------------- /src/exfiltrate.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifdef HAVE_CONFIG_H 17 | #include"config.h" 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include"common.h" 25 | #include"internal.h" 26 | 27 | extern std::string argv0base; 28 | 29 | BEGIN_NAMESPACE(); 30 | int 31 | usage(int rc) 32 | { 33 | std::cout << PACKAGE_STRING << std::endl 34 | << "Usage: " << argv0base << " [ -hsOp ] -k \n" 35 | << " -h, --help Show this help text.\n" 36 | << " -k Key file.\n" 37 | << " -O Use Well Known Secret for owner password. Default is ask.\n" 38 | << " -p Ask for key password/PIN. Default is Well Known Secret.\n" 39 | << " -s Ask for SRK password/PIN. Default is Well Known Secret.\n"; 40 | return rc; 41 | } 42 | END_NAMESPACE(); 43 | 44 | int 45 | wrapped_main(int argc, char **argv) 46 | { 47 | bool set_srk_pin = false; 48 | bool set_key_pin = false; 49 | bool set_owner_pin = true; 50 | std::string srk_pin; 51 | std::string key_pin; 52 | std::string owner; 53 | std::string keyfile; 54 | int c; 55 | 56 | while (EOF != (c = getopt(argc, argv, "hk:Ops"))) { 57 | switch (c) { 58 | case 'h': 59 | return usage(0); 60 | case 'k': 61 | keyfile = optarg; 62 | break; 63 | case 's': 64 | set_srk_pin = true; 65 | break; 66 | case 'p': 67 | set_key_pin = true; 68 | break; 69 | case 'O': 70 | set_owner_pin = false; 71 | break; 72 | default: 73 | return usage(1); 74 | } 75 | } 76 | 77 | if (keyfile.empty()) { 78 | std::cerr << argv0base << ": Empty key file name." << std::endl; 79 | return usage(1); 80 | } 81 | 82 | if (set_owner_pin) { 83 | owner = stpm::xgetpass("Enter owner password"); 84 | } 85 | 86 | if (set_key_pin) { 87 | key_pin = stpm::xgetpass("Enter key password"); 88 | } 89 | 90 | if (set_srk_pin) { 91 | srk_pin = stpm::xgetpass("Enter SRK password"); 92 | } 93 | 94 | const auto key = stpm::parse_keyfile(stpm::slurp_file(keyfile)); 95 | const auto sw = stpm::exfiltrate_key(key, 96 | set_srk_pin ? &srk_pin : nullptr, 97 | owner, 98 | set_key_pin ? &key_pin : nullptr); 99 | std::cout << sw << std::endl; 100 | return 0; 101 | } 102 | /* ---- Emacs Variables ---- 103 | * Local Variables: 104 | * c-basic-offset: 2 105 | * indent-tabs-mode: nil 106 | * End: 107 | */ 108 | -------------------------------------------------------------------------------- /src/verify.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifdef HAVE_CONFIG_H 17 | #include"config.h" 18 | #endif 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include"tss/tspi.h" 29 | 30 | #include"common.h" 31 | #include"internal.h" 32 | 33 | extern std::string argv0base; 34 | 35 | 36 | BEGIN_NAMESPACE(); 37 | int 38 | usage(int rc) 39 | { 40 | std::cout << PACKAGE_STRING << std::endl 41 | << "Usage: " << argv0base 42 | << " [ -hq ] -f -s -k \n" 43 | << " -f File to verify.\n" 44 | << " -s File containing signature.\n" 45 | << " -h, --help Show this help text.\n" 46 | << " -k File containing key data.\n" 47 | << " -q Don't output any messages.\n"; 48 | return rc; 49 | } 50 | END_NAMESPACE(); 51 | 52 | int 53 | wrapped_main(int argc, char **argv) 54 | { 55 | int c; 56 | std::string keyfile; 57 | std::string signfile; 58 | std::string signaturefile; 59 | bool quiet{false}; 60 | while (EOF != (c = getopt(argc, argv, "hk:f:s:q"))) { 61 | switch (c) { 62 | case 'h': 63 | return usage(0); 64 | case 'k': 65 | keyfile = optarg; 66 | break; 67 | case 'f': 68 | signfile = optarg; 69 | break; 70 | case 's': 71 | signaturefile = optarg; 72 | break; 73 | case 'q': 74 | quiet = true; 75 | break; 76 | default: 77 | return usage(1); 78 | } 79 | } 80 | if (optind != argc) { 81 | std::cerr << argv0base << ": Extra non-option args not allowed.\n"; 82 | return usage(1); 83 | } 84 | if (keyfile.empty() || signfile.empty() || signaturefile.empty()) { 85 | std::cerr << argv0base 86 | << ": Need to specify keyfile, data file, and signature file." 87 | << std::endl; 88 | return usage(1); 89 | } 90 | 91 | const auto key = stpm::parse_keyfile(stpm::slurp_file(keyfile)); 92 | const auto to_sign = stpm::slurp_file(signfile); 93 | const auto signature = stpm::slurp_file(signaturefile); 94 | if (!verify(key, to_sign, signature)) { 95 | if (!quiet) { 96 | std::cout << "fail\n"; 97 | } 98 | return 1; 99 | } 100 | if (!quiet) { 101 | std::cout << "success\n"; 102 | } 103 | return 0; 104 | } 105 | /* ---- Emacs Variables ---- 106 | * Local Variables: 107 | * c-basic-offset: 2 108 | * indent-tabs-mode: nil 109 | * End: 110 | */ 111 | -------------------------------------------------------------------------------- /src/session.h: -------------------------------------------------------------------------------- 1 | /** -*- c++ -*- 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef __INCLUDE__SIMPLE_TPM_PK11_SESSION_H__ 17 | #define __INCLUDE__SIMPLE_TPM_PK11_SESSION_H__ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | class PK11Error: public std::runtime_error { 27 | public: 28 | PK11Error(int incode, const std::string& msg) 29 | :std::runtime_error("Code=" + std::to_string(unsigned(incode)) + ": " + msg), 30 | code(incode) 31 | {} 32 | virtual ~PK11Error() throw() {} 33 | 34 | const int code; 35 | }; 36 | 37 | class Config { 38 | public: 39 | Config(const std::string&); 40 | 41 | std::string configfile_; 42 | std::string keyfile_; 43 | std::string logfilename_; 44 | std::shared_ptr logfile_; 45 | 46 | bool set_srk_pin_; 47 | bool set_key_pin_; 48 | std::string srk_pin_; 49 | std::string key_pin_; 50 | bool debug_; 51 | 52 | void debug_log(const char*,...) const; 53 | 54 | private: 55 | void read_file(std::ifstream&); 56 | }; 57 | 58 | /* 59 | typedef struct CK_ATTRIBUTE { 60 | CK_ATTRIBUTE_TYPE type; 61 | CK_VOID_PTR pValue; 62 | CK_ULONG ulValueLen; 63 | } CK_ATTRIBUTE; 64 | */ 65 | 66 | // deep copy of CK_ATTRIBUTE 67 | // data_ is raw data (need to be reinterpret_cast to whatever makes sense) 68 | class CK_ATTRIBUTE_FULL { 69 | public: 70 | CK_ATTRIBUTE_FULL(CK_ATTRIBUTE); // construct from CK_ATTRIBUTE 71 | CK_ATTRIBUTE_TYPE type_; 72 | std::vector data_; 73 | }; 74 | 75 | class Session { 76 | public: 77 | Session(const Config&); 78 | 79 | void Login(CK_USER_TYPE type, const std::string& pin); 80 | void FindObjectsInit(CK_ATTRIBUTE_PTR filters, int nfilters); 81 | 82 | // Find a couple of objects. Returns number of objects supplied. 83 | int FindObjects(CK_OBJECT_HANDLE_PTR obj, int maxobj); 84 | 85 | void GetAttributeValue(CK_OBJECT_HANDLE hObject, 86 | CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount); 87 | 88 | void SignInit(CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey); 89 | void Sign(CK_BYTE_PTR pData, CK_ULONG usDataLen, 90 | CK_BYTE_PTR pSignature, CK_ULONG_PTR pusSignatureLen); 91 | private: 92 | Config config_; 93 | std::string pin_; 94 | 95 | // Set up by FindObjectsInit() used as state between FindObjects() 96 | // calls. 97 | int findpos_ = 0; 98 | std::vector find_filters_; 99 | }; 100 | #endif 101 | /* ---- Emacs Variables ---- 102 | * Local Variables: 103 | * c-basic-offset: 2 104 | * indent-tabs-mode: nil 105 | * End: 106 | */ 107 | -------------------------------------------------------------------------------- /src/sign.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifdef HAVE_CONFIG_H 17 | #include"config.h" 18 | #endif 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include"tss/tspi.h" 29 | 30 | #include"common.h" 31 | #include"internal.h" 32 | 33 | extern std::string argv0base; 34 | 35 | 36 | BEGIN_NAMESPACE(); 37 | int 38 | usage(int rc) 39 | { 40 | std::cout << PACKAGE_STRING << std::endl 41 | << "Usage: " << argv0base << " [ -hrs ] -f -k \n" 42 | << " -f File to sign.\n" 43 | << " -h, --help Show this help text.\n" 44 | << " -k File containing key data.\n" 45 | << " -r Raw.\n" 46 | << " -s Prompt for SRK password/PIN.\n"; 47 | return rc; 48 | } 49 | END_NAMESPACE(); 50 | 51 | int 52 | wrapped_main(int argc, char **argv) 53 | { 54 | int c; 55 | std::string keyfile; 56 | std::string signfile; 57 | bool set_srk_pin{false}; 58 | bool set_key_pin{false}; 59 | bool raw = false; 60 | while (EOF != (c = getopt(argc, argv, "rhk:f:s"))) { 61 | switch (c) { 62 | case 'r': 63 | raw = true; 64 | break; 65 | case 'h': 66 | return usage(0); 67 | case 'k': 68 | keyfile = optarg; 69 | break; 70 | case 's': 71 | set_srk_pin = true; 72 | break; 73 | case 'f': 74 | signfile = optarg; 75 | break; 76 | default: 77 | return usage(1); 78 | } 79 | } 80 | if (optind != argc) { 81 | std::cerr << "stpm-sign: Extra non-option args not allowed.\n"; 82 | return usage(1); 83 | } 84 | if (keyfile.empty() || signfile.empty()) { 85 | std::cerr << "stpm-sign: Need to specify keyfile and data file" 86 | << std::endl; 87 | return usage(1); 88 | } 89 | 90 | const auto key = stpm::parse_keyfile(stpm::slurp_file(keyfile)); 91 | const auto to_sign = stpm::slurp_file(signfile); 92 | std::string srk_pin; 93 | if (set_srk_pin) { 94 | srk_pin = stpm::xgetpass("Enter SRK PIN"); 95 | } 96 | 97 | if (stpm::auth_required(set_srk_pin ? &srk_pin : NULL, 98 | key)) { 99 | set_key_pin = true; 100 | } 101 | 102 | std::string key_pin; 103 | if (set_key_pin) { 104 | key_pin = stpm::xgetpass("Enter key PIN"); 105 | } 106 | auto out = sign(key, to_sign, 107 | set_srk_pin ? &srk_pin : NULL, 108 | set_key_pin ? &key_pin : NULL); 109 | if (raw) { 110 | std::cout << out; 111 | } else { 112 | std::cout << "Loaded key: " << key << std::endl 113 | << "--- Signature ---\n" 114 | << stpm::to_hex(out) << std::endl; 115 | } 116 | return 0; 117 | } 118 | /* ---- Emacs Variables ---- 119 | * Local Variables: 120 | * c-basic-offset: 2 121 | * indent-tabs-mode: nil 122 | * End: 123 | */ 124 | -------------------------------------------------------------------------------- /src/keygen.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // Key generator main(). 17 | #ifdef HAVE_CONFIG_H 18 | #include"config.h" 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include"common.h" 29 | #include"internal.h" 30 | 31 | extern std::string argv0base; 32 | 33 | BEGIN_NAMESPACE(); 34 | int 35 | usage(int rc) 36 | { 37 | std::cout << PACKAGE_STRING << std::endl 38 | << "Usage: " << argv0base << " [ -hsSp ] [ -b ] -o \n" 39 | << " -b Key size in bits. TPM chips tend to support up to 2048.\n" 40 | << " -h, --help Show this help text.\n" 41 | << " -o Output file to store the key information in.\n" 42 | << " -p Set a password/PIN on the generated key.\n" 43 | << " -s Ask for SRK password/PIN. Default is Well Known Secret.\n" 44 | << " -S Generate the key in software (see manpage).\n"; 45 | return rc; 46 | } 47 | END_NAMESPACE(); 48 | 49 | int 50 | wrapped_main(int argc, char **argv) 51 | { 52 | int c; 53 | std::string output; 54 | bool set_srk_pin{false}; 55 | bool set_key_pin{false}; 56 | int bits = 2048; 57 | bool software{false}; 58 | 59 | while (EOF != (c = getopt(argc, argv, "b:ho:sSp"))) { 60 | switch (c) { 61 | case 'b': 62 | bits = std::stoi(optarg); 63 | break; 64 | case 'h': 65 | return usage(0); 66 | case 's': 67 | set_srk_pin = true; 68 | break; 69 | case 'S': 70 | software = true; 71 | break; 72 | case 'p': 73 | set_key_pin = true; 74 | break; 75 | case 'o': 76 | output = optarg; 77 | break; 78 | default: 79 | return usage(1); 80 | } 81 | } 82 | if (optind != argc) { 83 | std::cerr << argv0base << ": Extra non-option args not allowed.\n"; 84 | return usage(1); 85 | } 86 | if (output.empty()) { 87 | std::cerr << argv0base << ": Empty output file name." << std::endl; 88 | return usage(1); 89 | } 90 | 91 | std::string srk_pin; 92 | if (set_srk_pin) { 93 | srk_pin = stpm::xgetpass("Enter SRK PIN"); 94 | } 95 | 96 | std::string key_pin; 97 | if (set_key_pin) { 98 | key_pin = stpm::xgetpass("Enter key PIN"); 99 | } 100 | stpm::Key key; 101 | if (software) { 102 | const auto sw = stpm::generate_software_key(bits); 103 | key = stpm::wrap_key(set_srk_pin ? &srk_pin : nullptr, 104 | set_key_pin ? &key_pin : nullptr, 105 | sw); 106 | } else { 107 | key = stpm::generate_key(set_srk_pin ? &srk_pin : nullptr, 108 | set_key_pin ? &key_pin : nullptr, 109 | bits); 110 | } 111 | std::ofstream fo(output); 112 | if (!fo) { 113 | std::cerr << "Unable to open '" << output << "': " 114 | << strerror(errno) << std::endl; 115 | return 1; 116 | } 117 | fo << "# Some sort of key.\n" 118 | << "# Key was generated in " << (software ? "software.\n" : "hardware.\n") 119 | << "exp " << stpm::to_hex(key.exponent) << std::endl 120 | << "mod " << stpm::to_hex(key.modulus) << std::endl 121 | << "blob " << stpm::to_hex(key.blob) << std::endl; 122 | return 0; 123 | } 124 | /* ---- Emacs Variables ---- 125 | * Local Variables: 126 | * c-basic-offset: 2 127 | * indent-tabs-mode: nil 128 | * End: 129 | */ 130 | -------------------------------------------------------------------------------- /src/keygen_test.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include"config.h" 17 | #include"gtest/gtest.h" 18 | 19 | #include"test_util.h" 20 | 21 | extern std::string argv0base; 22 | 23 | static void 24 | reset_getopt() 25 | { 26 | #if HAVE_DECL_OPTRESET 27 | optreset = 1; 28 | #endif 29 | optind = 1; 30 | } 31 | 32 | namespace fake_tspi_data { 33 | extern int keysize; 34 | } 35 | 36 | extern int wrapped_main(int, char**); 37 | 38 | TEST(Usage, NoOpts) 39 | { 40 | argv0base = "foobinary"; 41 | CaptureStreams s; 42 | reset_getopt(); 43 | char *argv[] = { 44 | (char*)"keygen", 45 | NULL, 46 | }; 47 | EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 48 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 49 | EXPECT_EQ("foobinary: Empty output file name.\n", s.stderr()); 50 | EXPECT_EQ("", s.stdlog()); 51 | } 52 | 53 | TEST(Usage, HelpOpts) 54 | { 55 | CaptureStreams s; 56 | reset_getopt(); 57 | char *argv[] = { 58 | (char*)"keygen", 59 | (char*)"-h", 60 | NULL, 61 | }; 62 | EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 63 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 64 | EXPECT_EQ("", s.stderr()); 65 | EXPECT_EQ("", s.stdlog()); 66 | } 67 | 68 | TEST(Keygen, EmptyOutputName) 69 | { 70 | argv0base = "foobinary"; 71 | CaptureStreams s; 72 | reset_getopt(); 73 | char *argv[] = { 74 | (char*)"keygen", 75 | (char*)"-o", 76 | (char*)"", 77 | NULL, 78 | }; 79 | EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 80 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 81 | EXPECT_EQ("foobinary: Empty output file name.\n", s.stderr()); 82 | EXPECT_EQ("", s.stdlog()); 83 | } 84 | 85 | TEST(Keygen, BadOutputName) 86 | { 87 | CaptureStreams s; 88 | reset_getopt(); 89 | char *argv[] = { 90 | (char*)"keygen", 91 | (char*)"-o", 92 | (char*)"/non/existing/file/here/3ht.sn,hsn", 93 | NULL, 94 | }; 95 | EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 96 | EXPECT_EQ("", s.stdout()); 97 | EXPECT_EQ("Unable to open '/non/existing/file/here/3ht.sn,hsn':" 98 | " No such file or directory\n", s.stderr()); 99 | EXPECT_EQ("Modulus size: 10\nExponent size: 10\nSize: 2048\nBlob size: 10\n", 100 | s.stdlog()); 101 | } 102 | 103 | TEST(Keygen, OK) 104 | { 105 | CaptureStreams s; 106 | reset_getopt(); 107 | char *argv[] = { 108 | (char*)"keygen", 109 | (char*)"-o", 110 | (char*)"/dev/null", 111 | NULL, 112 | }; 113 | EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 114 | EXPECT_EQ("", s.stdout()); 115 | EXPECT_EQ("", s.stderr()); 116 | EXPECT_EQ("Modulus size: 10\nExponent size: 10\nSize: 2048\nBlob size: 10\n", 117 | s.stdlog()); 118 | } 119 | 120 | TEST(Keygen, SWKeyOK) 121 | { 122 | CaptureStreams s; 123 | reset_getopt(); 124 | char *argv[] = { 125 | (char*)"keygen", 126 | (char*)"-S", 127 | (char*)"-o", 128 | (char*)"/dev/null", 129 | NULL, 130 | }; 131 | EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 132 | EXPECT_EQ("", s.stdout()); 133 | EXPECT_EQ("", s.stderr()); 134 | EXPECT_EQ("", s.stdlog()); 135 | } 136 | 137 | TEST(Keygen, SmallerKey) 138 | { 139 | CaptureStreams s; 140 | reset_getopt(); 141 | char *argv[] = { 142 | (char*)"keygen", 143 | (char*)"-b", 144 | (char*)"1024", 145 | (char*)"-o", 146 | (char*)"/dev/null", 147 | NULL, 148 | }; 149 | fake_tspi_data::keysize = 1024; 150 | EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 151 | EXPECT_EQ("", s.stdout()); 152 | EXPECT_EQ("", s.stderr()); 153 | EXPECT_EQ("Modulus size: 10\nExponent size: 10\nSize: 1024\nBlob size: 10\n", 154 | s.stdlog()); 155 | } 156 | 157 | TEST(Keygen, WrongTPMKeySize) 158 | { 159 | CaptureStreams s; 160 | reset_getopt(); 161 | char *argv[] = { 162 | (char*)"keygen", 163 | (char*)"-b", 164 | (char*)"1024", 165 | (char*)"-o", 166 | (char*)"/dev/null", 167 | NULL, 168 | }; 169 | fake_tspi_data::keysize = 2048; 170 | EXPECT_THROW(wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv), 171 | std::exception); 172 | } 173 | /* ---- Emacs Variables ---- 174 | * Local Variables: 175 | * c-basic-offset: 2 176 | * indent-tabs-mode: nil 177 | * End: 178 | */ 179 | -------------------------------------------------------------------------------- /src/sign_test.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include"config.h" 17 | #include 18 | #include"gtest/gtest.h" 19 | 20 | #include"test_util.h" 21 | 22 | static void 23 | reset_getopt() 24 | { 25 | #if HAVE_DECL_OPTRESET 26 | optreset = 1; 27 | #endif 28 | optind = 1; 29 | } 30 | 31 | extern int wrapped_main(int, char**); 32 | 33 | TEST(Usage, NoOpts) 34 | { 35 | CaptureStreams s; 36 | reset_getopt(); 37 | char *argv[] = { 38 | (char*)"sign", 39 | NULL, 40 | }; 41 | EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 42 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 43 | EXPECT_EQ("stpm-sign: Need to specify keyfile and data file\n", s.stderr()); 44 | EXPECT_EQ("", s.stdlog()); 45 | } 46 | 47 | TEST(Sign, NoDataFile) 48 | { 49 | CaptureStreams s; 50 | reset_getopt(); 51 | char *argv[] = { 52 | (char*)"sign", 53 | (char*)"-k", 54 | (char*)"", 55 | NULL, 56 | }; 57 | EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 58 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 59 | EXPECT_EQ("stpm-sign: Need to specify keyfile and data file\n", s.stderr()); 60 | EXPECT_EQ("", s.stdlog()); 61 | } 62 | 63 | TEST(Sign, NoKeyFile) 64 | { 65 | CaptureStreams s; 66 | reset_getopt(); 67 | char *argv[] = { 68 | (char*)"sign", 69 | (char*)"-f", 70 | (char*)"", 71 | NULL, 72 | }; 73 | EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 74 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 75 | EXPECT_EQ("stpm-sign: Need to specify keyfile and data file\n", s.stderr()); 76 | EXPECT_EQ("", s.stdlog()); 77 | } 78 | 79 | TEST(Usage, HelpOpts) 80 | { 81 | CaptureStreams s; 82 | reset_getopt(); 83 | char *argv[] = { 84 | (char*)"sign", 85 | (char*)"-h", 86 | NULL, 87 | }; 88 | EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 89 | EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); 90 | EXPECT_EQ("", s.stderr()); 91 | EXPECT_EQ("", s.stdlog()); 92 | } 93 | 94 | TEST(Sign, BadKeyfileName) 95 | { 96 | CaptureStreams s; 97 | reset_getopt(); 98 | char *argv[] = { 99 | (char*)"sign", 100 | (char*)"-k", 101 | (char*)"/non/existing/file/here/3ht.sn,hsn", 102 | (char*)"-f", 103 | (char*)"/dev/null", 104 | NULL, 105 | }; 106 | EXPECT_THROW(wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv), 107 | std::runtime_error); 108 | EXPECT_EQ("", s.stdout()); 109 | EXPECT_EQ("", s.stderr()); 110 | EXPECT_EQ("", s.stdlog()); 111 | } 112 | 113 | TEST(Sign, BadDatafileName) 114 | { 115 | CaptureStreams s; 116 | reset_getopt(); 117 | char *argv[] = { 118 | (char*)"sign", 119 | (char*)"-k", 120 | (char*)"testdata/correct.key", 121 | (char*)"-f", 122 | (char*)"/non/existing/file/here/3ht.sn,hsn", 123 | NULL, 124 | }; 125 | EXPECT_THROW(wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv), 126 | std::runtime_error); 127 | EXPECT_EQ("", s.stdlog()); 128 | EXPECT_EQ("", s.stdout()); 129 | EXPECT_EQ("", s.stderr()); 130 | EXPECT_EQ("", s.stdlog()); 131 | } 132 | 133 | TEST(Sign, BadKeyfile) 134 | { 135 | CaptureStreams s; 136 | reset_getopt(); 137 | char *argv[] = { 138 | (char*)"sign", 139 | (char*)"-k", 140 | (char*)"testdata/broken.key", 141 | (char*)"-f", 142 | (char*)"/dev/null", 143 | NULL, 144 | }; 145 | bool threw = false; 146 | try { 147 | wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv); 148 | } catch (const std::runtime_error& e) { 149 | EXPECT_EQ("Keyfile format error(line 2: typo 010001)", 150 | std::string(e.what())); 151 | EXPECT_EQ("", s.stdlog()); 152 | EXPECT_EQ("", s.stdout()); 153 | EXPECT_EQ("", s.stderr()); 154 | threw = true; 155 | } 156 | EXPECT_TRUE(threw); 157 | } 158 | 159 | TEST(Sign, OK) 160 | { 161 | CaptureStreams s; 162 | reset_getopt(); 163 | char *argv[] = { 164 | (char*)"sign", 165 | (char*)"-k", 166 | (char*)"testdata/correct.key", 167 | (char*)"-f", 168 | (char*)"/dev/null", 169 | NULL, 170 | }; 171 | EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); 172 | EXPECT_EQ("Loaded key: mod=010203,exp=010001,blob=010203040506\n--- Signature ---\n12345678\n", s.stdout()); 173 | EXPECT_EQ("", s.stderr()); 174 | EXPECT_EQ("", s.stdlog()); 175 | } 176 | /* ---- Emacs Variables ---- 177 | * Local Variables: 178 | * c-basic-offset: 2 179 | * indent-tabs-mode: nil 180 | * End: 181 | */ 182 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /** -*- c++ -*- 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Header file for all library functions. 18 | */ 19 | #ifndef __INCLUDE__SIMPLE_TPM_PK11_COMMON_H__ 20 | #define __INCLUDE__SIMPLE_TPM_PK11_COMMON_H__ 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include"tss/tspi.h" 29 | 30 | namespace stpm { 31 | #if 0 32 | } 33 | #endif 34 | 35 | template 36 | class AutoFree { 37 | public: 38 | AutoFree(): resource_(New()) {} 39 | AutoFree(T* r): resource_(r) {} 40 | AutoFree(const AutoFree&) = delete; 41 | AutoFree(const AutoFree&&) = delete; 42 | AutoFree& operator=(const AutoFree&) = delete; 43 | AutoFree& operator=(const AutoFree&&) = delete; 44 | ~AutoFree() 45 | { 46 | if (!resource_) { 47 | return; 48 | } 49 | Free(resource_); 50 | resource_ = nullptr; 51 | } 52 | T* get() const 53 | { 54 | return resource_; 55 | } 56 | T** getp() 57 | { 58 | return &resource_; 59 | } 60 | T* operator->() const 61 | { 62 | return resource_; 63 | } 64 | T* release() 65 | { 66 | T* ret = resource_; 67 | resource_ = nullptr; 68 | return ret; 69 | } 70 | private: 71 | T* resource_; 72 | }; 73 | 74 | typedef AutoFree RSAWrap; 75 | typedef AutoFree BIGNUMWrap; 76 | typedef AutoFree BNCTXWrap; 77 | 78 | // Exception type for TPM errors, adding helpful troubleshooting information 79 | // in extra(). 80 | class TSPIException: public std::runtime_error { 81 | public: 82 | TSPIException(const std::string& s, int code); 83 | virtual ~TSPIException() throw() {}; 84 | const std::string& extra() const { return extra_; } 85 | const int tspi_error; 86 | 87 | private: 88 | static std::string code_to_extra(int); 89 | static std::string code_to_string(int); 90 | 91 | const std::string extra_; 92 | }; 93 | 94 | // TPM key parts in binary. 95 | struct Key { 96 | std::string exponent; // Almost certainly 65537. 97 | std::string modulus; // 98 | std::string blob; // For HW keys, blob encrypted by SRK. 99 | }; 100 | 101 | // Software key parts in binary. 102 | struct SoftwareKey { 103 | std::string exponent; // Almost certainly 65537. 104 | std::string modulus; // 105 | std::string key; // The private key. 106 | }; 107 | 108 | 109 | // Convert binary to hex and back. 110 | std::string to_hex(const std::string& s); 111 | std::string to_bin(const std::string& s); 112 | 113 | // Like basename(3), but with std::string. 114 | std::string xbasename(const std::string& fullpath); 115 | 116 | std::string xgethostname(); 117 | 118 | // Parse a keyfile into a struct. Does not use the TPM. 119 | Key parse_keyfile(const std::string&); 120 | 121 | // Generate a signing key inside the TPM. 122 | // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). 123 | Key generate_key(const std::string* srk_pin, const std::string* key_pin, 124 | int bits); 125 | 126 | // Generate an RSA key in software. 127 | SoftwareKey generate_software_key(int bits); 128 | 129 | // Generate a signing key inside the TPM. 130 | // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). 131 | Key wrap_key(const std::string* srk_pin, const std::string* key_pin, 132 | const SoftwareKey& key); 133 | 134 | // Sign plain data. 135 | // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). 136 | std::string sign(const Key& key, const std::string& data, 137 | const std::string* srk_pin, 138 | const std::string* key_pin); 139 | 140 | // Verify signature. 141 | // This is a software-only operation. 142 | bool verify(const Key& key, const std::string& data, 143 | const std::string& sig); 144 | 145 | // Exfiltrate key 146 | // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). 147 | SoftwareKey exfiltrate_key(const Key& key, 148 | const std::string* srk_pin, 149 | const std::string& owner_password, 150 | const std::string* key_pin); 151 | 152 | // Return true if key is password protected. 153 | bool auth_required(const std::string* srk_pin, const Key& key); 154 | 155 | std::string xctime(); 156 | 157 | // Read in a whole file. 158 | std::string slurp_file(const std::string& fn); 159 | 160 | void do_log(std::ostream* o, const std::string& msg); 161 | std::string xsprintf(const char* fmt, ...); 162 | 163 | 164 | // This function assumes std::cin is connected to STDIN_FILENO, 165 | // and that std::cout and std::cin are attached to "the terminal". 166 | std::string xgetpass(const std::string& prompt); 167 | 168 | void set_policy_secret(TSS_HPOLICY policy, const std::string* pin); 169 | } // namespace stpm 170 | 171 | // Pretty-print keys. 172 | std::ostream& operator<<(std::ostream&, const struct stpm::Key&); 173 | std::ostream& operator<<(std::ostream&, const struct stpm::SoftwareKey&); 174 | #endif 175 | /* ---- Emacs Variables ---- 176 | * Local Variables: 177 | * c-basic-offset: 2 178 | * indent-tabs-mode: nil 179 | * End: 180 | */ 181 | -------------------------------------------------------------------------------- /src/pk11_test.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // Test the PKCS#11 module purely through the C API. 17 | 18 | #include"gtest/gtest.h" 19 | #include 20 | 21 | #include"common.h" 22 | #include"test_util.h" 23 | 24 | namespace fake_tspi_data { 25 | extern int keysize; 26 | } 27 | 28 | class PK11Test: public ::testing::Test { 29 | public: 30 | void SetUp() 31 | { 32 | setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.config", 1); 33 | setenv("SIMPLE_TPM_PK11_DEBUG", "on", 1); 34 | ASSERT_EQ(CKR_OK, C_GetFunctionList(&func_)); 35 | ASSERT_EQ(CKR_OK, func_->C_Initialize(h_)); 36 | } 37 | void TearDown() 38 | { 39 | ASSERT_EQ(CKR_OK, func_->C_Finalize(h_)); 40 | } 41 | 42 | protected: 43 | CaptureStreams cs; 44 | CK_FUNCTION_LIST *func_; 45 | void* h_; 46 | }; 47 | 48 | TEST_F(PK11Test, GetInfo) 49 | { 50 | CK_INFO info; 51 | ASSERT_EQ(CKR_OK, func_->C_GetInfo(&info)); 52 | ASSERT_EQ(0, info.cryptokiVersion.major); 53 | ASSERT_EQ(1, info.cryptokiVersion.minor); 54 | ASSERT_EQ(0, info.libraryVersion.major); 55 | ASSERT_EQ(1, info.libraryVersion.minor); 56 | ASSERT_EQ("simple-tpm-pk11 manufacturer", 57 | std::string((char*)info.manufacturerID)); 58 | ASSERT_EQ("simple-tpm-pk11 library", 59 | std::string((char*)info.libraryDescription)); 60 | } 61 | 62 | TEST_F(PK11Test, NoConfig) 63 | { 64 | setenv("SIMPLE_TPM_PK11_CONFIG", "/config/missing/here", 1); 65 | 66 | CK_SESSION_HANDLE s; 67 | ASSERT_EQ(CKR_FUNCTION_FAILED, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); 68 | } 69 | 70 | TEST_F(PK11Test, EmptyConfig) 71 | { 72 | setenv("SIMPLE_TPM_PK11_CONFIG", "/dev/null", 1); 73 | setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); 74 | CK_SESSION_HANDLE s; 75 | ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); 76 | 77 | CK_MECHANISM mech = { 78 | CKM_RSA_PKCS, NULL_PTR, 0 79 | }; 80 | 81 | CK_OBJECT_HANDLE key = 0; 82 | // TODO: Get first key. 83 | 84 | CK_BYTE data[35]; 85 | CK_BYTE signature[20]; 86 | CK_ULONG slen; 87 | ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); 88 | ASSERT_EQ(CKR_GENERAL_ERROR, func_->C_Sign(s, data, sizeof(data), signature, &slen)); 89 | ASSERT_NE(cs.stderr().find("/dev/" + stpm::xgethostname() + ".key"), std::string::npos); 90 | } 91 | 92 | TEST_F(PK11Test, MissingKeyfile) 93 | { 94 | setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.missingkeyfile.config", 1); 95 | setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); 96 | CK_SESSION_HANDLE s; 97 | ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); 98 | 99 | CK_MECHANISM mech = { 100 | CKM_RSA_PKCS, NULL_PTR, 0 101 | }; 102 | 103 | CK_OBJECT_HANDLE key = 0; 104 | // TODO: Get first key. 105 | 106 | CK_BYTE data[35]; 107 | CK_BYTE signature[20]; 108 | CK_ULONG slen; 109 | ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); 110 | ASSERT_EQ(CKR_GENERAL_ERROR, func_->C_Sign(s, data, sizeof(data), signature, &slen)); 111 | EXPECT_NE(std::string::npos, cs.stderr().find("Failed to read key file 'testdata/missing-file'")); 112 | } 113 | 114 | TEST_F(PK11Test, BadKeyfile) 115 | { 116 | setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.badkeyfile.config", 1); 117 | setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); 118 | CK_SESSION_HANDLE s; 119 | ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); 120 | 121 | CK_MECHANISM mech = { 122 | CKM_RSA_PKCS, NULL_PTR, 0 123 | }; 124 | 125 | CK_OBJECT_HANDLE key = 0; 126 | // TODO: Get first key. 127 | 128 | CK_BYTE data[35]; 129 | CK_BYTE signature[20]; 130 | CK_ULONG slen; 131 | ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); 132 | ASSERT_EQ(CKR_FUNCTION_FAILED, func_->C_Sign(s, data, sizeof(data), signature, &slen)); 133 | EXPECT_NE(std::string::npos, cs.stderr().find("Keyfile format error")); 134 | } 135 | 136 | TEST_F(PK11Test, AbsKeyfile) 137 | { 138 | setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.abskeyfile.config", 1); 139 | setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); 140 | CK_SESSION_HANDLE s; 141 | ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); 142 | 143 | CK_MECHANISM mech = { 144 | CKM_RSA_PKCS, NULL_PTR, 0 145 | }; 146 | 147 | CK_OBJECT_HANDLE key = 0; 148 | // TODO: Get first key. 149 | 150 | CK_BYTE data[35]; 151 | CK_BYTE signature[20]; 152 | CK_ULONG slen; 153 | ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); 154 | ASSERT_EQ(CKR_FUNCTION_FAILED, func_->C_Sign(s, data, sizeof(data), signature, &slen)); 155 | EXPECT_NE(std::string::npos, cs.stderr().find("Keyfile incomplete")); 156 | } 157 | 158 | TEST_F(PK11Test, Sign) 159 | { 160 | // TODO: actually test correct output. 161 | CK_SESSION_HANDLE s; 162 | ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); 163 | 164 | CK_MECHANISM mech = { 165 | CKM_RSA_PKCS, NULL_PTR, 0 166 | }; 167 | 168 | CK_OBJECT_HANDLE key = 0; 169 | // TODO: Get first key. 170 | 171 | CK_BYTE data[35]; 172 | CK_BYTE signature[20]; 173 | CK_ULONG slen; 174 | ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); 175 | ASSERT_EQ(CKR_OK, func_->C_Sign(s, data, sizeof(data), signature, &slen)); 176 | ASSERT_EQ(CKR_OK, func_->C_CloseSession(s)); 177 | } 178 | -------------------------------------------------------------------------------- /src/fake_tspi.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Fake libtspi for testing. 18 | */ 19 | #include"tss/tspi.h" 20 | 21 | namespace fake_tspi_data { 22 | int keysize = 2048; 23 | } 24 | 25 | TSPICALL 26 | Tspi_Context_Create(TSS_HCONTEXT* phContext) 27 | { 28 | return TSS_SUCCESS; 29 | } 30 | 31 | TSPICALL 32 | Tspi_SetAttribUint32(TSS_HOBJECT hObject, 33 | TSS_FLAG attribFlag, 34 | TSS_FLAG subFlag, 35 | UINT32 ulAttrib) 36 | { 37 | return TSS_SUCCESS; 38 | } 39 | 40 | TSPICALL 41 | Tspi_GetAttribUint32(TSS_HOBJECT hObject, 42 | TSS_FLAG attribFlag, 43 | TSS_FLAG subFlag, 44 | UINT32* pulAttrib) 45 | { 46 | switch (subFlag) { 47 | case TSS_TSPATTRIB_KEYINFO_AUTHDATAUSAGE: 48 | *pulAttrib = 0; 49 | break; 50 | case TSS_TSPATTRIB_KEYINFO_RSA_KEYSIZE: 51 | *pulAttrib = fake_tspi_data::keysize; 52 | break; 53 | } 54 | return TSS_SUCCESS; 55 | } 56 | 57 | TSPICALL 58 | Tspi_GetAttribData(TSS_HOBJECT hObject, 59 | TSS_FLAG attribFlag, 60 | TSS_FLAG subFlag, 61 | UINT32* pulAttribDataSize, 62 | BYTE** prgbAttribData) 63 | { 64 | static BYTE buf[10]; 65 | *pulAttribDataSize = 10; 66 | *prgbAttribData = buf; 67 | return TSS_SUCCESS; 68 | } 69 | 70 | TSPICALL 71 | Tspi_Context_Connect(TSS_HCONTEXT hContext, 72 | TSS_UNICODE* wszDestination) 73 | { 74 | return TSS_SUCCESS; 75 | } 76 | 77 | TSPICALL 78 | Tspi_Key_CreateKey(TSS_HKEY hKey, 79 | TSS_HKEY hWrappingKey, 80 | TSS_HPCRS hPcrComposite) 81 | { 82 | return TSS_SUCCESS; 83 | } 84 | 85 | TSPICALL 86 | Tspi_Context_CreateObject(TSS_HCONTEXT hContext, 87 | TSS_FLAG objectType, 88 | TSS_FLAG initFlags, 89 | TSS_HOBJECT* phObject) 90 | { 91 | return TSS_SUCCESS; 92 | } 93 | 94 | TSPICALL 95 | Tspi_Context_GetTpmObject(TSS_HCONTEXT hContext, 96 | TSS_HTPM* phTPM) 97 | { 98 | return TSS_SUCCESS; 99 | } 100 | 101 | TSPICALL 102 | Tspi_TPM_StirRandom(TSS_HTPM hTPM, 103 | UINT32 ulEntropyDataLength, 104 | BYTE* rgbEntropyData) 105 | { 106 | return TSS_SUCCESS; 107 | } 108 | 109 | TSPICALL 110 | Tspi_Context_LoadKeyByBlob(TSS_HCONTEXT hContext, 111 | TSS_HKEY hUnwrappingKey, 112 | UINT32 ulBlobLength, 113 | BYTE* rgbBlobData, 114 | TSS_HKEY* phKey) 115 | { 116 | return TSS_SUCCESS; 117 | } 118 | 119 | TSPICALL 120 | Tspi_Context_LoadKeyByUUID(TSS_HCONTEXT hContext, 121 | TSS_FLAG persistentStorageType, 122 | TSS_UUID uuidData, 123 | TSS_HKEY* phKey) 124 | { 125 | return TSS_SUCCESS; 126 | } 127 | 128 | TSPICALL 129 | Tspi_Policy_SetSecret(TSS_HPOLICY hPolicy, 130 | TSS_FLAG secretMode, 131 | UINT32 ulSecretLength, 132 | BYTE* rgbSecret) 133 | { 134 | return TSS_SUCCESS; 135 | } 136 | 137 | TSPICALL 138 | Tspi_Policy_AssignToObject(TSS_HPOLICY hPolicy, 139 | TSS_HOBJECT hObject) 140 | { 141 | return TSS_SUCCESS; 142 | } 143 | 144 | TSPICALL 145 | Tspi_Hash_Sign(TSS_HHASH hHash, 146 | TSS_HKEY hKey, 147 | UINT32* pulSignatureLength, 148 | BYTE** prgbSignature) 149 | { 150 | static BYTE sign[] = {0x12, 0x34, 0x56, 0x78}; 151 | *pulSignatureLength = sizeof(sign); 152 | *prgbSignature = sign; 153 | return TSS_SUCCESS; 154 | } 155 | 156 | TSPICALL 157 | Tspi_Hash_SetHashValue(TSS_HHASH hHash, 158 | UINT32 ulHashValueLength, 159 | BYTE* rgbHashValue) 160 | { 161 | return TSS_SUCCESS; 162 | } 163 | 164 | TSPICALL 165 | Tspi_Hash_UpdateHashValue(TSS_HHASH hHash, 166 | UINT32 ulDataLength, 167 | BYTE* rgbData) 168 | { 169 | return TSS_SUCCESS; 170 | } 171 | 172 | TSPICALL 173 | Tspi_Key_GetPubKey(TSS_HKEY hKey, 174 | UINT32* pulPubKeyLength, 175 | BYTE** prgbPubKey) 176 | { 177 | return TSS_SUCCESS; 178 | } 179 | 180 | TSPICALL 181 | Tspi_SetAttribData(TSS_HOBJECT hObject, 182 | TSS_FLAG attribFlag, 183 | TSS_FLAG subFlag, 184 | UINT32 ulAttribDataSize, 185 | BYTE* rgbAttribData) 186 | { 187 | return TSS_SUCCESS; 188 | } 189 | 190 | TSPICALL 191 | Tspi_Key_WrapKey(TSS_HKEY hKey, 192 | TSS_HKEY hWrappingKey, 193 | TSS_HPCRS hPcrComposite) 194 | { 195 | return TSS_SUCCESS; 196 | } 197 | 198 | TSPICALL 199 | Tspi_TPM_AuthorizeMigrationTicket(TSS_HTPM hTPM, 200 | TSS_HKEY hMigrationKey, 201 | TSS_MIGRATE_SCHEME migrationScheme, 202 | UINT32* pulMigTicketLength, 203 | BYTE** prgbMigTicket) 204 | { 205 | return TSS_SUCCESS; 206 | } 207 | 208 | 209 | /* ---- Emacs Variables ---- 210 | * Local Variables: 211 | * c-basic-offset: 2 212 | * indent-tabs-mode: nil 213 | * End: 214 | */ 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple TPM PK11 2 | 3 | A simple library for using the TPM chip to secure SSH keys. 4 | 5 | Copyright 2013-2016 Google Inc. All Rights Reserved. 6 | Apache 2.0 license. 7 | 8 | This is NOT a Google product. 9 | 10 | Contact: thomas@habets.se / habets@google.com 11 | https://github.com/ThomasHabets/ 12 | 13 | 14 | ## Install dependencies 15 | 16 | ### Debian 17 | ```shell 18 | apt install tpm-tools libtspi-dev libopencryptoki-dev libssl-dev 19 | ``` 20 | 21 | ### Fedora 22 | ```shell 23 | tpm-tools 24 | opencryptoki-devel 25 | trousers-devel 26 | openssl-devel 27 | ``` 28 | 29 | ### FreeBSD 30 | ```shell 31 | pkg install tpm-tools trousers-tddl opencryptoki openssl 32 | ``` 33 | 34 | ## Build simple-tpm-pk11 35 | 36 | If there is no `configure` script (e.g. because the release comes directly 37 | from the git repo where generated files are not committed), then run: 38 | 39 | ```shell 40 | apt install autotools libtool # Or equivalent for your OS. 41 | ./bootstrap.sh 42 | ``` 43 | 44 | When `configure` does exist, configure and build: 45 | 46 | ```shell 47 | ./configure && make && sudo make install 48 | ``` 49 | 50 | 51 | ## Init TPM chip 52 | 1. If you have not taken ownership, do so. 53 | ```shell 54 | tpm_takeownership -z 55 | Enter owner password: [enter something secret here] 56 | Confirm password: [enter something secret here] 57 | ``` 58 | 59 | 2. SRK password is usually the Well Known Secret (all nulls). You can 60 | specify a password but it's easier it you don't. The SRK password is only 61 | used to allow crypto operations. You still need blobs and key passwords to 62 | use other peoples keys. 63 | 64 | The "SRK password" is needed to be able to do operations with the "SRK", 65 | which is the actual cryptographic key. The user has no access to the SRK 66 | directly. The same goes for other keys protected by the TPM chip. 67 | ```shell 68 | tpm_changeownerauth -s -r 69 | ``` 70 | 71 | If you get any error messages, see read TPM-TROUBLESHOOTING. 72 | 73 | ## User setup 74 | 75 | ### 1. Create key 76 | ``` 77 | mkdir ~/.simple-tpm-pk11/ 78 | stpm-keygen -o ~/.simple-tpm-pk11/my.key 79 | ``` 80 | 81 | (use `-p` if you want to set a password on the key) 82 | 83 | Try out the key: 84 | ``` 85 | dd if=/dev/urandom of=to-sign bs=1 count=35 86 | stpm-sign -k ~/.simple-tpm-pk11/my.key -f to-sign 87 | stpm-sign -k ~/.simple-tpm-pk11/my.key -f to-sign -r > to-sign.sig 88 | stpm-verify -f to-sign -k ~/.simple-tpm-pk11/my.key -s to-sign.sig 89 | ``` 90 | 91 | ### 2. Create config 92 | ``` 93 | echo "key my.key" > ~/.simple-tpm-pk11/config 94 | ``` 95 | 96 | #### Optional config options 97 | 98 | ##### `log foo.log` 99 | ##### `key_pin my-pin-here` 100 | ##### `srk_pin my-pin-here` 101 | ##### `debug` 102 | 103 | ### 3. Extract the public key in SSH format 104 | ``` 105 | ssh-keygen -D libsimple-tpm-pk11.so 106 | ``` 107 | 108 | Install it where you want to log in, in the usual authorized_keys way. 109 | 110 | Try logging in using your new fancy key: 111 | ``` 112 | ssh -I libsimple-tpm-pk11.so shell.example.com 113 | ``` 114 | 115 | ### 4. Configure SSH to always use this module 116 | Add this to `~/.ssh/config`: 117 | ``` 118 | Host * 119 | PKCS11Provider libsimple-tpm-pk11.so 120 | ``` 121 | 122 | then try: 123 | ```shell 124 | ssh shell.example.com 125 | ``` 126 | 127 | ### 4a. Alternatively, add the TPM to your `ssh-agent` 128 | 129 | This has to be the OpenSSH `ssh-agent`, since gnome-keyring doesn't support 130 | PKCS#11. A sign that you run gnome-keyring (or your OpenSSH is compiled 131 | without PKCS#11 support) is that you see this error message when you try: 132 | 133 | ``` 134 | $ ssh-add -s /…/libsimple-tpm-pk11.so 135 | Enter passphrase for PKCS#11: 136 | Could not add card "/…/libsimple-tpm-pk11.so": agent refused operation 137 | ``` 138 | 139 | ## Tested with 140 | 141 | ### Hardware 142 | * Dell Precision T3500 / WEC TPM 1.2.2.81 143 | * HP Z440 / IFX TPM 1.2.4.40 144 | * Lenovo T410 / STM TPM 1.2.8.16 145 | * Lenovo T440s / STM TPM 1.2.13.12 146 | * Lenovo T500 / INTC STM 1.2.4.1 147 | * Lenono X200s / INTC TPM 1.2.4.1 148 | * Lenovo X230 / STM TPM 1.2.13.8 149 | * Lenovo X240 / STM TPM 1.2.13.12 150 | * Lenovo T460s /IFX TPM 1.2.6.40 151 | 152 | ### Software 153 | * OpenSSH 5.9 154 | * OpenSSH 6.0p1 on Debian 7.2 155 | * OpenSSH 6.4p1 on CentOS 7.0 156 | * OpenSSH 6.6.1p1 on FreeBSD 11-CURRENT 157 | * OpenSSH 6.8p1 on Arch Linux 158 | * OpenSSH 7.1p2 on Debian 159 | * OpenSSH 7.2p2 Ubuntu 16.04.2 LTS (Xenial Xerus) 160 | * OpenSSH 7.9p1 on Debian 10.3 161 | 162 | ## TODO 163 | * Clean up code. 164 | * config option: log to stdout and/or stderr in addition to logfile. 165 | * Install in the correct place. 166 | * Add PKCS11 support to ssh *server*. 167 | * Global config in /etc. 168 | * Script to automate setting up, including verifying TPM state and fixing it. 169 | * Auto-generate keys on demand? Or should this only be part of script to set up? 170 | * Make it work with gpg, and document. 171 | * Make it work with SSH certs, and document. 172 | * Make it work with openssl, and document. 173 | * Make it work with Firefox, and document. 174 | * Make it work with Chrome, and document. 175 | * Make it work with encrypted home directories, and document. 176 | 177 | 178 | ## Reference links 179 | * http://secgroup.ext.dsi.unive.it/projects/security-apis/tookan/ 180 | * http://secgroup.ext.dsi.unive.it/projects/security-apis/cryptokix/ 181 | * http://trousers.sourceforge.net/pkcs11.html 182 | * http://www.trustedcomputinggroup.org/resources/tcg_software_stack_tss_specification 183 | * http://www.infineon.com/dgdl/TPM+Key+Backup+and+Recovery.pdf 184 | * http://www.engadget.com/2010/02/12/christopher-tarnovsky-hacks-infineons-unhackable-chip-we-pre/ 185 | * http://resources.infosecinstitute.com/linux-tpm-encryption-initializing-and-using-the-tpm/ 186 | * http://p11-glue.freedesktop.org/p11-kit.html 187 | * http://trousers.sourceforge.net/dev_faq.html 188 | 189 | 190 | ## Make new release 191 | * Update configure.ac with new version, commit. 192 | * git tag -a -s 0.0x 193 | * git push --tags 194 | 195 | 196 | ## Some random notes, not instructions 197 | ```shell 198 | openssl genrsa -out rsa-key 2048 199 | openssl rsa -in rsa-key -modulus 200 | exponent is always 65537. 201 | ssh-keygen -f rsa-key -y > rsa-key.pub 202 | ``` 203 | -------------------------------------------------------------------------------- /check-srk/check-srk.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | * Ugly hack to extract the SRK modulus to then check for Infineon disaster vuln. 18 | * 19 | * Compile with: 20 | * g++ -o check-srk -std=gnu++11 check-srk.cc -ltspi -lssl -lcrypto 21 | * 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include"../src/common.h" 37 | 38 | std::vector>> 39 | make_tests() 40 | { 41 | // Credit: https://crypto.stackexchange.com/questions/52292/what-is-fast-prime 42 | const std::vector> generators = { 43 | {2, 11}, 44 | {6, 13}, 45 | {8, 17}, 46 | {9, 19}, 47 | {3, 37}, 48 | {26, 53}, 49 | {20, 61}, 50 | {35, 71}, 51 | {24, 73}, 52 | {13, 79}, 53 | {6, 97}, 54 | {51, 103}, 55 | {53, 107}, 56 | {54, 109}, 57 | {42, 127}, 58 | {50, 151}, 59 | {78, 157}, 60 | }; 61 | 62 | stpm::BNCTXWrap ctx; 63 | std::vector>> ret; 64 | for (const auto& g : generators) { 65 | stpm::BIGNUMWrap r, p, res, i; 66 | 67 | BN_dec2bn(r.getp(), std::to_string(g.first).c_str()); 68 | BN_dec2bn(p.getp(), std::to_string(g.second).c_str()); 69 | 70 | std::set l; 71 | for (int c = 0; c < g.second; c++) { 72 | BN_dec2bn(i.getp(), std::to_string(c).c_str()); 73 | BN_mod_exp(res.get(), i.get(), r.get(), p.get(), ctx.get()); 74 | if (!strcmp(BN_bn2dec(res.get()), "1")) { 75 | l.insert(c); 76 | } 77 | } 78 | ret.push_back({g.second, l}); 79 | } 80 | return ret; 81 | } 82 | 83 | const std::vector>> tests = make_tests(); 84 | 85 | bool 86 | is_vuln(BIGNUM* modulus) { 87 | stpm::BNCTXWrap ctx; 88 | 89 | stpm::BIGNUMWrap m, s, z; 90 | for (const auto ms : tests) { 91 | BN_dec2bn(m.getp(), std::to_string(ms.first).c_str()); 92 | BN_mod(z.get(), modulus, m.get(), ctx.get()); 93 | const std::string lhs = BN_bn2dec(z.get()); 94 | char* end; 95 | auto n = strtoul(lhs.c_str(), &end, 10); 96 | if (*end) { 97 | std::cerr << "internal errz\n"; 98 | exit(1); 99 | } 100 | if (!ms.second.count(n)) { 101 | return false; 102 | } 103 | } 104 | return true; 105 | } 106 | 107 | void 108 | self_test() 109 | { 110 | std::clog << "Running self test…\n"; 111 | const std::string bad = 112 | "19938056020098197365562045602452702481764029031788901515041637346" 113 | "59664301810397435517958505193657308878577182396178388805949567514" 114 | "93072326478239626428117453688560571382508192163477482599263238431" 115 | "23409832353911425474214399357279154291350609095053100959029512111" 116 | "18843523882986697836910369306226827544922081786889434456105668874" 117 | "75559631105557876688469588507548413520336914672788668741683050708" 118 | "52577071087674316932650955818705357676526040250814850835930967219" 119 | "87657456512408680098709248942496286520609642408378616866232460847" 120 | "82246097027454407148731567090590896088004920146482353728837014129" 121 | "47627934562329911019602948939463"; 122 | const std::string good = 123 | "10370958248394357517927560885830722492008924663624587591822132987" 124 | "96780013019139759397453360064558094368183834718579254844745074698" 125 | "14152191546180006061001646994710489151933960477358446164968530575" 126 | "65033770341665769755559091048068263979924104921646738915200262650" 127 | "78651948163902522767455462804827862036592643288146195217828089952" 128 | "88806167443550955372809448344660673102522500801426337516136781403" 129 | "92439851732672531048042240879559796179184971260687770238899126397" 130 | "90153991157220986800506575923567680693759749035973704825051373788" 131 | "95095946104093984893324803391764381923945603803100327231565213476" 132 | "35531022883252286505870258531927"; 133 | 134 | stpm::BIGNUMWrap m; 135 | BN_dec2bn(m.getp(), good.c_str()); 136 | if (is_vuln(m.get())) { 137 | std::cerr << "Internal self test error: Known good was detected as bad\n"; 138 | exit(1); 139 | } 140 | 141 | BN_dec2bn(m.getp(), bad.c_str()); 142 | if (!is_vuln(m.get())) { 143 | std::cerr << "Internal self test error: Known bad was detected as good\n"; 144 | exit(1); 145 | } 146 | } 147 | 148 | int 149 | main(int argc, char** argv) 150 | { 151 | self_test(); 152 | 153 | // NULL for WKS. 154 | const char* srk_pin = NULL; 155 | int opt; 156 | while ((opt = getopt(argc, argv, "s:")) != -1) { 157 | switch (opt) { 158 | case 's': 159 | srk_pin = optarg; 160 | break; 161 | default: 162 | fprintf(stderr, "Usage: %s [-s ]\n", argv[0]); 163 | exit(EXIT_FAILURE); 164 | } 165 | } 166 | 167 | TSS_HCONTEXT ctx; 168 | if (TSS_SUCCESS != Tspi_Context_Create(&ctx)) { 169 | fprintf(stderr, "Failed to create context\n"); 170 | exit(1); 171 | } 172 | if (TSS_SUCCESS != Tspi_Context_Connect(ctx, NULL)) { 173 | fprintf(stderr, "Failed to connect context\n"); 174 | exit(1); 175 | } 176 | 177 | TSS_HTPM tpm; 178 | TSS_RESULT res = Tspi_Context_GetTpmObject(ctx, &tpm); 179 | if (TSS_SUCCESS != res) { 180 | fprintf(stderr, "Failed to get TPM object: %d %x\n", res, res); 181 | exit(1); 182 | } 183 | 184 | TSS_HKEY key; 185 | TSS_HPOLICY policy; 186 | res = Tspi_Context_CreateObject(ctx, 187 | TSS_OBJECT_TYPE_RSAKEY, TSS_KEY_TSP_SRK, &key); 188 | if (TSS_SUCCESS != res) { 189 | fprintf(stderr, "Failed to create SRK object: %d %x\n", res, res); 190 | exit(1); 191 | } 192 | 193 | res = Tspi_Context_LoadKeyByUUID( 194 | ctx, 195 | TSS_PS_TYPE_SYSTEM, 196 | TSS_UUID_SRK, 197 | &key); 198 | if (TSS_SUCCESS != res) { 199 | fprintf(stderr, "Failed to load SRK key: %x\n", res); 200 | exit(1); 201 | } 202 | 203 | res = Tspi_Context_CreateObject(ctx, 204 | TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy); 205 | 206 | if (TSS_SUCCESS != res) { 207 | fprintf(stderr, "Failed to create policy: %d %x\n", res, res); 208 | exit(1); 209 | } 210 | 211 | // Set up SRK PIN. 212 | if (!srk_pin){ 213 | BYTE wks[] = TSS_WELL_KNOWN_SECRET; 214 | int wks_size = sizeof(wks); 215 | res = Tspi_Policy_SetSecret(policy, 216 | TSS_SECRET_MODE_SHA1, wks_size, wks); 217 | if (TSS_SUCCESS != res) { 218 | fprintf(stderr, "Failed to set WKS: %d %x\n", res, res); 219 | exit(1); 220 | } 221 | } else { 222 | res = Tspi_Policy_SetSecret(policy, TSS_SECRET_MODE_PLAIN, strlen(srk_pin),(BYTE*)srk_pin); 223 | if (TSS_SUCCESS != res) { 224 | fprintf(stderr, "Failed to set WKS: %d %x\n", res, res); 225 | exit(1); 226 | } 227 | } 228 | 229 | res = Tspi_Policy_AssignToObject(policy, key); 230 | if (TSS_SUCCESS != res) { 231 | fprintf(stderr, "Failed to assign policy to key: %d %x\n", res, res); 232 | exit(1); 233 | } 234 | 235 | // Get size. 236 | { 237 | UINT32 size; 238 | res = Tspi_GetAttribUint32( 239 | key, 240 | TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_KEYSIZE, 241 | &size); 242 | if (TSS_SUCCESS != res) { 243 | fprintf(stderr, "Failed to get length of SRK: %d %x\n", res, res); 244 | exit(1); 245 | } 246 | std::clog << "Size: " << size << std::endl; 247 | } 248 | 249 | if (true) { 250 | // TODO: I don't know why I have to GetPubKey before GetAttribData, 251 | // but apparently I do. 252 | BYTE *srk_pub; 253 | UINT32 srk_pub_len = 0; 254 | res = Tspi_Key_GetPubKey(key, &srk_pub_len, &srk_pub); 255 | if (TSS_SUCCESS != res) { 256 | fprintf(stderr, 257 | "Failed to SRK pubkey: %x\n" 258 | "Maybe you have an SRK PIN you need to supply with -s?\n", res); 259 | exit(1); 260 | } 261 | } 262 | 263 | // Get modulus. 264 | { 265 | BYTE* m; 266 | UINT32 m_size = 0; 267 | res = Tspi_GetAttribData( 268 | key, TSS_TSPATTRIB_RSAKEY_INFO, 269 | TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, 270 | &m_size, &m); 271 | if (TPM_ERROR(res)) { 272 | fprintf(stderr, "Failed to get SRK modulus: %x\n", res); 273 | exit(1); 274 | } 275 | stpm::BIGNUMWrap mod; 276 | if (!BN_bin2bn(m, m_size, mod.get())) { 277 | std::cerr << "BN_bin2bn failed\n"; 278 | exit(1); 279 | } 280 | std::clog << "Modulus:\n"; 281 | std::cout << BN_bn2dec(mod.get()) << std::endl; 282 | if (is_vuln(mod.get())) { 283 | std::cerr << "--------------\nTHE KEY IS WEAK!\n"; 284 | } else { 285 | std::cerr << "--------------\nThe key is fine.\n"; 286 | } 287 | } 288 | } 289 | /* ---- Emacs Variables ---- 290 | * Local Variables: 291 | * c-basic-offset: 2 292 | * indent-tabs-mode: nil 293 | * End: 294 | */ 295 | -------------------------------------------------------------------------------- /src/pk11.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * 18 | * 19 | * This file contains the PKCS#11 API, meaning C_GetFunctionList and the C 20 | * callbacks it supplies. 21 | * 22 | * In this file is the glue between the trousers C API and the rest of the 23 | * simple-tpm-pk11 code which is C++. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include"tss/tspi.h" 38 | 39 | #include"common.h" 40 | #include"internal.h" 41 | #include"session.h" 42 | 43 | using stpm::xctime; 44 | using stpm::xsprintf; 45 | 46 | BEGIN_NAMESPACE(); 47 | 48 | CK_FUNCTION_LIST funclist; 49 | const std::string config_dir = ".simple-tpm-pk11"; 50 | const char* env_debug = "SIMPLE_TPM_PK11_DEBUG"; 51 | const char* env_config = "SIMPLE_TPM_PK11_CONFIG"; 52 | const CK_SLOT_ID tpm_slot_id = 0x1234; 53 | const char library_description[] = "simple-tpm-pk11 library"; // 32 54 | const char manufacturer_id[] = "simple-tpm-pk11 manufacturer"; // 32 55 | const char slot_description[] = "Simple-TPM-PK11 slot"; // 64 56 | const char token_label[] = "Simple-TPM-PK11 token"; // 32 57 | const char token_model[] = "model"; // 16 58 | const char token_serial[] = "serial"; // 16 59 | 60 | // TODO: allocate and free sessions properly. 61 | std::vector sessions; 62 | 63 | Config get_config(); 64 | 65 | void 66 | log_error(const std::string& msg) 67 | { 68 | try { 69 | auto cfg = get_config(); 70 | stpm::do_log(cfg.logfile_.get(), xctime() + " " + msg); 71 | } catch(...) { 72 | std::cerr << "PK11 ERROR> " << msg << std::endl; 73 | } 74 | syslog(LOG_ERR, "%s", msg.c_str()); 75 | } 76 | 77 | bool 78 | debug_env() 79 | { 80 | const char *dbg{getenv(env_debug)}; 81 | return !!dbg; 82 | } 83 | 84 | void 85 | log_debug(const std::string& msg) 86 | { 87 | try { 88 | auto cfg = get_config(); 89 | if (cfg.debug_ || debug_env()) { 90 | stpm::do_log(cfg.logfile_.get(), xctime() + " DEBUG " + msg); 91 | } 92 | } catch (...) { 93 | if (debug_env()) { 94 | std::cerr << xctime() << " DEBUG " << msg << std::endl; 95 | } 96 | } 97 | } 98 | 99 | // string must be padded with space (0x20) and must not be null terminated 100 | static void 101 | write_pk11_str(void* dest, const char* src, size_t src_size, size_t max_size) 102 | { 103 | memset((char*)dest, 0x20, max_size); 104 | memcpy((char*)dest, src, std::min(src_size, max_size)); 105 | } 106 | 107 | Config 108 | get_config() 109 | { 110 | const char* home{getenv("HOME")}; 111 | if (home == nullptr) { 112 | throw std::runtime_error(std::string(__func__) + "(): " 113 | + "getenv(HOME) failed."); 114 | } 115 | 116 | std::string config_path{std::string{home} + "/" + config_dir + "/config"}; 117 | const char* conf_env{getenv(env_config)}; 118 | if (conf_env) { 119 | config_path = conf_env; 120 | } 121 | 122 | auto ret = Config{config_path}; 123 | if (debug_env()) { 124 | ret.debug_ = true; 125 | } 126 | return ret; 127 | } 128 | 129 | CK_RV 130 | wrap_exceptions(const std::string& name, std::function f) 131 | { 132 | log_debug(name); 133 | try { 134 | f(); 135 | return CKR_OK; 136 | } catch (const PK11Error& e) { 137 | log_error(name + "(): " + e.what()); 138 | return e.code; 139 | } catch (const std::exception& e) { 140 | log_error(name + "(): " + e.what()); 141 | } catch (...) { 142 | log_error(name + "(): Unknown exception"); 143 | } 144 | return CKR_FUNCTION_FAILED; 145 | } 146 | 147 | CK_RV 148 | C_GetInfo(CK_INFO_PTR pInfo) 149 | { 150 | return wrap_exceptions(__func__, [&]{ 151 | memset(pInfo, 0, sizeof(CK_INFO)); 152 | pInfo->cryptokiVersion.major = 0; 153 | pInfo->cryptokiVersion.minor = 1; 154 | // TODO: flags 155 | write_pk11_str(pInfo->libraryDescription, library_description, strlen(library_description), 32); 156 | write_pk11_str(pInfo->manufacturerID, manufacturer_id, strlen(manufacturer_id), 32); 157 | 158 | // TODO: take these version numbers from somewhere canonical. 159 | pInfo->libraryVersion.major = 0; 160 | pInfo->libraryVersion.minor = 1; 161 | }); 162 | } 163 | 164 | CK_RV 165 | C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, 166 | CK_ULONG_PTR pusCount) 167 | { 168 | return wrap_exceptions(__func__, [&]{ 169 | if (pSlotList && *pusCount) { 170 | *pSlotList = tpm_slot_id; 171 | } 172 | *pusCount = 1; 173 | }); 174 | } 175 | 176 | CK_RV 177 | C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, 178 | CK_VOID_PTR pApplication, 179 | CK_RV (*Notify) (CK_SESSION_HANDLE hSession, 180 | CK_NOTIFICATION event, CK_VOID_PTR pApplication), 181 | CK_SESSION_HANDLE_PTR phSession) 182 | { 183 | return wrap_exceptions(__func__, [&]{ 184 | sessions.emplace_back(get_config()); 185 | *phSession = sessions.size() - 1; 186 | }); 187 | } 188 | 189 | CK_RV 190 | C_CloseSession(CK_SESSION_HANDLE hSession) 191 | { 192 | return wrap_exceptions(__func__, [&]{}); 193 | } 194 | 195 | CK_RV 196 | C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) 197 | { 198 | return wrap_exceptions(__func__, [&]{ 199 | pInfo->slotID = 0; 200 | pInfo->state = CKS_RW_USER_FUNCTIONS; /* ? */ 201 | pInfo->flags = CKF_SERIAL_SESSION; 202 | pInfo->ulDeviceError = 0; 203 | }); 204 | } 205 | 206 | CK_RV 207 | C_Login(CK_SESSION_HANDLE hSession, 208 | CK_USER_TYPE userType, CK_CHAR_PTR pPin, 209 | CK_ULONG usPinLen) 210 | { 211 | return wrap_exceptions(__func__, [&]{ 212 | sessions[hSession].Login(userType, std::string{pPin, pPin+usPinLen}); 213 | }); 214 | } 215 | 216 | CK_RV 217 | C_Logout(CK_SESSION_HANDLE hSession) 218 | { 219 | return wrap_exceptions(__func__, [&]{}); 220 | } 221 | 222 | CK_RV 223 | C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) 224 | { 225 | return wrap_exceptions(__func__, [&]{ 226 | // TODO: fill these out from slot. 227 | write_pk11_str(pInfo->slotDescription, slot_description, strlen(slot_description), 64); 228 | write_pk11_str(pInfo->manufacturerID, manufacturer_id, strlen(manufacturer_id), 32); 229 | 230 | pInfo->flags = CKF_TOKEN_PRESENT; 231 | pInfo->hardwareVersion = { 0, 0 }; 232 | pInfo->firmwareVersion = { 0, 0 }; 233 | }); 234 | } 235 | 236 | 237 | CK_RV 238 | C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) 239 | { 240 | return wrap_exceptions(__func__, [&]{ 241 | // TODO: fill these out from token. 242 | write_pk11_str(pInfo->label, token_label, strlen(token_label), 32); 243 | write_pk11_str(pInfo->manufacturerID, manufacturer_id, strlen(manufacturer_id), 32); 244 | write_pk11_str(pInfo->model, token_model, strlen(token_model), 16); 245 | write_pk11_str(pInfo->serialNumber, token_serial, strlen(token_serial), 16); 246 | 247 | // TODO: Add CKF_RNG. 248 | pInfo->flags = CKF_TOKEN_INITIALIZED; 249 | auto config = get_config(); 250 | 251 | std::string kfs; 252 | try { 253 | kfs = stpm::slurp_file(config.keyfile_); 254 | } catch (...) { 255 | throw PK11Error(CKR_GENERAL_ERROR, 256 | "Failed to read key file '" + config.keyfile_ + "'"); 257 | } 258 | const stpm::Key key = stpm::parse_keyfile(kfs); 259 | 260 | if (stpm::auth_required(config.set_srk_pin_ ? &config.srk_pin_ : NULL, 261 | key)) { 262 | pInfo->flags |= CKF_LOGIN_REQUIRED; 263 | } 264 | pInfo->ulMaxSessionCount = 1000; 265 | pInfo->ulSessionCount = 0; 266 | pInfo->ulMaxRwSessionCount = 1000; 267 | pInfo->ulRwSessionCount = 0; 268 | pInfo->ulMaxPinLen = 64; 269 | pInfo->ulMinPinLen = 6; 270 | pInfo->ulTotalPublicMemory = 1000000; 271 | pInfo->ulFreePublicMemory = 1000000; 272 | pInfo->ulTotalPrivateMemory = 1000000; 273 | pInfo->ulFreePrivateMemory = 1000000; 274 | pInfo->hardwareVersion.major = 0; 275 | pInfo->firmwareVersion.major = 0; 276 | strcpy((char*)pInfo->utcTime, "bleh"); // TODO. 277 | }); 278 | } 279 | 280 | CK_RV 281 | C_GetMechanismList(CK_SLOT_ID slot_id, CK_MECHANISM_TYPE_PTR pMechanismList, 282 | CK_ULONG_PTR pulCount) 283 | { 284 | return wrap_exceptions(__func__, [&]{ 285 | // TODO: I'm making these mechanisms up. They are probably not correct. 286 | if (slot_id != tpm_slot_id) { 287 | throw PK11Error(CKR_GENERAL_ERROR, "Not supported."); 288 | } 289 | if (*pulCount > 0) { 290 | pMechanismList[0] = CKM_RSA_PKCS; 291 | } 292 | 293 | // PKCS#11 key generation not yet implemented. 294 | // if (*pulCount > 1) { 295 | // pMechanismList[1] = CKM_RSA_PKCS_KEY_PAIR_GEN; 296 | // } 297 | *pulCount = 1; 298 | }); 299 | } 300 | 301 | CK_RV 302 | C_GetMechanismInfo(CK_SLOT_ID slot_id, CK_MECHANISM_TYPE type, 303 | CK_MECHANISM_INFO *info) 304 | { 305 | // TODO: I'm making these mechanisms up. They are probably not correct. 306 | return wrap_exceptions(__func__, [&]{ 307 | if (slot_id != tpm_slot_id) { 308 | throw PK11Error(CKR_GENERAL_ERROR, "Not supported."); 309 | } 310 | info->ulMinKeySize = 512; 311 | info->ulMaxKeySize = 2048; 312 | switch (type) { 313 | case CKM_RSA_PKCS: 314 | info->flags = CKF_SIGN | CKF_HW; 315 | break; 316 | case CKM_RSA_PKCS_KEY_PAIR_GEN: 317 | info->flags = CKF_GENERATE_KEY_PAIR | CKF_HW; 318 | break; 319 | default: 320 | throw PK11Error(CKR_GENERAL_ERROR, "Not supported."); 321 | } 322 | }); 323 | } 324 | 325 | CK_RV 326 | C_Finalize(CK_VOID_PTR pReserved) 327 | { 328 | return wrap_exceptions(__func__, [&]{}); 329 | } 330 | 331 | CK_RV 332 | C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR filters, 333 | CK_ULONG nfilters) 334 | { 335 | return wrap_exceptions(__func__, [&]{ 336 | sessions[hSession].FindObjectsInit(filters, nfilters); 337 | }); 338 | } 339 | 340 | CK_RV 341 | C_FindObjects(CK_SESSION_HANDLE hSession, 342 | CK_OBJECT_HANDLE_PTR phObject, CK_ULONG usMaxObjectCount, 343 | CK_ULONG_PTR nfound) 344 | { 345 | return wrap_exceptions(__func__, [&]{ 346 | *nfound = sessions[hSession].FindObjects(phObject, usMaxObjectCount); 347 | }); 348 | } 349 | 350 | CK_RV 351 | C_FindObjectsFinal(CK_SESSION_HANDLE hSession) 352 | { 353 | return wrap_exceptions(__func__, [&]{}); 354 | } 355 | 356 | CK_RV 357 | C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, 358 | CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount) 359 | { 360 | return wrap_exceptions(xsprintf("%s(count=%d)", __func__, usCount), [&]{ 361 | sessions[hSession].GetAttributeValue(hObject, pTemplate, usCount); 362 | }); 363 | } 364 | 365 | CK_RV 366 | C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, 367 | CK_OBJECT_HANDLE hKey) 368 | { 369 | return wrap_exceptions(__func__, [&]{ 370 | sessions[hSession].SignInit(pMechanism, hKey); 371 | }); 372 | } 373 | 374 | CK_RV 375 | C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, 376 | CK_ULONG usDataLen, CK_BYTE_PTR pSignature, 377 | CK_ULONG_PTR pusSignatureLen) 378 | { 379 | return wrap_exceptions(__func__, [&]{ 380 | sessions[hSession].Sign(pData, usDataLen, 381 | pSignature, pusSignatureLen); 382 | }); 383 | } 384 | 385 | CK_RV 386 | C_Initialize(CK_VOID_PTR pInitArgs) 387 | { 388 | return wrap_exceptions(__func__, [&]{}); 389 | } 390 | 391 | CK_RV 392 | C_InitToken(CK_SLOT_ID slot_id, unsigned char *pin, 393 | unsigned long pin_len, unsigned char *label) 394 | { 395 | return wrap_exceptions(__func__, [&]{ 396 | throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); 397 | }); 398 | } 399 | 400 | CK_RV 401 | C_InitPIN(CK_SESSION_HANDLE session, unsigned char *pin, 402 | unsigned long pin_len) 403 | { 404 | return wrap_exceptions(__func__, [&]{ 405 | throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); 406 | }); 407 | } 408 | 409 | CK_RV 410 | C_SetPIN(CK_SESSION_HANDLE session, unsigned char *old_pin, 411 | unsigned long old_len, unsigned char *new_pin, 412 | unsigned long new_len) 413 | { 414 | return wrap_exceptions(__func__, [&]{ 415 | throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); 416 | }); 417 | } 418 | 419 | CK_RV 420 | C_CloseAllSessions(CK_SLOT_ID slot_id) 421 | { 422 | return wrap_exceptions(__func__, [&]{ 423 | throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); 424 | }); 425 | } 426 | 427 | 428 | CK_RV 429 | C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) 430 | { 431 | return wrap_exceptions(__func__, [&]{ 432 | // No random number generation is supported. 433 | throw PK11Error(CKR_RANDOM_NO_RNG, "Not supported."); 434 | }); 435 | } 436 | 437 | __attribute__((constructor)) 438 | void cons() 439 | { 440 | #define F(x) funclist.C_##x = C_##x 441 | F(GetInfo); 442 | F(Initialize); 443 | F(InitToken); 444 | F(InitPIN); 445 | F(SetPIN); 446 | F(Finalize); 447 | F(GetSlotList); 448 | F(GetSlotInfo); 449 | F(GetTokenInfo); 450 | F(GetMechanismList); 451 | F(GetMechanismInfo); 452 | F(Login); 453 | F(Logout); 454 | 455 | F(OpenSession); 456 | F(CloseSession); 457 | F(CloseAllSessions); 458 | F(GetSessionInfo); 459 | 460 | F(FindObjectsInit); 461 | F(FindObjects); 462 | F(FindObjectsFinal); 463 | 464 | F(GetAttributeValue); 465 | F(SignInit); 466 | F(Sign); 467 | F(SeedRandom); 468 | #undef F 469 | } 470 | 471 | END_NAMESPACE(); 472 | 473 | extern "C" CK_RV 474 | C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) 475 | { 476 | return wrap_exceptions(__func__, [&]{ 477 | *ppFunctionList = &funclist; 478 | }); 479 | } 480 | /* ---- Emacs Variables ---- 481 | * Local Variables: 482 | * c-basic-offset: 2 483 | * indent-tabs-mode: nil 484 | * End: 485 | */ 486 | -------------------------------------------------------------------------------- /src/session.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include"session.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include"common.h" 31 | #include"internal.h" 32 | 33 | BEGIN_NAMESPACE(); 34 | 35 | // Like dirname(3), but always returns a string ending in '/', thus 36 | // always being safe for appeding a filename to. 37 | std::string 38 | xdirname(const std::string& relative) 39 | { 40 | const size_t s = relative.size(); 41 | std::vector buf(s + 1); 42 | memcpy(&buf[0], relative.data(), s); 43 | const std::string ret{dirname(&buf[0])}; 44 | if (ret == "/") { 45 | return ret; 46 | } 47 | return ret + "/"; 48 | } 49 | END_NAMESPACE(); 50 | 51 | Config::Config(const std::string& fn) 52 | :configfile_(fn), 53 | logfile_(new std::ofstream), 54 | set_srk_pin_(false), 55 | set_key_pin_(false), 56 | debug_(false) 57 | { 58 | std::ifstream f{fn}; 59 | if (!f) { 60 | throw std::runtime_error("Opening config file " + fn + " failed"); 61 | } 62 | read_file(f); 63 | if (*logfile_) { 64 | logfile_->open(logfilename_, std::ofstream::app); 65 | if (!logfile_) { 66 | throw std::runtime_error("Unable to open logfile " + logfilename_); 67 | } 68 | } 69 | if (keyfile_.empty()) { 70 | // TODO: should use fqdn? 71 | keyfile_ = xdirname(configfile_) + stpm::xgethostname() + ".key"; 72 | } 73 | } 74 | 75 | void 76 | Config::read_file(std::ifstream& f) 77 | { 78 | while (!f.eof()) { 79 | std::string line; 80 | getline(f, line); 81 | if (line.empty() || line[0] == '#') { 82 | continue; 83 | } 84 | 85 | std::istringstream linetokens{line}; 86 | std::string cmd, rest; 87 | getline(linetokens, cmd, ' '); 88 | getline(linetokens, rest); 89 | 90 | if (cmd == "key") { 91 | keyfile_ = rest; 92 | if (keyfile_.substr(0, 1) != "/") { 93 | keyfile_ = xdirname(configfile_) + rest; 94 | } 95 | } else if (cmd == "log") { 96 | logfilename_ = rest; 97 | if (logfilename_.substr(0, 1) != "/") { 98 | logfilename_ = xdirname(configfile_) + rest; 99 | } 100 | } else if (cmd == "key_pin") { 101 | key_pin_ = rest; 102 | set_key_pin_ = true; 103 | } else if (cmd == "srk_pin") { 104 | srk_pin_ = rest; 105 | set_srk_pin_ = true; 106 | } else if (cmd == "debug") { 107 | debug_ = true; 108 | } else { 109 | throw std::runtime_error("Unknown config line: " + line); 110 | } 111 | } 112 | } 113 | 114 | static 115 | CK_OBJECT_CLASS 116 | object_class(CK_OBJECT_HANDLE hObject) 117 | { 118 | return (hObject == 1) ? CKO_PUBLIC_KEY : CKO_PRIVATE_KEY; 119 | } 120 | 121 | // create deep copy 122 | CK_ATTRIBUTE_FULL::CK_ATTRIBUTE_FULL(CK_ATTRIBUTE attr) 123 | :type_(attr.type), data_(static_cast(attr.pValue), static_cast(attr.pValue) + attr.ulValueLen) 124 | { 125 | } 126 | 127 | Session::Session(const Config& config) 128 | :config_(config) 129 | { 130 | } 131 | 132 | void 133 | Session::Login(CK_USER_TYPE type, const std::string& pin) 134 | { 135 | config_.key_pin_ = pin; 136 | config_.set_key_pin_ = true; 137 | } 138 | 139 | void 140 | Session::FindObjectsInit(CK_ATTRIBUTE_PTR filters, int nfilters) 141 | { 142 | findpos_ = 1; // Handles can't be 0, or cryptoki will interpret it as an error 143 | // create deep copy of attribute filter array 144 | // it's possible that the memory of attribute filter array be reclaimed by the caller 145 | // between the call of FindObjectsInit and FindObjects 146 | find_filters_ = std::vector(filters, filters + nfilters); 147 | } 148 | 149 | int 150 | Session::FindObjects(CK_OBJECT_HANDLE_PTR obj, int maxobj) 151 | { 152 | int numFound = 0; 153 | for (; numFound < maxobj && findpos_ <= 2; findpos_++) { 154 | bool filterRejected = false; 155 | for (auto& x : find_filters_) { 156 | if (x.type_ == CKA_CLASS) { 157 | // match object class 158 | // only CKO_PUBLIC_KEY and CKO_PRIVATE_KEY is allowed 159 | if (*reinterpret_cast(x.data_.data()) != object_class(findpos_)) { 160 | filterRejected = true; 161 | break; 162 | } 163 | } else { 164 | // Ignore all other filters 165 | // TODO: implement CKA_ID and CKA_LABEL match 166 | } 167 | } 168 | if (!filterRejected) { 169 | obj[numFound++] = findpos_; 170 | } 171 | } 172 | return numFound; 173 | } 174 | 175 | void 176 | Config::debug_log(const char* fmt, ...) const 177 | { 178 | va_list args; 179 | va_start(args, fmt); 180 | 181 | va_list va2; 182 | va_copy(va2, args); 183 | 184 | size_t s = vsnprintf(NULL, 0, fmt, args) + 1; 185 | va_end(args); 186 | 187 | std::vector buf(s); 188 | vsnprintf(&buf[0], s, fmt, va2); 189 | va_end(va2); 190 | 191 | if (debug_) { 192 | stpm::do_log(logfile_.get(), stpm::xctime() + " DEBUG " + std::string(buf.begin(), buf.end()-1)); 193 | } 194 | } 195 | 196 | static bool 197 | getPublicKeyAttribute(const Config& config, CK_ATTRIBUTE_PTR pAttribute) 198 | { 199 | switch (pAttribute->type) { 200 | case CKA_ENCRYPT: 201 | config.debug_log(" Attribute: Encrypt"); 202 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 203 | if (pAttribute->pValue != nullptr) { 204 | *(CK_BBOOL *)(pAttribute->pValue) = false; 205 | } 206 | return true; 207 | 208 | case CKA_VERIFY: 209 | config.debug_log(" Attribute: Verify"); 210 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 211 | if (pAttribute->pValue != nullptr) { 212 | *(CK_BBOOL *)(pAttribute->pValue) = true; 213 | } 214 | return true; 215 | 216 | case CKA_VERIFY_RECOVER: 217 | config.debug_log(" Attribute: Verify Recover"); 218 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 219 | if (pAttribute->pValue != nullptr) { 220 | *(CK_BBOOL *)(pAttribute->pValue) = false; 221 | } 222 | return true; 223 | 224 | case CKA_WRAP: 225 | config.debug_log(" Attribute: Wrap"); 226 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 227 | if (pAttribute->pValue != nullptr) { 228 | *(CK_BBOOL *)(pAttribute->pValue) = false; 229 | } 230 | return true; 231 | 232 | case CKA_TRUSTED: 233 | config.debug_log(" Attribute: Trusted"); 234 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 235 | if (pAttribute->pValue != nullptr) { 236 | *(CK_BBOOL *)(pAttribute->pValue) = true; 237 | } 238 | return true; 239 | 240 | default: 241 | return false; 242 | } 243 | } 244 | 245 | static bool 246 | getPrivateKeyAttribute(const Config& config, CK_ATTRIBUTE_PTR pAttribute) 247 | { 248 | switch (pAttribute->type) { 249 | case CKA_SENSITIVE: 250 | config.debug_log(" Attribute: Sensitive"); 251 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 252 | if (pAttribute->pValue != nullptr) { 253 | *(CK_BBOOL *)(pAttribute->pValue) = true; 254 | } 255 | return true; 256 | 257 | case CKA_DECRYPT: 258 | config.debug_log(" Attribute: Decrypt"); 259 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 260 | if (pAttribute->pValue != nullptr) { 261 | *(CK_BBOOL *)(pAttribute->pValue) = false; 262 | } 263 | return true; 264 | 265 | case CKA_SIGN: 266 | config.debug_log(" Attribute: Sign"); 267 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 268 | if (pAttribute->pValue != nullptr) { 269 | *(CK_BBOOL *)(pAttribute->pValue) = true; 270 | } 271 | return true; 272 | 273 | case CKA_SIGN_RECOVER: 274 | config.debug_log(" Attribute: Sign Recover"); 275 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 276 | if (pAttribute->pValue != nullptr) { 277 | *(CK_BBOOL *)(pAttribute->pValue) = false; 278 | } 279 | return true; 280 | 281 | case CKA_UNWRAP: 282 | config.debug_log(" Attribute: Unwrap"); 283 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 284 | if (pAttribute->pValue != nullptr) { 285 | *(CK_BBOOL *)(pAttribute->pValue) = false; 286 | } 287 | return true; 288 | 289 | case CKA_EXTRACTABLE: 290 | config.debug_log(" Attribute: Extractable"); 291 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 292 | if (pAttribute->pValue != nullptr) { 293 | *(CK_BBOOL *)(pAttribute->pValue) = false; 294 | } 295 | return true; 296 | 297 | case CKA_ALWAYS_SENSITIVE: 298 | config.debug_log(" Attribute: Always Sensitive"); 299 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 300 | if (pAttribute->pValue != nullptr) { 301 | *(CK_BBOOL *)(pAttribute->pValue) = true; 302 | } 303 | return true; 304 | 305 | case CKA_NEVER_EXTRACTABLE: 306 | config.debug_log(" Attribute: Never Extractable"); 307 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 308 | if (pAttribute->pValue != nullptr) { 309 | *(CK_BBOOL *)(pAttribute->pValue) = true; 310 | } 311 | return true; 312 | #ifdef CKA_WRAP_WITH_TRUSTED 313 | case CKA_WRAP_WITH_TRUSTED: 314 | config.debug_log(" Attribute: Wrap with Trusted"); 315 | pAttribute->ulValueLen = sizeof(CK_BBOOL); 316 | if (pAttribute->pValue != nullptr) { 317 | *(CK_BBOOL *)(pAttribute->pValue) = false; 318 | } 319 | return true; 320 | #endif 321 | default: 322 | return false; 323 | } 324 | } 325 | 326 | void 327 | Session::GetAttributeValue(CK_OBJECT_HANDLE hObject, 328 | CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount) 329 | { 330 | std::string kfs; 331 | try { 332 | kfs = stpm::slurp_file(config_.keyfile_); 333 | } catch (...) { 334 | throw PK11Error(CKR_GENERAL_ERROR, 335 | "Failed to read key file '" + config_.keyfile_ + "'"); 336 | } 337 | const stpm::Key key = stpm::parse_keyfile(kfs); 338 | 339 | // TODO: maybe ID and label can be defined in config file 340 | char object_id[] = "1111"; 341 | char public_key_label[] = "simple-tpm-public-key"; 342 | char private_key_label[] = "simple-tpm-private-key"; 343 | char unknown_key_label[] = "simple-tpm-unknown-key"; 344 | 345 | for (unsigned i = 0; i < usCount; i++) { 346 | switch (pTemplate[i].type) { 347 | case CKA_CLASS: 348 | config_.debug_log(" Attribute %d: Class", i); 349 | pTemplate[i].ulValueLen = sizeof(CK_OBJECT_CLASS); 350 | if (pTemplate[i].pValue != nullptr) { 351 | *(CK_OBJECT_CLASS *)(pTemplate[i].pValue) = object_class(hObject); 352 | } 353 | break; 354 | 355 | case CKA_KEY_TYPE: 356 | config_.debug_log(" Attribute %d: Key type", i); 357 | pTemplate[i].ulValueLen = sizeof(CK_KEY_TYPE); 358 | if (pTemplate[i].pValue != nullptr) { 359 | *(CK_KEY_TYPE *)(pTemplate[i].pValue) = CKK_RSA; 360 | } 361 | break; 362 | 363 | case CKA_DERIVE: 364 | config_.debug_log(" Attribute %d: Derive", i); 365 | pTemplate[i].ulValueLen = sizeof(CK_BBOOL); 366 | if (pTemplate[i].pValue != nullptr) { 367 | *(CK_BBOOL *)(pTemplate[i].pValue) = false; 368 | } 369 | break; 370 | 371 | case CKA_LOCAL: 372 | config_.debug_log(" Attribute %d: Local", i); 373 | pTemplate[i].ulValueLen = sizeof(CK_BBOOL); 374 | if (pTemplate[i].pValue != nullptr) { 375 | *(CK_BBOOL *)(pTemplate[i].pValue) = true; 376 | } 377 | break; 378 | 379 | case CKA_ID: 380 | config_.debug_log(" Attribute %d: ID", i); 381 | // public key and private key can have the same ID according to spec 382 | pTemplate[i].ulValueLen = 4; // ID 383 | if (pTemplate[i].pValue) { 384 | memcpy(pTemplate[i].pValue, object_id, 4); 385 | } 386 | break; 387 | 388 | case CKA_MODULUS: 389 | config_.debug_log(" Attribute %d: Modulus size %d", 390 | i, key.modulus.size()); 391 | pTemplate[i].ulValueLen = key.modulus.size(); 392 | if (pTemplate[i].pValue) { 393 | memcpy(pTemplate[i].pValue, key.modulus.data(), key.modulus.size()); 394 | } 395 | break; 396 | 397 | case CKA_MODULUS_BITS: 398 | config_.debug_log(" Attribute %d: Modulus bits (unsupported) %d", 399 | i, key.modulus.size()); 400 | pTemplate[i].ulValueLen = sizeof(CK_ULONG); 401 | if (pTemplate[i].pValue) { 402 | *(CK_ULONG *)(pTemplate[i].pValue) = 2048UL; 403 | } 404 | break; 405 | 406 | case CKA_PUBLIC_EXPONENT: 407 | config_.debug_log(" Attribute %d: Exponent size %d", 408 | i, key.exponent.size()); 409 | pTemplate[i].ulValueLen = key.exponent.size(); 410 | if (pTemplate[i].pValue) { 411 | memcpy(pTemplate[i].pValue, key.exponent.data(), key.exponent.size()); 412 | } 413 | break; 414 | 415 | case CKA_SUBJECT: 416 | config_.debug_log(" Attribute %d: Subject (unsupported)", i); 417 | pTemplate[i].ulValueLen = 0; 418 | break; 419 | 420 | case CKA_VALUE: 421 | config_.debug_log(" Attribute %d: Value (unsupported)", i); 422 | pTemplate[i].ulValueLen = 0; 423 | break; 424 | 425 | case CKA_LABEL: 426 | config_.debug_log(" Attribute %d: Label (unsupported)", i); 427 | if (hObject == 1) { 428 | // public key 429 | if (pTemplate[i].pValue) { 430 | memcpy(pTemplate[i].pValue, public_key_label, strlen(public_key_label)); 431 | } 432 | pTemplate[i].ulValueLen = strlen(public_key_label); 433 | } else if (hObject == 2) { 434 | // private key 435 | if (pTemplate[i].pValue) { 436 | memcpy(pTemplate[i].pValue, private_key_label, strlen(private_key_label)); 437 | } 438 | pTemplate[i].ulValueLen = strlen(private_key_label); 439 | } else { 440 | // should not happen 441 | if (pTemplate[i].pValue) { 442 | memcpy(pTemplate[i].pValue, unknown_key_label, strlen(unknown_key_label)); 443 | } 444 | pTemplate[i].ulValueLen = strlen(unknown_key_label); 445 | } 446 | break; 447 | 448 | case CKA_START_DATE: 449 | case CKA_END_DATE: 450 | config_.debug_log(" Attribute %d: Start or End Date (unsupported)", i); 451 | pTemplate[i].ulValueLen = 0; 452 | break; 453 | 454 | case 0x202: // CKA_ALWAYS_AUTHENTICATE: 455 | config_.debug_log(" Attribute %d: Always authenticate (unsupported)", i); 456 | pTemplate[i].ulValueLen = 0; 457 | break; 458 | 459 | default: 460 | // if (hObject == 1) { 461 | // // public key 462 | // if (getPublicKeyAttribute(config_, &pTemplate[i])) { 463 | // continue; 464 | // } 465 | // } else if (hObject == 2) { 466 | // // private key 467 | // if (getPrivateKeyAttribute(config_, &pTemplate[i])) { 468 | // continue; 469 | // } 470 | // } else {} 471 | // Some libraries would like to query public key attributes on private keys (or vice versa) 472 | // This provide some sane defaults 473 | if (getPublicKeyAttribute(config_, &pTemplate[i]) || 474 | getPrivateKeyAttribute(config_, &pTemplate[i])) { 475 | continue; 476 | } 477 | config_.debug_log(" Attribute %d: Unknown (%d)", i, pTemplate[i].type); 478 | pTemplate[i].ulValueLen = 0; 479 | std::stringstream ss; 480 | ss << stpm::xctime() 481 | << " unknown attribute: " 482 | << pTemplate[i].type; 483 | stpm::do_log(config_.logfile_.get(), ss.str()); 484 | } 485 | } 486 | } 487 | 488 | void 489 | Session::SignInit(CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) 490 | { 491 | } 492 | 493 | void 494 | Session::Sign(CK_BYTE_PTR pData, CK_ULONG usDataLen, 495 | CK_BYTE_PTR pSignature, CK_ULONG_PTR pusSignatureLen) 496 | { 497 | if (pSignature == nullptr) { 498 | // when pSignature is NULL, we should tell caller the size of the buffer needed 499 | // according to the call convention mentioned in the spec 500 | *pusSignatureLen = 256; // signature size is 256 bytes 501 | return; 502 | } 503 | std::string kfs; 504 | try { 505 | kfs = stpm::slurp_file(config_.keyfile_); 506 | } catch (...) { 507 | throw PK11Error(CKR_GENERAL_ERROR, 508 | "Failed to read key file '" + config_.keyfile_ + "'"); 509 | } 510 | const stpm::Key key = stpm::parse_keyfile(kfs); 511 | const std::string data{pData, pData+usDataLen}; 512 | const std::string signature{ 513 | stpm::sign(key, data, 514 | config_.set_srk_pin_ ? &config_.srk_pin_ : NULL, 515 | config_.set_key_pin_ ? &config_.key_pin_ : NULL)}; 516 | *pusSignatureLen = signature.size(); 517 | memcpy(pSignature, signature.data(), signature.size()); 518 | 519 | std::stringstream ss; 520 | ss << stpm::xctime() 521 | << " signing " << data.size() << " bytes."; 522 | stpm::do_log(config_.logfile_.get(), ss.str()); 523 | config_.debug_log("signing %s (len %d), output %d bytes", 524 | stpm::to_hex(data).c_str(), 525 | data.size(), 526 | *pusSignatureLen); 527 | } 528 | /* ---- Emacs Variables ---- 529 | * Local Variables: 530 | * c-basic-offset: 2 531 | * indent-tabs-mode: nil 532 | * End: 533 | */ 534 | -------------------------------------------------------------------------------- /src/common.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include"config.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include"openssl/err.h" 35 | #include"openssl/ossl_typ.h" 36 | #include"openssl/rand.h" 37 | #include"openssl/rsa.h" 38 | #include"openssl/x509.h" 39 | 40 | #include"tss/tspi.h" 41 | #include"trousers/trousers.h" 42 | 43 | #include"common.h" 44 | #include"tspiwrap.h" 45 | #include"internal.h" 46 | 47 | namespace { 48 | 49 | // In OpenSSL 1.1 these functions replaced direct access to internals 50 | // of the `RSA` struct. 51 | // This is local implementations so that we can still build under 52 | // OpenSSL 1.0. 53 | #ifndef HAVE_RSA_SET0_KEY 54 | int 55 | RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d) 56 | { 57 | rsa->n = n; 58 | rsa->e = e; 59 | rsa->d = d; 60 | return 1; 61 | } 62 | #endif 63 | #ifndef HAVE_RSA_GET0_KEY 64 | void 65 | RSA_get0_key(RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) 66 | { 67 | if (n) { 68 | *n = rsa->n; 69 | } 70 | if (e) { 71 | *e = rsa->e; 72 | } 73 | if (d) { 74 | *d = rsa->d; 75 | } 76 | } 77 | #endif 78 | #ifndef HAVE_RSA_GET0_FACTORS 79 | void 80 | RSA_get0_factors(const RSA *rsa, const BIGNUM **p, const BIGNUM **q) 81 | { 82 | if (p != NULL) { 83 | *p = rsa->p; 84 | } 85 | if (q != NULL) { 86 | *q = rsa->q; 87 | } 88 | } 89 | #endif 90 | } 91 | 92 | 93 | std::ostream& 94 | operator<<(std::ostream& o, const struct stpm::Key& key) 95 | { 96 | o << "mod=" << stpm::to_hex(key.modulus) 97 | << ",exp=" << stpm::to_hex(key.exponent) 98 | << ",blob=" << stpm::to_hex(key.blob); 99 | return o; 100 | } 101 | 102 | std::ostream& 103 | operator<<(std::ostream& o, const struct stpm::SoftwareKey& key) 104 | { 105 | o << "mod=" << stpm::to_hex(key.modulus) 106 | << ",exp=" << stpm::to_hex(key.exponent) 107 | << ",key=" << stpm::to_hex(key.key); 108 | return o; 109 | } 110 | 111 | BEGIN_NAMESPACE(stpm); 112 | const std::string random_device = "/dev/urandom"; 113 | const int num_random_bytes = 10240; // 10*8192 bits. 114 | const char* env_log_stderr = "SIMPLE_TPM_PK11_LOG_STDERR"; 115 | const TSS_UUID srk_uuid = TSS_UUID_SRK; 116 | 117 | std::string 118 | xgetpass(const std::string& prompt) 119 | { 120 | const int fd = STDIN_FILENO; 121 | std::cerr << prompt << ": " << std::flush; 122 | std::string line; 123 | if (!isatty(fd)) { 124 | getline(std::cin, line); 125 | } else { 126 | struct termios old; 127 | if (tcgetattr(fd, &old)) { 128 | throw std::runtime_error(std::string("tcgetattr(stdin): ") + strerror(errno)); 129 | } 130 | 131 | struct termios ti = old; 132 | ti.c_lflag &= ~ECHO; 133 | if (tcsetattr(fd, TCSAFLUSH, &ti)) { 134 | throw std::runtime_error(std::string("tcsetattr(stdin, TCSAFLUSH, no echo): ") + strerror(errno)); 135 | } 136 | getline(std::cin, line); 137 | if (tcsetattr(fd, TCSAFLUSH, &old)) { 138 | throw std::runtime_error(std::string("tcsetattr(stdin, TCSAFLUSH, old): ") + strerror(errno)); 139 | } 140 | } 141 | std::cerr << std::endl; 142 | return line; 143 | } 144 | 145 | // Wrap Tspi_* calls, checking return value and throwing exception. 146 | // TODO: Adding debug logging. 147 | TSS_RESULT 148 | tscall(const std::string& name, std::function func) 149 | { 150 | TSS_RESULT res; 151 | if (TSS_SUCCESS != (res = func())) { 152 | throw TSPIException(name, res); 153 | } 154 | return res; 155 | } 156 | 157 | TSPIException::TSPIException(const std::string& func, int code) 158 | :std::runtime_error(func + ": " + code_to_string(code)), 159 | tspi_error(code), 160 | extra_(code_to_extra(code)) 161 | { } 162 | 163 | // Turn trousers error code into useful string. 164 | std::string 165 | TSPIException::code_to_string(int code) 166 | { 167 | const std::string layer{Trspi_Error_Layer(code)}; 168 | const std::string err{Trspi_Error_String(code)}; 169 | 170 | std::stringstream ss; 171 | ss << "Code=0x" 172 | << std::setw(8) << std::setbase(16) << std::setfill('0') << code 173 | << ": " << layer 174 | << ": " << err; 175 | return ss.str(); 176 | } 177 | 178 | std::string 179 | TSPIException::code_to_extra(int code) 180 | { 181 | switch (code) { 182 | case TPM_E_INVALID_KEYHANDLE: 183 | return "Likely problem:\n" 184 | " If this happened while trying to read the public SRK, then your TPM is not\n" 185 | " configured to allow that. If it happens on any other key then it's probably\n" 186 | " a bug in simple-tpm-pk11.\n" 187 | "Possible solution:\n" 188 | " Allow reading public SRK with tpm_restrictsrk -a."; 189 | case TPM_E_AUTHFAIL: 190 | return "Likely problem:\n" 191 | " Either the SRK password or the key password is incorrect.\n" 192 | " The Well Known Secret (20 nulls unhashed) is not the same as the password \"\".\n" 193 | "Possible solution:\n" 194 | " The SRK password can (and arguable should) be set to the Well Known Secret using:\n" 195 | " tpm_changeownerauth -s -r\n" 196 | " Alternatively the SRK password can be given with -s to stpm-keygen/stpm-sign and\n" 197 | " with srk_pin in the configuration file for the PKCS#11 module."; 198 | case TSS_LAYER_TSP | TSS_E_COMM_FAILURE: 199 | return "Likely problem:\n" 200 | " The tcsd daemon is not running and listening on TCP port 30003, or there\n" 201 | " is a firewall preventing connections to it.\n" 202 | "Possible solution:\n" 203 | " Make sure trousers is started (/etc/init.d/trousers start) correctly, and\n" 204 | " and check any logs for why it might not be coming up correctly.\n" 205 | " It could fail to start because it's not finding a device /dev/tpm*."; 206 | case TSS_E_PS_KEY_NOTFOUND: 207 | return "Likely problem:\n" 208 | " The TPM chip is not active. Use tpm_getpubek to see if its error message\n" 209 | " confirms this.\n" 210 | "Possible solution:\n" 211 | " Power off the machine, power it back on, go into BIOS, and make sure the\n" 212 | " TPM chip / security chip is \"Active\"."; 213 | } 214 | return ""; 215 | } 216 | 217 | std::string 218 | xrandom(int bytes) 219 | { 220 | std::vector buf(bytes); 221 | std::ifstream f; 222 | f.rdbuf()->pubsetbuf(nullptr, 0); 223 | f.open(random_device, std::ios::binary); 224 | if (!f.good()) { 225 | throw std::runtime_error("Failed to open " + random_device); 226 | } 227 | f.read(&buf[0], buf.size()); 228 | if (f.fail() || f.eof()) { 229 | throw std::runtime_error("EOF in " + random_device); 230 | } 231 | if (static_cast(f.gcount()) != buf.size()) { 232 | throw std::runtime_error("Short full read from " + random_device); 233 | } 234 | return std::string(buf.begin(), buf.end()); 235 | } 236 | 237 | std::string 238 | bn2string(const BIGNUM* bn) 239 | { 240 | std::vector buf(BN_num_bytes(bn)); 241 | unsigned int size; 242 | if (0 >= (size = BN_bn2bin(bn, &buf[0]))) { 243 | throw std::runtime_error("Broken BIGNUM sent to BN_bn2bin."); 244 | } 245 | return std::string(buf.begin(), buf.end()); 246 | } 247 | 248 | BIGNUM* 249 | string2bn(const std::string& s) 250 | { 251 | BIGNUMWrap ret; 252 | if (!BN_bin2bn(reinterpret_cast(s.data()), s.size(), ret.get())) { 253 | throw std::runtime_error("Broken BIGNUM sent to BN_bin2bn."); 254 | } 255 | return ret.release(); 256 | } 257 | 258 | std::string 259 | xctime() 260 | { 261 | time_t t; 262 | time(&t); 263 | char buf[128] = {0}; 264 | ctime_r(&t, buf); 265 | while (strlen(buf) && buf[strlen(buf)-1] == '\n') { 266 | buf[strlen(buf)-1] = 0; 267 | } 268 | return buf; 269 | } 270 | 271 | std::string 272 | xsprintf(const char* fmt, ...) { 273 | va_list args; 274 | va_start(args, fmt); 275 | 276 | va_list va2; 277 | va_copy(va2, args); 278 | 279 | size_t s = vsnprintf(NULL, 0, fmt, args) + 1; 280 | va_end(args); 281 | 282 | std::vector buf(s); 283 | vsnprintf(&buf[0], s, fmt, va2); 284 | va_end(va2); 285 | 286 | return std::string(buf.begin(), buf.end()-1); 287 | } 288 | 289 | bool 290 | log_stderr() 291 | { 292 | const char *doit{getenv(env_log_stderr)}; 293 | return !!doit; 294 | } 295 | 296 | void 297 | do_log(std::ostream* o, const std::string& msg) 298 | { 299 | *o << msg << std::endl; 300 | if (log_stderr()) { 301 | std::cerr << msg << std::endl; 302 | } 303 | } 304 | 305 | int 306 | keysize_flag(int bits) { 307 | switch (bits) { 308 | case 512: 309 | return TSS_KEY_SIZE_512; 310 | case 1024: 311 | return TSS_KEY_SIZE_1024; 312 | case 2048: 313 | return TSS_KEY_SIZE_2048; 314 | case 4096: 315 | return TSS_KEY_SIZE_4096; 316 | case 8192: 317 | return TSS_KEY_SIZE_8192; 318 | case 16384: 319 | return TSS_KEY_SIZE_16384; 320 | } 321 | throw std::runtime_error("Unknown key size: " + std::to_string(bits) + "bit"); 322 | } 323 | 324 | SoftwareKey 325 | generate_software_key(int bits) 326 | { 327 | const std::string entropy = xrandom(num_random_bytes); 328 | RAND_seed(entropy.data(), entropy.size()); 329 | if (!RAND_status()) { 330 | throw std::runtime_error("OpenSSL PRNG wants more entropy"); 331 | } 332 | 333 | RSAWrap rsa; 334 | BIGNUMWrap f4; 335 | BN_set_word(f4.get(), RSA_F4); 336 | if (!RSA_generate_key_ex(rsa.get(), bits, f4.get(), NULL)) { 337 | throw std::runtime_error("RSA_generate_key_ex failed"); 338 | } 339 | SoftwareKey swkey; 340 | BIGNUM *m, *e; 341 | RSA_get0_key(rsa.get(), const_cast(&m), const_cast(&e), NULL); 342 | swkey.modulus = bn2string(m); 343 | swkey.exponent = bn2string(e); 344 | BIGNUM *p; 345 | RSA_get0_factors(rsa.get(), const_cast(&p), NULL); 346 | swkey.key = bn2string(p); 347 | return swkey; 348 | } 349 | 350 | Key 351 | wrap_key(const std::string* srk_pin, const std::string* key_pin, 352 | const SoftwareKey& swkey) 353 | { 354 | TPMStuff stuff{srk_pin}; 355 | 356 | // === Set up key object === 357 | int init_flags = 358 | TSS_KEY_TYPE_SIGNING 359 | | keysize_flag(swkey.modulus.size() * 8) 360 | | TSS_KEY_VOLATILE 361 | | TSS_KEY_NO_AUTHORIZATION 362 | | TSS_KEY_MIGRATABLE; // Wrapped keys must be migratable. :-( 363 | 364 | TSS_HKEY key; 365 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), 366 | TSS_OBJECT_TYPE_RSAKEY, init_flags, &key); 367 | TSS_HPOLICY key_policy; 368 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), 369 | TSS_OBJECT_TYPE_POLICY, TSS_POLICY_MIGRATION, &key_policy); 370 | 371 | // Set PIN. 372 | set_policy_secret(key_policy, key_pin); 373 | TSCALL(Tspi_Policy_AssignToObject, key_policy, key); 374 | 375 | // Load SRK public key. 376 | { 377 | UINT32 pubKeySize; 378 | BYTE *pubKey = nullptr; 379 | TSCALL(Tspi_Key_GetPubKey, stuff.srk(), &pubKeySize, &pubKey); 380 | Tspi_Context_FreeMemory(stuff.ctx(), pubKey); 381 | } 382 | 383 | // Need to set DER mode for signing. 384 | TSCALL(Tspi_SetAttribUint32, key, 385 | TSS_TSPATTRIB_KEY_INFO, 386 | TSS_TSPATTRIB_KEYINFO_SIGSCHEME, 387 | TSS_SS_RSASSAPKCS1V15_DER); 388 | 389 | // Set private key. 390 | TSCALL(Tspi_SetAttribData, key, TSS_TSPATTRIB_KEY_BLOB, 391 | TSS_TSPATTRIB_KEYBLOB_PRIVATE_KEY, 392 | swkey.key.size(), (BYTE*)swkey.key.data()); 393 | 394 | // Set modulus. 395 | TSCALL(Tspi_SetAttribData, key, 396 | TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, 397 | swkey.modulus.size(), (BYTE*)swkey.modulus.data()); 398 | 399 | // Wrap key. 400 | TSCALL(Tspi_Key_WrapKey, key, stuff.srk(), 0); 401 | 402 | Key ret; 403 | ret.modulus = swkey.modulus; 404 | ret.exponent = swkey.exponent; 405 | 406 | // Get keyblob. 407 | UINT32 blob_size; 408 | BYTE* blob_blob; 409 | TSCALL(Tspi_GetAttribData, key, 410 | TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB, 411 | &blob_size, &blob_blob); 412 | ret.blob = std::string(blob_blob, blob_blob+blob_size); 413 | return ret; 414 | } 415 | 416 | Key 417 | generate_key(const std::string* srk_pin, const std::string* key_pin, int bits) { 418 | TPMStuff stuff{srk_pin}; 419 | 420 | { // Get some random data and seed the TPM with it. 421 | const int chunk = 32; 422 | for (int left = num_random_bytes; left > 0; left -= chunk) { 423 | const std::string entropy = xrandom(chunk); 424 | TSCALL(Tspi_TPM_StirRandom, stuff.tpm(), 425 | entropy.size(), (BYTE*)entropy.data()); 426 | } 427 | } 428 | 429 | // === Set up key object === 430 | int init_flags = 431 | TSS_KEY_TYPE_SIGNING 432 | | keysize_flag(bits) 433 | | TSS_KEY_VOLATILE 434 | | TSS_KEY_NOT_MIGRATABLE; 435 | 436 | if (key_pin) { 437 | init_flags |= TSS_KEY_AUTHORIZATION; 438 | } else { 439 | init_flags |= TSS_KEY_NO_AUTHORIZATION; 440 | } 441 | 442 | TSS_HKEY key; 443 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), 444 | TSS_OBJECT_TYPE_RSAKEY, init_flags, &key); 445 | TSS_HPOLICY key_policy; 446 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), 447 | TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &key_policy); 448 | 449 | set_policy_secret(key_policy, key_pin); 450 | TSCALL(Tspi_Policy_AssignToObject, key_policy, key); 451 | 452 | // Need to set DER mode for signing. 453 | TSCALL(Tspi_SetAttribUint32, key, 454 | TSS_TSPATTRIB_KEY_INFO, 455 | TSS_TSPATTRIB_KEYINFO_SIGSCHEME, 456 | TSS_SS_RSASSAPKCS1V15_DER); 457 | 458 | // === Create Key === 459 | TSCALL(Tspi_Key_CreateKey, key, stuff.srk(), 0); 460 | 461 | Key ret; 462 | // Get modulus. 463 | UINT32 mod_size; 464 | BYTE* mod_blob; 465 | TSCALL(Tspi_GetAttribData, key, 466 | TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, 467 | &mod_size, &mod_blob); 468 | std::clog << "Modulus size: " << mod_size << std::endl; 469 | ret.modulus = std::string(std::string(mod_blob, mod_blob+mod_size)); 470 | 471 | // Print the public key. 472 | // We extract the modulus and exponent separately instead for now. 473 | if (false) { 474 | TSCALL(Tspi_Key_LoadKey, key, stuff.srk()); 475 | 476 | UINT32 pub_size; 477 | BYTE* pub; 478 | TSCALL(Tspi_Key_GetPubKey, key, &pub_size, &pub); 479 | std::clog << "Pub: " << to_hex(std::string((char*)pub, pub_size)) << std::endl; 480 | } 481 | 482 | // Get exponent. 483 | UINT32 exp_size; 484 | BYTE* exp_blob; 485 | TSCALL(Tspi_GetAttribData, key, 486 | TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT, 487 | &exp_size, &exp_blob); 488 | std::clog << "Exponent size: " << exp_size << std::endl; 489 | ret.exponent = std::string{std::string(exp_blob, exp_blob+exp_size)}; 490 | 491 | // Get keysize. 492 | UINT32 size; 493 | TSCALL(Tspi_GetAttribUint32, key, 494 | TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_KEYSIZE, 495 | &size); 496 | std::clog << "Size: " << size << std::endl; 497 | if ((UINT32)bits != size) { 498 | throw std::runtime_error("Asked for " + std::to_string(bits) + " bit key," 499 | " but got " + std::to_string(size) + " bit key,"); 500 | } 501 | 502 | // Get keyblob. 503 | UINT32 blob_size; 504 | BYTE* blob_blob; 505 | TSCALL(Tspi_GetAttribData, key, 506 | TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB, 507 | &blob_size, &blob_blob); 508 | std::clog << "Blob size: " << blob_size << std::endl; 509 | ret.blob = std::string{std::string(blob_blob, blob_blob+blob_size)}; 510 | return ret; 511 | } 512 | 513 | std::string 514 | xbasename(const std::string& fullpath) 515 | { 516 | const size_t s = fullpath.size(); 517 | std::vector buf(s + 1); 518 | memcpy(&buf[0], fullpath.data(), s); 519 | const std::string ret{basename(&buf[0])}; 520 | return ret; 521 | } 522 | 523 | std::string 524 | xgethostname() 525 | { 526 | std::vector buf(1024); 527 | if (gethostname(&buf[0], buf.size() - 1)) { 528 | throw std::runtime_error(std::string("gethostbyname(): ") + strerror(errno)); 529 | } 530 | return &buf[0]; 531 | } 532 | 533 | std::string 534 | to_hex(const std::string& s) 535 | { 536 | std::stringstream ss; 537 | for (auto c : s) { 538 | ss << std::setw(2) << std::setfill('0') << std::setbase(16) 539 | << unsigned(c & 0xff); 540 | } 541 | return ss.str(); 542 | } 543 | 544 | std::string 545 | to_bin(const std::string& s) 546 | { 547 | std::map m; 548 | for (int c = 0; c < 256; c++) { 549 | unsigned char t[2] = {(unsigned char)c, 0}; 550 | m[to_hex((char*)t)] = c; 551 | } 552 | 553 | if (s.size() & 1) { 554 | throw std::runtime_error("to_bin() on odd length string"); 555 | } 556 | std::string ret; 557 | for (unsigned c = 0; c < s.size(); c+=2) { 558 | auto t = s.substr(c, 2); 559 | ret += m[t]; 560 | } 561 | return ret; 562 | } 563 | 564 | Key 565 | parse_keyfile(const std::string& s) 566 | { 567 | std::istringstream ss(s); 568 | Key key; 569 | int linenum = 0; 570 | while (!ss.eof()) { 571 | std::string line; 572 | getline(ss, line); 573 | linenum++; 574 | if (line.empty() || line[0] == '#') { 575 | continue; 576 | } 577 | 578 | std::istringstream linetokens{line}; 579 | std::string cmd, rest; 580 | getline(linetokens, cmd, ' '); 581 | getline(linetokens, rest); 582 | if (cmd == "mod") { 583 | key.modulus = to_bin(rest); 584 | } else if (cmd == "blob") { 585 | key.blob = to_bin(rest); 586 | } else if (cmd == "exp") { 587 | key.exponent = to_bin(rest); 588 | } else { 589 | throw std::runtime_error("Keyfile format error(line " 590 | + std::to_string(linenum) + ": " + line + ")"); 591 | } 592 | } 593 | if (key.modulus.empty() || key.blob.empty() || key.exponent.empty()) { 594 | throw std::runtime_error("Keyfile incomplete. Needs modulus, exponent and blob."); 595 | } 596 | return key; 597 | } 598 | 599 | bool 600 | auth_required(const std::string* srk_pin, const Key& key) 601 | { 602 | TPMStuff stuff{srk_pin}; 603 | 604 | int init_flags = 605 | TSS_KEY_TYPE_SIGNING 606 | | TSS_KEY_VOLATILE 607 | | TSS_KEY_NO_AUTHORIZATION 608 | | TSS_KEY_NOT_MIGRATABLE; 609 | 610 | TSS_HKEY hkey; 611 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, 612 | init_flags, &hkey); 613 | TSCALL(Tspi_Context_LoadKeyByBlob, stuff.ctx(), stuff.srk(), 614 | key.blob.size(), (BYTE*)key.blob.data(), &hkey); 615 | 616 | UINT32 auth; 617 | // TODO: AUTHUSAGE or AUTHDATAUSAGE? 618 | TSCALL(Tspi_GetAttribUint32, hkey, 619 | TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_AUTHDATAUSAGE, 620 | &auth); 621 | return !!auth; 622 | } 623 | 624 | std::string 625 | slurp_file(const std::string& fn) 626 | { 627 | std::ifstream f(fn); 628 | if (!f) { 629 | throw std::runtime_error("Can't open file '" + fn + "'"); 630 | } 631 | return std::string{std::istreambuf_iterator(f), 632 | std::istreambuf_iterator()}; 633 | } 634 | 635 | // Set password/PIN on a policy. If nullptr pin is given, use the Well Known Secret. 636 | void 637 | set_policy_secret(TSS_HPOLICY policy, const std::string* pin) 638 | { 639 | if (pin) { 640 | TSCALL(Tspi_Policy_SetSecret, policy, 641 | TSS_SECRET_MODE_PLAIN, 642 | pin->size(), 643 | (BYTE*)pin->data()); 644 | } else { 645 | BYTE wks[] = TSS_WELL_KNOWN_SECRET; 646 | int wks_size = sizeof(wks); 647 | TSCALL(Tspi_Policy_SetSecret, policy, 648 | TSS_SECRET_MODE_SHA1, wks_size, wks); 649 | } 650 | } 651 | 652 | /** 653 | * https://www.cylab.cmu.edu/tiw/slides/challener-TPM.pdf 654 | * TODO: this doesn't work yet. 655 | */ 656 | SoftwareKey 657 | exfiltrate_key(const Key& key, 658 | const std::string* srk_pin, 659 | const std::string& owner_password, 660 | const std::string* key_pin) 661 | { 662 | TPMStuff stuff{srk_pin}; 663 | 664 | // === Load key === 665 | int init_flags = 666 | TSS_KEY_TYPE_SIGNING 667 | | TSS_KEY_VOLATILE 668 | | TSS_KEY_NO_AUTHORIZATION 669 | | TSS_KEY_MIGRATABLE; 670 | TSS_HKEY sign; 671 | TSS_HPOLICY policy_sign; 672 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, 673 | init_flags, &sign); 674 | TSCALL(Tspi_Context_LoadKeyByBlob, stuff.ctx(), stuff.srk(), 675 | key.blob.size(), (BYTE*)key.blob.data(), &sign); 676 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), 677 | TSS_OBJECT_TYPE_POLICY, TSS_POLICY_MIGRATION, 678 | &policy_sign); 679 | set_policy_secret(policy_sign, key_pin); 680 | TSCALL(Tspi_Policy_AssignToObject, policy_sign, sign); 681 | 682 | // Set owner password. 683 | { 684 | TSS_HPOLICY policy_tpm; 685 | TSCALL(Tspi_GetPolicyObject, stuff.tpm(), TSS_POLICY_USAGE, &policy_tpm); 686 | set_policy_secret(policy_tpm, &owner_password); 687 | } 688 | 689 | // Generate migration ticket. 690 | BYTE* ticket; 691 | UINT32 ticket_size; 692 | TSCALL(Tspi_TPM_AuthorizeMigrationTicket, 693 | stuff.tpm(), 694 | stuff.srk(), // TODO: change to target key. 695 | TSS_MS_REWRAP, 696 | &ticket_size, &ticket); 697 | 698 | // Create migration blob. 699 | BYTE* rnd; 700 | UINT32 rnd_size; 701 | BYTE* migrblob; 702 | UINT32 migrblob_size; 703 | TSCALL(Tspi_Key_CreateMigrationBlob, 704 | sign, // Key to migrate. 705 | stuff.srk(), // Parent key. 706 | ticket_size, ticket, // Migration ticket. 707 | &rnd_size, &rnd, // Random data. 708 | &migrblob_size, &migrblob); // Migration data blob. 709 | 710 | // TODO: Decrypt migration blob. 711 | return SoftwareKey(); 712 | } 713 | 714 | std::string 715 | sign(const Key& key, const std::string& data, 716 | const std::string* srk_pin, 717 | const std::string* key_pin) 718 | { 719 | TPMStuff stuff{srk_pin}; 720 | 721 | // === Load key === 722 | int init_flags = 723 | TSS_KEY_TYPE_SIGNING 724 | | TSS_KEY_VOLATILE 725 | | TSS_KEY_NO_AUTHORIZATION 726 | | TSS_KEY_NOT_MIGRATABLE; 727 | TSS_HKEY sign; 728 | TSS_HPOLICY policy_sign; 729 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, 730 | init_flags, &sign); 731 | TSCALL(Tspi_Context_LoadKeyByBlob, stuff.ctx(), stuff.srk(), 732 | key.blob.size(), (BYTE*)key.blob.data(), &sign); 733 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), 734 | TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, 735 | &policy_sign); 736 | 737 | set_policy_secret(policy_sign, key_pin); 738 | TSCALL(Tspi_Policy_AssignToObject, policy_sign, sign); 739 | 740 | // === Sign === 741 | TSS_HHASH hash; 742 | UINT32 sig_size; 743 | BYTE* sig_blob; 744 | TSCALL(Tspi_Context_CreateObject, stuff.ctx(), 745 | TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &hash); 746 | TSCALL(Tspi_Hash_SetHashValue, hash, data.size(), (BYTE*)data.data()); 747 | if (false) { 748 | TSCALL(Tspi_SetAttribUint32, sign, TSS_TSPATTRIB_KEY_INFO, 749 | TSS_TSPATTRIB_KEYINFO_SIGSCHEME, 750 | TSS_SS_RSASSAPKCS1V15_DER); 751 | } 752 | TSCALL(Tspi_Hash_Sign, hash, sign, &sig_size, &sig_blob); 753 | return std::string{sig_blob, sig_blob+sig_size}; 754 | } 755 | 756 | std::string 757 | public_decrypt(const Key& key, const std::string& sig) 758 | { 759 | // Load key. 760 | RSAWrap rsa; 761 | if (!RSA_set0_key(rsa.get(), string2bn(key.modulus), string2bn(key.exponent), 762 | NULL)) { 763 | throw std::runtime_error("RSA_set0_key failed"); 764 | } 765 | 766 | // Decrypt signature. 767 | std::vector d(RSA_size(rsa.get())); 768 | const int len = RSA_public_decrypt( 769 | sig.size(), 770 | reinterpret_cast(sig.data()), 771 | &d[0], 772 | rsa.get(), 773 | RSA_PKCS1_PADDING); 774 | if (len < 0) { 775 | throw std::runtime_error(xsprintf("RSA_public_decrypt failed: %s", ERR_error_string(ERR_get_error(), NULL))); 776 | } 777 | return std::string{&d[0], &d[len]}; 778 | } 779 | 780 | bool 781 | verify(const Key& key, const std::string& data, const std::string& sig) 782 | { 783 | // TODO: Make this comparison constant time. 784 | if (data != public_decrypt(key, sig)) { 785 | return false; 786 | } 787 | return true; 788 | } 789 | END_NAMESPACE(stpm); 790 | /* ---- Emacs Variables ---- 791 | * Local Variables: 792 | * c-basic-offset: 2 793 | * indent-tabs-mode: nil 794 | * End: 795 | */ 796 | --------------------------------------------------------------------------------