pax_global_header00006660000000000000000000000064130054132160014505gustar00rootroot0000000000000052 comment=4cd96b60b702d9e74bc5e757e1cd19777f7345cc simple-tpm-pk11-0.06/000077500000000000000000000000001300541321600142735ustar00rootroot00000000000000simple-tpm-pk11-0.06/.gitignore000066400000000000000000000003301300541321600162570ustar00rootroot00000000000000*~ *.o *.lo *.la *.in Makefile aclocal.m4 autom4te.cache config.h config.log config.status configure libtool m4 doc/Makefile.in .deps .libs stamp-h1 *_test .dirstamp stpm-keygen stpm-sign stpm-exfiltrate stpm-verify simple-tpm-pk11-0.06/.travis.yml000066400000000000000000000003611300541321600164040ustar00rootroot00000000000000language: cpp compiler: - clang - gcc script: ./bootstrap.sh && ./configure && make && make check before_install: - sudo apt-get update -qq - sudo apt-get install -qq tpm-tools libtspi-dev libopencryptoki-dev libssl-dev libgtest-dev simple-tpm-pk11-0.06/FAQ000066400000000000000000000004501300541321600146240ustar00rootroot00000000000000Q: Will this work on Macs? A: No. Macs do not have TPM chips. Please complain to your Apple dealer. --- Q: Should I generate keys on the TPM chip, or import software generated keys? A: Generate on the TPM chip. See: http://blog.habets.se/2013/11/Should-I-generate-my-keys-in-software-or-hardware simple-tpm-pk11-0.06/LICENSE000066400000000000000000000010751300541321600153030ustar00rootroot00000000000000Copyright 2013 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. simple-tpm-pk11-0.06/Makefile.am000066400000000000000000000050151300541321600163300ustar00rootroot00000000000000ACLOCAL_AMFLAGS=-I m4 SUBDIRS=doc bin_PROGRAMS=stpm-keygen stpm-sign stpm-exfiltrate stpm-verify TESTS=stpm-keygen_test stpm-sign_test common_test pk11_test stpm-verify_test check_PROGRAMS=$(TESTS) common_test_SOURCES=\ src/common_test.cc \ src/common.cc \ src/tspiwrap.cc \ src/fake_tspi.cc \ src/libgtest.cc common_test_CXXFLAGS=-I/usr/src/gtest common_test_LDFLAGS= common_test_LDADD=-lpthread pk11_test_SOURCES=\ src/pk11_test.cc \ src/pk11.cc \ src/common.cc \ src/session.cc \ src/tspiwrap.cc \ src/fake_tspi.cc \ src/libgtest.cc pk11_test_CXXFLAGS=-I/usr/src/gtest pk11_test_LDFLAGS= pk11_test_LDADD=-lpthread stpm_keygen_SOURCES=\ src/keygen.cc \ src/common.cc \ src/tspiwrap.cc \ src/wrap_main.cc stpm_keygen_test_SOURCES=\ src/keygen.cc \ src/keygen_test.cc \ src/common.cc \ src/tspiwrap.cc \ src/fake_tspi.cc \ src/libgtest.cc stpm_keygen_test_CXXFLAGS=-I/usr/src/gtest stpm_keygen_test_LDFLAGS= stpm_keygen_test_LDADD=-lpthread stpm_exfiltrate_SOURCES=\ src/wrap_main.cc \ src/common.cc \ src/tspiwrap.cc \ src/exfiltrate.cc stpm_sign_SOURCES=\ src/wrap_main.cc \ src/common.cc \ src/tspiwrap.cc \ src/sign.cc stpm_sign_test_SOURCES=\ src/sign.cc \ src/sign_test.cc \ src/common.cc \ src/tspiwrap.cc \ src/fake_tspi.cc \ src/libgtest.cc stpm_sign_test_CXXFLAGS=-I/usr/src/gtest stpm_sign_test_LDFLAGS= stpm_sign_test_LDADD=-lpthread stpm_verify_SOURCES=\ src/wrap_main.cc \ src/common.cc \ src/tspiwrap.cc \ src/verify.cc stpm_verify_test_SOURCES=\ src/verify.cc \ src/verify_test.cc \ src/common.cc \ src/tspiwrap.cc \ src/fake_tspi.cc \ src/libgtest.cc stpm_verify_test_CXXFLAGS=-I/usr/src/gtest stpm_verify_test_LDFLAGS= stpm_verify_test_LDADD=-lpthread lib_LTLIBRARIES=libsimple-tpm-pk11.la # Workaround for "object `...' created both with libtool and without". libsimple_tpm_pk11_la_CXXFLAGS = $(AM_CXXFLAGS) libsimple_tpm_pk11_la_SOURCES=\ src/pk11.cc \ src/session.cc \ src/tspiwrap.cc \ src/common.cc libsimple_tpm_pk11_la_LDFLAGS=-version-info 0:0:0 if WITH_PRECOMPILED_GTEST common_test_LDADD+=-lgtest -lgtest_main pk11_test_LDADD+=-lgtest -lgtest_main stpm_keygen_test_LDADD+=-lgtest -lgtest_main stpm_sign_test_LDADD+=-lgtest -lgtest_main stpm_verify_test_LDADD+=-lgtest -lgtest_main endif check-tpm: ./testscripts/all.sh mrproper: maintainer-clean rm -f aclocal.m4 configure.scan depcomp missing install-sh config.h.in rm -fr config.guess config.sub build-stamp autom4te.cache/ rm -f Makefile.in configure autoscan*.log debian/debhelper.log rm -f debian/substvars debian/files rm -fr debian/tmp rm -fr m4 rm -f doc/Makefile.in simple-tpm-pk11-0.06/README.md000066400000000000000000000111361300541321600155540ustar00rootroot00000000000000# Simple TPM PK11 A simple library for using the TPM chip to secure SSH keys. Copyright 2013-2016 Google Inc. All Rights Reserved. Apache 2.0 license. This is NOT a Google product. Contact: thomas@habets.se / habets@google.com https://github.com/ThomasHabets/ ## Install dependencies ### Debian ```shell apt-get install tpm-tools libtspi-dev libopencryptoki-dev libssl-dev ``` ### Fedora ```shell tpm-tools opencryptoki-devel trousers-devel openssl-devel ``` ### FreeBSD ```shell pkg install tpm-tools trousers-tddl opencryptoki openssl ``` ## Build simple-tpm-pk11 ```shell ./configure && make && sudo make install ``` ## Init TPM chip 1. If you have not taken ownership, do so. ```shell tpm_takeownership -z Enter owner password: [enter something secret here] Confirm password: [enter something secret here] ``` 2. SRK password is usually the Well Known Secret (all nulls). You can specify a password but it's easier it you don't. The SRK password is only used to allow crypto operations. You still need blobs and key passwords to use other peoples keys. ```shell tpm_changeownerauth -s -r ``` If you get any error messages, see read TPM-TROUBLESHOOTING. ## User setup ### 1. Create key ``` mkdir ~/.simple-tpm-pk11/ stpm-keygen -o ~/.simple-tpm-pk11/my.key ``` (use `-p` if you want to set a password on the key) Try out the key: ``` dd if=/dev/urandom of=to-sign bs=1 count=35 stpm-sign -k ~/.simple-tpm-pk11/my.key -f to-sign stpm-sign -k ~/.simple-tpm-pk11/my.key -f to-sign -r > to-sign.sig stpm-verify -f to-sign -k ~/.simple-tpm-pk11/my.key -s to-sign.sig ``` ### 2. Create config ``` echo "key my.key" > ~/.simple-tpm-pk11/config ``` Optionally add "log foo.log" in there too. ### 3. Extract the public key in SSH format ``` ssh-keygen -D libsimple-tpm-pk11.so ``` Install it where you want to log in, in the usual authorized_keys way. Try logging in using your new fancy key: ``` ssh -I libsimple-tpm-pk11.so shell.example.com ``` ### 4. Configure SSH to always use this module Add this to `~/.ssh/config`: ``` Host * PKCS11Provider libsimple-tpm-pk11.so ``` then try: ```shell ssh shell.example.com ``` ### 4a. Alternatively, add the TPM to your `ssh-agent` This has to be the OpenSSH `ssh-agent`, since gnome-keyring doesn't support PKCS#11. A sign that you run gnome-keyring (or your OpenSSH is compiled without PKCS#11 support) is that you see this error message when you try: ``` $ ssh-add -s /…/libsimple-tpm-pk11.so Enter passphrase for PKCS#11: Could not add card "/…/libsimple-tpm-pk11.so": agent refused operation ``` ## Tested with ### Hardware * Dell Precision T3500 / WEC TPM 1.2.2.81 * HP Z440 / IFX TPM 1.2.4.40 * Lenovo T410 / STM TPM 1.2.8.16 * Lenovo T440s / STM TPM 1.2.13.12 * Lenovo T500 / INTC STM 1.2.4.1 * Lenono X200s / INTC TPM 1.2.4.1 * Lenovo X240 / STM TPM 1.2.13.12 ### Software * OpenSSH 5.9 * OpenSSH 6.0p1 on Debian 7.2 * OpenSSH 6.4p1 on CentOS 7.0 * OpenSSH 6.6.1p1 on FreeBSD 11-CURRENT * OpenSSH 6.8p1 on Arch Linux * OpenSSH 7.1p2 on Debian ## TODO * Clean up code. * config option: log to stdout and/or stderr in addition to logfile. * Install in the correct place. * Add PKCS11 support to ssh *server*. * Global config in /etc. * Optionally stir with /dev/random when generating keys. * Script to automate setting up, including verifying TPM state and fixing it. * Auto-generate keys on demand? Or should this only be part of script to set up? * Make it work with gpg, and document. * Make it work with SSH certs, and document. * Make it work with openssl, and document. * Make it work with Firefox, and document. * Make it work with Chrome, and document. * Make it work with encrypted home directories, and document. ## Reference links * http://secgroup.ext.dsi.unive.it/projects/security-apis/tookan/ * http://secgroup.ext.dsi.unive.it/projects/security-apis/cryptokix/ * http://trousers.sourceforge.net/pkcs11.html * http://www.trustedcomputinggroup.org/resources/tcg_software_stack_tss_specification * http://www.infineon.com/dgdl/TPM+Key+Backup+and+Recovery.pdf * http://www.engadget.com/2010/02/12/christopher-tarnovsky-hacks-infineons-unhackable-chip-we-pre/ * http://trousers.sourceforge.net/dev_faq.html * http://resources.infosecinstitute.com/linux-tpm-encryption-initializing-and-using-the-tpm/ * http://p11-glue.freedesktop.org/p11-kit.html * http://trousers.sourceforge.net/dev_faq.html ## Make new release * Update configure.ac with new version, commit. * git tag -a -s 0.0x * git push --tags ## Some random notes, not instructions ```shell openssl genrsa -out rsa-key 2048 openssl rsa -in rsa-key -modulus exponent is always 65537. ssh-keygen -f rsa-key -y > rsa-key.pub ``` simple-tpm-pk11-0.06/TPM-TROUBLESHOOTING000066400000000000000000000032471300541321600171510ustar00rootroot00000000000000Problem: tpm_clear --force [...] TPM is disabled Solution: Go into BIOS and enable the TPM chip. --- Problem: tpm_clear --force TPM Successfully Cleared. You need to reboot to complete this operation. After reboot the TPM will be in the default state: unowned, disabled and inactive. Solution: Reboot. --- Problem tpm_clear --force [...] Bad physical presence value Solution Reset using a cold boot and the BIOS. See below. --- Problem stpm-keygen -o my.key [...] TPM is defending against dictionary attacks and is in some time-out period Solution tpm_resetdalock --- Problem One of the solutions assumes I know the owner password, and I don't. Solution 1) Shut off the machine. Reboot will not do. Power it down. 2) Boot the machine and enter the BIOS. 3) In the BIOS, find "Clear TPM chip" and run that. 4) Boot the OS and start from scratch with tpm_takeownership. --- Problem Key not found in persistent storage. Solution Did you reboot after clearing/taking ownership? Try that first. --- Problem stpm-keygen -o my.key Tspi_Context_LoadKeyByUUID: Code=0x00002020: tcs: Key not found in persistent storage Solution Is the TPM chip active? Check with tpm_getpubek. If it says it's inactive you may need to turn it on in the BIOS. If that doesn't work, try clearing it and starting from scratch. --- Problem After re-installation of the operating system the key is not found. $ ssh server C_GetTokenInfo failed: 6 no keys Solution The base library trousers saves some part of the key on the local filesystem. https://sourceforge.net/p/trousers/mailman/message/28828649/ Copy the file /var/lib/tpm/system.data as root to your new system. simple-tpm-pk11-0.06/bootstrap.sh000077500000000000000000000000441300541321600166450ustar00rootroot00000000000000#!/bin/sh mkdir -p m4 autoreconf -i simple-tpm-pk11-0.06/configure.ac000066400000000000000000000043611300541321600165650ustar00rootroot00000000000000AC_PREREQ(2.61) AC_INIT([simple-tpm-pk11], [0.06], [thomas@habets.se]) AC_CONFIG_AUX_DIR([m4]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([foreign subdir-objects]) AM_MAINTAINER_MODE LT_INIT([dlopen]) AC_CONFIG_SRCDIR([src/common.cc]) AC_CONFIG_HEADER(config.h) AC_CHECK_HEADERS([tss/tspi.h], [], [ [echo "Cannot continue:"] [echo " libtspi headers are missing; please install the package providing tss/tspi.h,"] [echo " which is libtspi-dev for Debian derivatives."] [exit 1] ]) AC_CHECK_LIB([tspi], [Tspi_GetAttribUint32], [], [ [echo "Cannot continue:"] [echo " libtspi is missing the required function Tspi_GetAttribUint32."] [exit 1] ]) AC_CHECK_DECLS([optarg, optind, optreset], [:], [:], [ #include #include #include ]) AC_CHECK_LIB([crypto], [BN_new], [], [ [echo "Cannot continue:"] [echo " libcrypto is missing the required function BN_new."] [exit 1] ]) AC_CHECK_HEADERS([opencryptoki/pkcs11.h], [], [ [echo "Cannot continue:"] [echo " opencryptoki headers are missing; please install the package providing"] [echo " opencryptoki/pkcs11.h, which is libopencryptoki-dev for Debian derivatives."] [exit 1] ]) AC_ARG_WITH(precompiled-gtest, [ --with-precompiled-gtest Use a system-provided precompiled version of gtest], [case "${withval}" in yes | no ) WITH_PRECOMPILED_GTEST="${withval}" ;; *) AC_MSG_ERROR(bad value ${withval} for --with-precompiled-gtest) ;; esac], [WITH_PRECOMPILED_GTEST="no"] ) AM_CONDITIONAL([WITH_PRECOMPILED_GTEST], [test "x$WITH_PRECOMPILED_GTEST" = "xyes"]) AS_IF([test "x$WITH_PRECOMPILED_GTEST" = "xyes"], [ AC_DEFINE([PRECOMPILED_GTEST], [], ["build using precompiled gtest library"]) ]) AC_PROG_CXX AC_PROG_INSTALL AC_SUBST([AM_CXXFLAGS]) AC_SUBST([AM_LDFLAGS]) # Library stuff. AC_ENABLE_SHARED AC_DISABLE_STATIC LT_INIT(libtool) AC_CHECK_FUNCS([RSA_get0_key RSA_set0_key RSA_get0_factors]) CXXFLAGS="-std=gnu++0x -Wall $CXXFLAGS" AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([doc/Makefile]) AC_OUTPUT echo " $PACKAGE_NAME version $PACKAGE_VERSION Prefix.........: $prefix Debug Build....: $debug C Compiler.....: $CC $CFLAGS $CPPFLAGS C++ Compiler...: $CXX $CXXFLAGS $CPPFLAGS Linker.........: $LD $LDFLAGS $LIBS " simple-tpm-pk11-0.06/doc/000077500000000000000000000000001300541321600150405ustar00rootroot00000000000000simple-tpm-pk11-0.06/doc/Makefile.am000066400000000000000000000007101300541321600170720ustar00rootroot00000000000000AUTOMAKE_OPTIONS=foreign DISTCLEANFILES=*~ man_MANS=stpm-keygen.1 stpm-sign.1 stpm-verify.1 stpm-exfiltrate.1 simple-tpm-pk11.7 EXTRA_DIST=$(man_MANS) # Generated manpage files are checked in, and are normally not built. # That's why there's nothing on the right hand side of this rule. # To re-generate, delete the target files and run 'make'. %.1 %.7: yodl2man -o $@.tmp $@.yodl perl -ne 's/(^|(?<=[^\\\w]))-/\\-/g;print' < $@.tmp > $@ rm -f $@.tmp simple-tpm-pk11-0.06/doc/simple-tpm-pk11.7000066400000000000000000000042741300541321600200000ustar00rootroot00000000000000.TH "simple\-tpm\-pk11" "7" "1th December, 2013" "simple\-tpm\-pk11" "" .SH "NAME" simple\-tpm\-pk11 \- Simple PKCS11 provider for TPM chips .PP .SH "DESCRIPTION" \fIsimple\-tpm\-pk11\fP Is a PKCS11 provider for TPM chips\&. Its primary purpose is to protect SSH client keys so that they can\(cq\&t be copied or stolen if the machine they\(cq\&re on gets compromised\&. .PP .SH "OPTIONS" Since PKCS11 modules are \&.so files loaded by other binaries, they don\(cq\&t take command line options\&. Instead \fIsimple\-tpm\-pk11\fP options can be set up environment variables\&. .IP "\fBSIMPLE_TPM_PK11_DEBUG\fP" If set, enables debug level logging\&. .IP "\fBSIMPLE_TPM_PK11_CONFIG\fP=/path/to/config" Override default config location\&. Default is ~/\&.simple\-tpm\-pk11/config\&. .IP "\fBSIMPLE_TPM_PK11_LOG_STDERR\fP" If set, copies all log output to STDERR\&. .PP .SH "CONFIGURATION FILE" Configuration options are of the key/value variety, with comments lines starting with \(dq\&#\(dq\&\&. .IP "key \fIkey file\fP" Full path to key file, or relative to ~/\&.simple\-tpm\-pk11\&. This the only required configuration option\&. .IP "debug" Enable debug level logging\&. .IP "srk_pin \fIPIN\fP" Set SRK PIN\&. Default is the Well Known Secret (20 nulls)\&. .IP "key_pin \fIPIN\fP" Set key PIN\&. .IP "log \fIlog file\fP" Full path to log file, or relative to ~/\&.simple\-tpm\-pk11\&. .PP .SH "EXAMPLES" .nf .sp # Load key from ~/\&.simple\-tpm\-pk11/my\&.key\&. key my\&.key .PP # Load key from /keys/foo/my\&.key, and the empty string as SRK PIN\&. key /keys/foo/my\&.key srk_pin .fi .in .PP .SH "TPM\-TROUBLESHOOTING" TODO\&. .PP .SH "DIAGNOSTICS" Most errors will probably be related to interacting with the TPM chip\&. Resetting the TPM chip and taking ownership should take care of most of them\&. See the \fITPM\-TROUBLESHOOTING\fP section\&. .PP .SH "BUGS" The password is read from stdin without turning off echo\&. It should be read from the terminal without echo\&. .PP .SH "SEE ALSO" \fBstpm\-keygen(1)\fP, \fBstpm\-sign(1)\fP .PP .SH "AUTHOR" Simple\-TPM\-PK11 was written By Thomas Habets / \&. .PP git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git simple-tpm-pk11-0.06/doc/simple-tpm-pk11.7.yodl000066400000000000000000000044471300541321600207500ustar00rootroot00000000000000manpage(simple-tpm-pk11)(7)(1th December, 2013)(simple-tpm-pk11)() manpagename(simple-tpm-pk11)(Simple PKCS11 provider for TPM chips) manpagedescription() em(simple-tpm-pk11) Is a PKCS11 provider for TPM chips. Its primary purpose is to protect SSH client keys so that they can't be copied or stolen if the machine they're on gets compromised. manpageoptions() Since PKCS11 modules are .so files loaded by other binaries, they don't take command line options. Instead em(simple-tpm-pk11) options can be set up environment variables. startdit() dit(bf(SIMPLE_TPM_PK11_DEBUG)) If set, enables debug level logging. dit(bf(SIMPLE_TPM_PK11_CONFIG)=/path/to/config) Override default config location. Default is ~/.simple-tpm-pk11/config. dit(bf(SIMPLE_TPM_PK11_LOG_STDERR)) If set, copies all log output to STDERR. enddit() manpagesection(CONFIGURATION FILE) Configuration options are of the key/value variety, with comments lines starting with "#". startdit() dit(key em(key file)) Full path to key file, or relative to ~/.simple-tpm-pk11. This the only required configuration option. dit(debug) Enable debug level logging. dit(srk_pin em(PIN)) Set SRK PIN. Default is the Well Known Secret (20 nulls). dit(key_pin em(PIN)) Set key PIN. dit(log em(log file)) Full path to log file, or relative to ~/.simple-tpm-pk11. enddit() manpagesection(EXAMPLES) mancommand(.nf) mancommand(.sp) # Load key from ~/.simple-tpm-pk11/my.key. key my.key # Load key from /keys/foo/my.key, and the empty string as SRK PIN. key /keys/foo/my.key srk_pin mancommand(.fi) mancommand(.in) manpagesection(TPM-TROUBLESHOOTING) TODO. manpagediagnostics() Most errors will probably be related to interacting with the TPM chip. Resetting the TPM chip and taking ownership should take care of most of them. See the em(TPM-TROUBLESHOOTING) section. manpagebugs() The password is read from stdin without turning off echo. It should be read from the terminal without echo. manpageseealso() bf(stpm-keygen(1)), bf(stpm-sign(1)) manpageauthor() Simple-TPM-PK11 was written By Thomas Habets / . git clone https://github.com/ThomasHabets/simple-tpm-pk11.git simple-tpm-pk11-0.06/doc/stpm-exfiltrate.1000066400000000000000000000036111300541321600202530ustar00rootroot00000000000000.TH "stpm\-exfiltrate" "1" "16th Febrary, 2014" "simple\-tpm\-pk11" "" .SH "NAME" stpm\-exfiltrate \- Extract key from TPM chip .PP .SH "SYNOPSIS" \fBstpm\-exfiltrate\fP [ \-hOps ] \-k \fIkey file\fP .PP .SH "DESCRIPTION" \fIstpm\-exfiltrate\fP extracts a key that is otherwise protected by the TPM chip\&. This only works if the key is \(dq\&migratable\(dq\& (meaning it was generated in software), and the TPM owner password is known\&. .PP This is why you should generate keys in hardware (the default) with stpm\-keygen and not use its \-S option\&. .PP .SH "OPTIONS" .IP "\-h" Show usage info\&. .IP "\-k \fIkey file\fP" Key blob file to read\&. .IP "\-O" Use Well Known Secret for owner password\&. Default is ask\&. .IP "\-p" Ask for key PIN / password\&. Default is Well Known Secret\&. .IP "\-o" Ask for SRK PIN / password\&. Default is Well Known Secret\&. .PP .SH "EXAMPLES" .nf .sp .PP stpm\-exfiltrate \-k ~/\&.simple\-tpm\-pk11/my\&.key Enter owner password: blah blah [ \&.\&.\&. key data here \&.\&.\&.] .PP stpm\-exfiltrate \-p \-k ~/\&.simple\-tpm\-pk11/my\&.key Enter owner password: blah blah Enter key PIN: my secret password here [ \&.\&.\&. key data here \&.\&.\&.] .PP stpm\-exfiltrate \-sp \-k ~/\&.simple\-tpm\-pk11/my\&.key Enter owner password: blah blah Enter key PIN: my secret password here Enter SRK PIN: 12345678 [ \&.\&.\&. key data here \&.\&.\&.] .fi .in .PP .SH "DIAGNOSTICS" Most errors will probably be related to interacting with the TPM chip\&. Resetting the TPM chip and taking ownership should take care of most of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of \fBsimple\-tpm\-pk11(7)\fP\&. .PP .SH "SEE ALSO" \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-sign(1)\fP, \fBstpm\-keygen\fP\&. .PP .SH "AUTHOR" Simple\-TPM\-PK11 was written By Thomas Habets / \&. .PP git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git simple-tpm-pk11-0.06/doc/stpm-exfiltrate.1.yodl000066400000000000000000000036221300541321600212230ustar00rootroot00000000000000manpage(stpm-exfiltrate)(1)(16th Febrary, 2014)(simple-tpm-pk11)() manpagename(stpm-exfiltrate)(Extract key from TPM chip) manpagesynopsis() bf(stpm-exfiltrate) [ -hOps ] -k em(key file) manpagedescription() em(stpm-exfiltrate) extracts a key that is otherwise protected by the TPM chip. This only works if the key is "migratable" (meaning it was generated in software), and the TPM owner password is known. This is why you should generate keys in hardware (the default) with stpm-keygen and not use its -S option. manpageoptions() startdit() dit(-h) Show usage info. dit(-k em(key file)) Key blob file to read. dit(-O) Use Well Known Secret for owner password. Default is ask. dit(-p) Ask for key PIN / password. Default is Well Known Secret. dit(-o) Ask for SRK PIN / password. Default is Well Known Secret. enddit() manpagesection(EXAMPLES) mancommand(.nf) mancommand(.sp) stpm-exfiltrate -k ~/.simple-tpm-pk11/my.key Enter owner password: blah blah [ ... key data here ...] stpm-exfiltrate -p -k ~/.simple-tpm-pk11/my.key Enter owner password: blah blah Enter key PIN: my secret password here [ ... key data here ...] stpm-exfiltrate -sp -k ~/.simple-tpm-pk11/my.key Enter owner password: blah blah Enter key PIN: my secret password here Enter SRK PIN: 12345678 [ ... key data here ...] mancommand(.fi) mancommand(.in) manpagediagnostics() Most errors will probably be related to interacting with the TPM chip. Resetting the TPM chip and taking ownership should take care of most of them. See the em(TPM-TROUBLESHOOTING) section of bf(simple-tpm-pk11(7)). manpageseealso() bf(simple-tpm-pk11(7)), bf(stpm-sign(1)), bf(stpm-keygen). manpageauthor() Simple-TPM-PK11 was written By Thomas Habets / . git clone https://github.com/ThomasHabets/simple-tpm-pk11.git simple-tpm-pk11-0.06/doc/stpm-keygen.1000066400000000000000000000045401300541321600173700ustar00rootroot00000000000000.TH "stpm\-keygen" "1" "1th December, 2013" "simple\-tpm\-pk11" "" .SH "NAME" stpm\-keygen \- Generate key pair for use with simple\-tpm\-pk11 .PP .SH "SYNOPSIS" \fBstpm\-keygen\fP [ \-hps ] \-o \fIoutput file\fP .PP .SH "DESCRIPTION" \fIstpm\-keygen\fP generates a 2048 RSA key inside the TPM chip, and saves the public key and the SRK\-encrypted private key (the \(dq\&blob\(dq\&) in the \fIoutput file\fP\&. .PP .SH "OPTIONS" .IP "\-h" Show usage info\&. .IP "\-o \fIoutput file\fP" Output file, where the public key and key blob will be written\&. .IP "\-p" Create the key with a PIN / password\&. The password will be prompted for inteactively\&. .IP "\-s" Ask for the SRK password interactively\&. By default the \(dq\&Well Known Secret\(dq\& (20 nulls) is used\&. The SRK password is an access token that must be presented for the TPM to perform any operation that involves the TPM, and an actual secret password is usually not required or useful\&. .IP "\-S" Generate key in software instead of hardware\&. The choice between generating the key in software and hardware is not an obvious one\&. It\(cq\&s hard to verify the quality of keys generated in hardware (e\&.g\&. bugs or backdoors), but software keys have existed in RAM at some point\&. And because software generated keys have to be generated as migratable keys, they can be extracted by someone who knows the TPM owner password\&. The recommended choice is to generate in hardware, which is also the default\&. .PP .SH "EXAMPLES" .nf .sp .PP stpm\-keygen \-o ~/\&.simple\-tpm\-pk11/my\&.key .PP stpm\-keygen \-p \-o ~/\&.simple\-tpm\-pk11/my\&.key Enter key PIN: my secret password here .PP stpm\-keygen \-sp \-o ~/\&.simple\-tpm\-pk11/my\&.key Enter SRK PIN: 12345678 Enter key PIN: my secret password here .fi .in .PP .SH "DIAGNOSTICS" Most errors will probably be related to interacting with the TPM chip\&. Resetting the TPM chip and taking ownership should take care of most of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of \fBsimple\-tpm\-pk11(7)\fP\&. .PP .SH "SEE ALSO" \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-sign(1)\fP\&. .PP http://blog\&.habets\&.se/2013/11/Should\-I\-generate\-my\-keys\-in\-software\-or\-hardware .PP .SH "AUTHOR" Simple\-TPM\-PK11 was written By Thomas Habets / \&. .PP git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git simple-tpm-pk11-0.06/doc/stpm-keygen.1.yodl000066400000000000000000000046661300541321600203470ustar00rootroot00000000000000manpage(stpm-keygen)(1)(1th December, 2013)(simple-tpm-pk11)() manpagename(stpm-keygen)(Generate key pair for use with simple-tpm-pk11) manpagesynopsis() bf(stpm-keygen) [ -hps ] -o em(output file) manpagedescription() em(stpm-keygen) generates a 2048 RSA key inside the TPM chip, and saves the public key and the SRK-encrypted private key (the "blob") in the em(output file). manpageoptions() startdit() dit(-h) Show usage info. dit(-o em(output file)) Output file, where the public key and key blob will be written. dit(-p) Create the key with a PIN / password. The password will be prompted for inteactively. dit(-s) Ask for the SRK password interactively. By default the "Well Known Secret" (20 nulls) is used. The SRK password is an access token that must be presented for the TPM to perform any operation that involves the TPM, and an actual secret password is usually not required or useful. dit(-S) Generate key in software instead of hardware. The choice between generating the key in software and hardware is not an obvious one. It's hard to verify the quality of keys generated in hardware (e.g. bugs or backdoors), but software keys have existed in RAM at some point. And because software generated keys have to be generated as migratable keys, they can be extracted by someone who knows the TPM owner password. The recommended choice is to generate in hardware, which is also the default. enddit() manpagesection(EXAMPLES) mancommand(.nf) mancommand(.sp) stpm-keygen -o ~/.simple-tpm-pk11/my.key stpm-keygen -p -o ~/.simple-tpm-pk11/my.key Enter key PIN: my secret password here stpm-keygen -sp -o ~/.simple-tpm-pk11/my.key Enter SRK PIN: 12345678 Enter key PIN: my secret password here mancommand(.fi) mancommand(.in) manpagediagnostics() Most errors will probably be related to interacting with the TPM chip. Resetting the TPM chip and taking ownership should take care of most of them. See the em(TPM-TROUBLESHOOTING) section of bf(simple-tpm-pk11(7)). manpageseealso() bf(simple-tpm-pk11(7)), bf(stpm-sign(1)). http://blog.habets.se/2013/11/Should-I-generate-my-keys-in-software-or-hardware manpageauthor() Simple-TPM-PK11 was written By Thomas Habets / . git clone https://github.com/ThomasHabets/simple-tpm-pk11.git simple-tpm-pk11-0.06/doc/stpm-sign.1000066400000000000000000000035301300541321600170440ustar00rootroot00000000000000.TH "stpm\-sign" "1" "1th December, 2013" "simple\-tpm\-pk11" "" .SH "NAME" stpm\-sign \- Sign data using the TPM chip .PP .SH "SYNOPSIS" \fBstpm\-sign\fP [ \-hs ] \-k \fIkey file\fP \-f \fIinput file\fP .PP .SH "DESCRIPTION" \fIstpm\-sign\fP takes the SRK\-encrypted key blob and has the TPM sign the contents of \fIinput file\fP using the key\&. .PP This program is mostly made for debugging, to make sure that the TPM is set up correctly and a valid key was generated\&. .PP .SH "OPTIONS" .IP "\-h" Show usage info\&. .IP "\-f \fIinput file\fP" File containing data to be signed\&. .IP "\-k" Key to sign with\&. The key is generated with \fIstpm\-keysign\fP\&. .IP "\-s" Ask for the SRK password interactively\&. By default the \(dq\&Well Known Secret\(dq\& (20 nulls) is used\&. The SRK password is an access token that must be presented for the TPM to perform any operation that involves the TPM, and an actual secret password is usually not required or useful\&. .PP .SH "EXAMPLES" .nf .sp .PP stpm\-sign \-k ~/\&.simple\-tpm\-pk11/my\&.key \-f my\-data\-here .PP stpm\-sign \-k ~/\&.simple\-tpm\-pk11/my\-PIN\-key\&.key \-f my\-data\-here Enter key PIN: my secret password here .PP stpm\-sign \-sk ~/\&.simple\-tpm\-pk11/my\-PIN\-key\&.key \-f my\-data\-here Enter SRK PIN: 12345678 Enter key PIN: my secret password here .fi .in .PP .SH "DIAGNOSTICS" Most errors will probably be related to interacting with the TPM chip\&. Resetting the TPM chip and taking ownership should take care of most of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of \fBsimple\-tpm\-pk11(7)\fP\&. .PP .SH "SEE ALSO" \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-keygen(1)\fP, \fBstpm\-verify(1)\fP\&. .PP .SH "AUTHOR" Simple\-TPM\-PK11 was written By Thomas Habets / \&. .PP git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git simple-tpm-pk11-0.06/doc/stpm-sign.1.yodl000066400000000000000000000035701300541321600200160ustar00rootroot00000000000000manpage(stpm-sign)(1)(1th December, 2013)(simple-tpm-pk11)() manpagename(stpm-sign)(Sign data using the TPM chip) manpagesynopsis() bf(stpm-sign) [ -hs ] -k em(key file) -f em(input file) manpagedescription() em(stpm-sign) takes the SRK-encrypted key blob and has the TPM sign the contents of em(input file) using the key. This program is mostly made for debugging, to make sure that the TPM is set up correctly and a valid key was generated. manpageoptions() startdit() dit(-h) Show usage info. dit(-f em(input file)) File containing data to be signed. dit(-k) Key to sign with. The key is generated with em(stpm-keysign). dit(-s) Ask for the SRK password interactively. By default the "Well Known Secret" (20 nulls) is used. The SRK password is an access token that must be presented for the TPM to perform any operation that involves the TPM, and an actual secret password is usually not required or useful. enddit() manpagesection(EXAMPLES) mancommand(.nf) mancommand(.sp) stpm-sign -k ~/.simple-tpm-pk11/my.key -f my-data-here stpm-sign -k ~/.simple-tpm-pk11/my-PIN-key.key -f my-data-here Enter key PIN: my secret password here stpm-sign -sk ~/.simple-tpm-pk11/my-PIN-key.key -f my-data-here Enter SRK PIN: 12345678 Enter key PIN: my secret password here mancommand(.fi) mancommand(.in) manpagediagnostics() Most errors will probably be related to interacting with the TPM chip. Resetting the TPM chip and taking ownership should take care of most of them. See the em(TPM-TROUBLESHOOTING) section of bf(simple-tpm-pk11(7)). manpageseealso() bf(simple-tpm-pk11(7)), bf(stpm-keygen(1)), bf(stpm-verify(1)). manpageauthor() Simple-TPM-PK11 was written By Thomas Habets / . git clone https://github.com/ThomasHabets/simple-tpm-pk11.git simple-tpm-pk11-0.06/doc/stpm-verify.1000066400000000000000000000027001300541321600174060ustar00rootroot00000000000000.TH "stpm\-verify" "1" "1th December, 2013" "simple\-tpm\-pk11" "" .SH "NAME" stpm\-verify \- Verify data using the TPM chip .PP .SH "SYNOPSIS" \fBstpm\-verify\fP [ \-hq ] \-f \fIdata\fP \-s \fIsig file\fP \-k .PP .SH "DESCRIPTION" \fIstpm\-verify\fP verifies data signed by \fIstpm\-sign\fP\&. .PP This program is mostly made for debugging, to make sure that the TPM is set up correctly and a valid key was generated\&. .PP .SH "OPTIONS" .IP "\-h" Show usage info\&. .IP "\-f \fIdata file\fP" File containing data to be verified\&. .IP "\-s \fIsig file\fP" File containing signature from \fIstpm\-sign\fP\&. .IP "\-k \fIkey file\fP" File containing the encrypted key blob\&. .PP .SH "EXAMPLES" .nf .sp dd if=/dev/urandom of=to\-sign bs=1 count=35 stpm\-sign \-k ~/\&.simple\-tpm\-pk11/my\&.key \-f to\-sign \-r > to\-sign\&.sig stpm\-verify \-f to\-sign \-k ~/\&.simple\-tpm\-pk11/my\&.key \-s to\-sign\&.sig .PP .fi .in .PP .SH "DIAGNOSTICS" Most errors will probably be related to interacting with the TPM chip\&. Resetting the TPM chip and taking ownership should take care of most of them\&. See the \fITPM\-TROUBLESHOOTING\fP section of \fBsimple\-tpm\-pk11(7)\fP\&. .PP .SH "SEE ALSO" \fBsimple\-tpm\-pk11(7)\fP, \fBstpm\-keygen(1)\fP, \fBstpm\-sign(1)\fP\&. .PP .SH "AUTHOR" Simple\-TPM\-PK11 was written By Thomas Habets / \&. .PP git clone https://github\&.com/ThomasHabets/simple\-tpm\-pk11\&.git simple-tpm-pk11-0.06/doc/stpm-verify.1.yodl000066400000000000000000000027131300541321600203600ustar00rootroot00000000000000manpage(stpm-verify)(1)(1th December, 2013)(simple-tpm-pk11)() manpagename(stpm-verify)(Verify data using the TPM chip) manpagesynopsis() bf(stpm-verify) [ -hq ] -f em(data) -s em(sig file) -k manpagedescription() em(stpm-verify) verifies data signed by em(stpm-sign). This program is mostly made for debugging, to make sure that the TPM is set up correctly and a valid key was generated. manpageoptions() startdit() dit(-h) Show usage info. dit(-f em(data file)) File containing data to be verified. dit(-s em(sig file)) File containing signature from em(stpm-sign). dit(-k em(key file)) File containing the encrypted key blob. enddit() manpagesection(EXAMPLES) mancommand(.nf) mancommand(.sp) dd if=/dev/urandom of=to-sign bs=1 count=35 stpm-sign -k ~/.simple-tpm-pk11/my.key -f to-sign -r > to-sign.sig stpm-verify -f to-sign -k ~/.simple-tpm-pk11/my.key -s to-sign.sig mancommand(.fi) mancommand(.in) manpagediagnostics() Most errors will probably be related to interacting with the TPM chip. Resetting the TPM chip and taking ownership should take care of most of them. See the em(TPM-TROUBLESHOOTING) section of bf(simple-tpm-pk11(7)). manpageseealso() bf(simple-tpm-pk11(7)), bf(stpm-keygen(1)), bf(stpm-sign(1)). manpageauthor() Simple-TPM-PK11 was written By Thomas Habets / . git clone https://github.com/ThomasHabets/simple-tpm-pk11.git simple-tpm-pk11-0.06/src/000077500000000000000000000000001300541321600150625ustar00rootroot00000000000000simple-tpm-pk11-0.06/src/common.cc000066400000000000000000000537461300541321600167000ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include"config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include"openssl/err.h" #include"openssl/ossl_typ.h" #include"openssl/rand.h" #include"openssl/rsa.h" #include"openssl/x509.h" #include"tss/tspi.h" #include"trousers/trousers.h" #include"common.h" #include"tspiwrap.h" #include"internal.h" namespace { // In OpenSSL 1.1 these functions replaced direct access to internals // of the `RSA` struct. // This is local implementations so that we can still build under // OpenSSL 1.0. #ifndef HAVE_RSA_SET0_KEY int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d) { rsa->n = n; rsa->e = e; rsa->d = d; return 0; } #endif #ifndef HAVE_RSA_GET0_KEY void RSA_get0_key(RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) { if (n) { *n = rsa->n; } if (e) { *e = rsa->e; } if (d) { *d = rsa->d; } } #endif #ifndef HAVE_RSA_GET0_FACTORS void RSA_get0_factors(const RSA *rsa, const BIGNUM **p, const BIGNUM **q) { if (p != NULL) { *p = rsa->p; } if (q != NULL) { *q = rsa->q; } } #endif } std::ostream& operator<<(std::ostream& o, const struct stpm::Key& key) { o << "mod=" << stpm::to_hex(key.modulus) << ",exp=" << stpm::to_hex(key.exponent) << ",blob=" << stpm::to_hex(key.blob); return o; } std::ostream& operator<<(std::ostream& o, const struct stpm::SoftwareKey& key) { o << "mod=" << stpm::to_hex(key.modulus) << ",exp=" << stpm::to_hex(key.exponent) << ",key=" << stpm::to_hex(key.key); return o; } BEGIN_NAMESPACE(stpm); const std::string random_device = "/dev/urandom"; const int num_random_bytes = 10240; // 10*8192 bits. const char* env_log_stderr = "SIMPLE_TPM_PK11_LOG_STDERR"; const TSS_UUID srk_uuid = TSS_UUID_SRK; BEGIN_NAMESPACE(); template class AutoFree { public: AutoFree(): resource_(New()) {} AutoFree(T* r): resource_(r) {} AutoFree(const AutoFree&) = delete; AutoFree(const AutoFree&&) = delete; AutoFree& operator=(const AutoFree&) = delete; AutoFree& operator=(const AutoFree&&) = delete; ~AutoFree() { if (!resource_) { return; } try { Free(resource_); resource_ = nullptr; } catch (const std::exception& e) { std::clog << "Exception thrown in free() code.\n"; throw; } } T* get() const { return resource_; } T* operator->() const { return resource_; } T* release() { T* ret = resource_; resource_ = nullptr; return ret; } private: T* resource_; }; typedef AutoFree RSAWrap; typedef AutoFree BIGNUMWrap; END_NAMESPACE(); std::string xgetpass(const std::string& prompt) { const int fd = STDIN_FILENO; std::cerr << prompt << ": " << std::flush; std::string line; if (!isatty(fd)) { getline(std::cin, line); } else { struct termios old; if (tcgetattr(fd, &old)) { throw std::runtime_error(std::string("tcgetattr(stdin): ") + strerror(errno)); } struct termios ti = old; ti.c_lflag &= ~ECHO; if (tcsetattr(fd, TCSAFLUSH, &ti)) { throw std::runtime_error(std::string("tcsetattr(stdin, TCSAFLUSH, no echo): ") + strerror(errno)); } getline(std::cin, line); if (tcsetattr(fd, TCSAFLUSH, &old)) { throw std::runtime_error(std::string("tcsetattr(stdin, TCSAFLUSH, old): ") + strerror(errno)); } } std::cerr << std::endl; return line; } // Wrap Tspi_* calls, checking return value and throwing exception. // TODO: Adding debug logging. TSS_RESULT tscall(const std::string& name, std::function func) { TSS_RESULT res; if (TSS_SUCCESS != (res = func())) { throw TSPIException(name, res); } return res; } TSPIException::TSPIException(const std::string& func, int code) :std::runtime_error(func + ": " + code_to_string(code)), tspi_error(code), extra_(code_to_extra(code)) { } // Turn trousers error code into useful string. std::string TSPIException::code_to_string(int code) { const std::string layer{Trspi_Error_Layer(code)}; const std::string err{Trspi_Error_String(code)}; std::stringstream ss; ss << "Code=0x" << std::setw(8) << std::setbase(16) << std::setfill('0') << code << ": " << layer << ": " << err; return ss.str(); } std::string TSPIException::code_to_extra(int code) { switch (code) { case TPM_E_INVALID_KEYHANDLE: return "Likely problem:\n" " If this happened while trying to read the public SRK, then your TPM is not\n" " configured to allow that. If it happens on any other key then it's probably\n" " a bug in simple-tpm-pk11.\n" "Possible solution:\n" " Allow reading public SRK with tpm_restrictsrk -a."; case TPM_E_AUTHFAIL: return "Likely problem:\n" " Either the SRK password or the key password is incorrect.\n" " The Well Known Secret (20 nulls unhashed) is not the same as the password \"\".\n" "Possible solution:\n" " The SRK password can (and arguable should) be set to the Well Known Secret using:\n" " tpm_changeownerauth -s -r\n" " Alternatively the SRK password can be given with -s to stpm-keygen/stpm-sign and\n" " with srk_pin in the configuration file for the PKCS#11 module."; case TSS_LAYER_TSP | TSS_E_COMM_FAILURE: return "Likely problem:\n" " The tscd daemon is not running and listening on TCP port 30003, or there\n" " is a firewall preventing connections to it.\n" "Possible solution:\n" " Make sure trousers is started (/etc/init.d/trousers start) correctly, and\n" " and check any logs for why it might not be coming up correctly.\n" " It could fail to start because it's not finding a device /dev/tpm*."; case TSS_E_PS_KEY_NOTFOUND: return "Likely problem:\n" " The TPM chip is not active. Use tpm_getpubek to see if its error message\n" " confirms this.\n" "Possible solution:\n" " Power off the machine, power it back on, go into BIOS, and make sure the\n" " TPM chip / security chip is \"Active\"."; } return ""; } std::string xrandom(int bytes) { std::vector buf(bytes); std::ifstream f; f.rdbuf()->pubsetbuf(nullptr, 0); f.open(random_device, std::ios::binary); if (!f.good()) { throw std::runtime_error("Failed to open " + random_device); } f.read(&buf[0], buf.size()); if (f.fail() || f.eof()) { throw std::runtime_error("EOF in " + random_device); } if (static_cast(f.gcount()) != buf.size()) { throw std::runtime_error("Short full read from " + random_device); } return std::string(buf.begin(), buf.end()); } std::string bn2string(const BIGNUM* bn) { std::vector buf(BN_num_bytes(bn)); unsigned int size; if (0 >= (size = BN_bn2bin(bn, &buf[0]))) { throw std::runtime_error("Broken BIGNUM sent to BN_bn2bin."); } return std::string(buf.begin(), buf.end()); } BIGNUM* string2bn(const std::string& s) { BIGNUMWrap ret; if (!BN_bin2bn(reinterpret_cast(s.data()), s.size(), ret.get())) { throw std::runtime_error("Broken BIGNUM sent to BN_bin2bn."); } return ret.release(); } std::string xctime() { time_t t; time(&t); char buf[128] = {0}; ctime_r(&t, buf); while (strlen(buf) && buf[strlen(buf)-1] == '\n') { buf[strlen(buf)-1] = 0; } return buf; } std::string xsprintf(const char* fmt, ...) { va_list args; va_start(args, fmt); va_list va2; va_copy(va2, args); size_t s = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); std::vector buf(s); vsnprintf(&buf[0], s, fmt, va2); va_end(va2); return std::string(buf.begin(), buf.end()); } bool log_stderr() { const char *doit{getenv(env_log_stderr)}; return !!doit; } void do_log(std::ostream* o, const std::string& msg) { *o << msg << std::endl; if (log_stderr()) { std::cerr << msg << std::endl; } } int keysize_flag(int bits) { switch (bits) { case 512: return TSS_KEY_SIZE_512; case 1024: return TSS_KEY_SIZE_1024; case 2048: return TSS_KEY_SIZE_2048; case 4096: return TSS_KEY_SIZE_4096; case 8192: return TSS_KEY_SIZE_8192; case 16384: return TSS_KEY_SIZE_16384; } throw std::runtime_error("Unknown key size: " + std::to_string(bits) + "bit"); } SoftwareKey generate_software_key(int bits) { const std::string entropy = xrandom(num_random_bytes); RAND_seed(entropy.data(), entropy.size()); if (!RAND_status()) { throw std::runtime_error("OpenSSL PRNG wants more entropy"); } RSAWrap rsa; BIGNUMWrap f4; BN_set_word(f4.get(), RSA_F4); if (!RSA_generate_key_ex(rsa.get(), bits, f4.get(), NULL)) { throw std::runtime_error("RSA_generate_key_ex failed"); } SoftwareKey swkey; BIGNUM *m, *e; RSA_get0_key(rsa.get(), const_cast(&m), const_cast(&e), NULL); swkey.modulus = bn2string(m); swkey.exponent = bn2string(e); BIGNUM *p; RSA_get0_factors(rsa.get(), const_cast(&p), NULL); swkey.key = bn2string(p); return swkey; } Key wrap_key(const std::string* srk_pin, const std::string* key_pin, const SoftwareKey& swkey) { TPMStuff stuff{srk_pin}; // === Set up key object === int init_flags = TSS_KEY_TYPE_SIGNING | keysize_flag(swkey.modulus.size() * 8) | TSS_KEY_VOLATILE | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_MIGRATABLE; // Wrapped keys must be migratable. :-( TSS_HKEY key; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, init_flags, &key); TSS_HPOLICY key_policy; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_MIGRATION, &key_policy); // Set PIN. set_policy_secret(key_policy, key_pin); TSCALL(Tspi_Policy_AssignToObject, key_policy, key); // Load SRK public key. { UINT32 pubKeySize; BYTE *pubKey = nullptr; TSCALL(Tspi_Key_GetPubKey, stuff.srk(), &pubKeySize, &pubKey); Tspi_Context_FreeMemory(stuff.ctx(), pubKey); } // Need to set DER mode for signing. TSCALL(Tspi_SetAttribUint32, key, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER); // Set private key. TSCALL(Tspi_SetAttribData, key, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_PRIVATE_KEY, swkey.key.size(), (BYTE*)swkey.key.data()); // Set modulus. TSCALL(Tspi_SetAttribData, key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, swkey.modulus.size(), (BYTE*)swkey.modulus.data()); // Wrap key. TSCALL(Tspi_Key_WrapKey, key, stuff.srk(), 0); Key ret; ret.modulus = swkey.modulus; ret.exponent = swkey.exponent; // Get keyblob. UINT32 blob_size; BYTE* blob_blob; TSCALL(Tspi_GetAttribData, key, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB, &blob_size, &blob_blob); ret.blob = std::string(blob_blob, blob_blob+blob_size); return ret; } Key generate_key(const std::string* srk_pin, const std::string* key_pin, int bits) { TPMStuff stuff{srk_pin}; { // Get some random data and seed the TPM with it. const int chunk = 32; for (int left = num_random_bytes; left > 0; left -= chunk) { const std::string entropy = xrandom(chunk); TSCALL(Tspi_TPM_StirRandom, stuff.tpm(), entropy.size(), (BYTE*)entropy.data()); } } // === Set up key object === int init_flags = TSS_KEY_TYPE_SIGNING | keysize_flag(bits) | TSS_KEY_VOLATILE | TSS_KEY_NOT_MIGRATABLE; if (key_pin) { init_flags |= TSS_KEY_AUTHORIZATION; } else { init_flags |= TSS_KEY_NO_AUTHORIZATION; } TSS_HKEY key; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, init_flags, &key); TSS_HPOLICY key_policy; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &key_policy); set_policy_secret(key_policy, key_pin); TSCALL(Tspi_Policy_AssignToObject, key_policy, key); // Need to set DER mode for signing. TSCALL(Tspi_SetAttribUint32, key, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER); // === Create Key === TSCALL(Tspi_Key_CreateKey, key, stuff.srk(), 0); Key ret; // Get modulus. UINT32 mod_size; BYTE* mod_blob; TSCALL(Tspi_GetAttribData, key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &mod_size, &mod_blob); std::clog << "Modulus size: " << mod_size << std::endl; ret.modulus = std::string(std::string(mod_blob, mod_blob+mod_size)); // Print the public key. // We extract the modulus and exponent separately instead for now. if (false) { TSCALL(Tspi_Key_LoadKey, key, stuff.srk()); UINT32 pub_size; BYTE* pub; TSCALL(Tspi_Key_GetPubKey, key, &pub_size, &pub); std::clog << "Pub: " << to_hex(std::string((char*)pub, pub_size)) << std::endl; } // Get exponent. UINT32 exp_size; BYTE* exp_blob; TSCALL(Tspi_GetAttribData, key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT, &exp_size, &exp_blob); std::clog << "Exponent size: " << exp_size << std::endl; ret.exponent = std::string{std::string(exp_blob, exp_blob+exp_size)}; // Get keysize. UINT32 size; TSCALL(Tspi_GetAttribUint32, key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_KEYSIZE, &size); std::clog << "Size: " << size << std::endl; if ((UINT32)bits != size) { throw std::runtime_error("Asked for " + std::to_string(bits) + " bit key," " but got " + std::to_string(size) + " bit key,"); } // Get keyblob. UINT32 blob_size; BYTE* blob_blob; TSCALL(Tspi_GetAttribData, key, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB, &blob_size, &blob_blob); std::clog << "Blob size: " << blob_size << std::endl; ret.blob = std::string{std::string(blob_blob, blob_blob+blob_size)}; return ret; } std::string xbasename(const std::string& fullpath) { const size_t s = fullpath.size(); std::vector buf(s + 1); memcpy(&buf[0], fullpath.data(), s); const std::string ret{basename(&buf[0])}; return ret; } std::string xgethostname() { std::vector buf(1024); if (gethostname(&buf[0], buf.size() - 1)) { throw std::runtime_error(std::string("gethostbyname(): ") + strerror(errno)); } return &buf[0]; } std::string to_hex(const std::string& s) { std::stringstream ss; for (auto c : s) { ss << std::setw(2) << std::setfill('0') << std::setbase(16) << unsigned(c & 0xff); } return ss.str(); } std::string to_bin(const std::string& s) { std::map m; for (int c = 0; c < 256; c++) { unsigned char t[2] = {(unsigned char)c, 0}; m[to_hex((char*)t)] = c; } if (s.size() & 1) { throw std::runtime_error("to_bin() on odd length string"); } std::string ret; for (unsigned c = 0; c < s.size(); c+=2) { auto t = s.substr(c, 2); ret += m[t]; } return ret; } Key parse_keyfile(const std::string& s) { std::istringstream ss(s); Key key; int linenum = 0; while (!ss.eof()) { std::string line; getline(ss, line); linenum++; if (line.empty() || line[0] == '#') { continue; } std::istringstream linetokens{line}; std::string cmd, rest; getline(linetokens, cmd, ' '); getline(linetokens, rest); if (cmd == "mod") { key.modulus = to_bin(rest); } else if (cmd == "blob") { key.blob = to_bin(rest); } else if (cmd == "exp") { key.exponent = to_bin(rest); } else { throw std::runtime_error("Keyfile format error(line " + std::to_string(linenum) + ": " + line + ")"); } } if (key.modulus.empty() || key.blob.empty() || key.exponent.empty()) { throw std::runtime_error("Keyfile incomplete. Needs modulus, exponent and blob."); } return key; } bool auth_required(const std::string* srk_pin, const Key& key) { TPMStuff stuff{srk_pin}; int init_flags = TSS_KEY_TYPE_SIGNING | TSS_KEY_VOLATILE | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_NOT_MIGRATABLE; TSS_HKEY hkey; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, init_flags, &hkey); TSCALL(Tspi_Context_LoadKeyByBlob, stuff.ctx(), stuff.srk(), key.blob.size(), (BYTE*)key.blob.data(), &hkey); UINT32 auth; // TODO: AUTHUSAGE or AUTHDATAUSAGE? TSCALL(Tspi_GetAttribUint32, hkey, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_AUTHDATAUSAGE, &auth); return !!auth; } std::string slurp_file(const std::string& fn) { std::ifstream f(fn); if (!f) { throw std::runtime_error("Can't open file '" + fn + "'"); } return std::string{std::istreambuf_iterator(f), std::istreambuf_iterator()}; } // Set password/PIN on a policy. If nullptr pin is given, use the Well Known Secret. void set_policy_secret(TSS_HPOLICY policy, const std::string* pin) { if (pin) { TSCALL(Tspi_Policy_SetSecret, policy, TSS_SECRET_MODE_PLAIN, pin->size(), (BYTE*)pin->data()); } else { BYTE wks[] = TSS_WELL_KNOWN_SECRET; int wks_size = sizeof(wks); TSCALL(Tspi_Policy_SetSecret, policy, TSS_SECRET_MODE_SHA1, wks_size, wks); } } /** * https://www.cylab.cmu.edu/tiw/slides/challener-TPM.pdf * TODO: this doesn't work yet. */ SoftwareKey exfiltrate_key(const Key& key, const std::string* srk_pin, const std::string& owner_password, const std::string* key_pin) { TPMStuff stuff{srk_pin}; // === Load key === int init_flags = TSS_KEY_TYPE_SIGNING | TSS_KEY_VOLATILE | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_MIGRATABLE; TSS_HKEY sign; TSS_HPOLICY policy_sign; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, init_flags, &sign); TSCALL(Tspi_Context_LoadKeyByBlob, stuff.ctx(), stuff.srk(), key.blob.size(), (BYTE*)key.blob.data(), &sign); TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_MIGRATION, &policy_sign); set_policy_secret(policy_sign, key_pin); TSCALL(Tspi_Policy_AssignToObject, policy_sign, sign); // Set owner password. { TSS_HPOLICY policy_tpm; TSCALL(Tspi_GetPolicyObject, stuff.tpm(), TSS_POLICY_USAGE, &policy_tpm); set_policy_secret(policy_tpm, &owner_password); } // Generate migration ticket. BYTE* ticket; UINT32 ticket_size; TSCALL(Tspi_TPM_AuthorizeMigrationTicket, stuff.tpm(), stuff.srk(), // TODO: change to target key. TSS_MS_REWRAP, &ticket_size, &ticket); // Create migration blob. BYTE* rnd; UINT32 rnd_size; BYTE* migrblob; UINT32 migrblob_size; TSCALL(Tspi_Key_CreateMigrationBlob, sign, // Key to migrate. stuff.srk(), // Parent key. ticket_size, ticket, // Migration ticket. &rnd_size, &rnd, // Random data. &migrblob_size, &migrblob); // Migration data blob. // TODO: Decrypt migration blob. return SoftwareKey(); } std::string sign(const Key& key, const std::string& data, const std::string* srk_pin, const std::string* key_pin) { TPMStuff stuff{srk_pin}; // === Load key === int init_flags = TSS_KEY_TYPE_SIGNING | TSS_KEY_VOLATILE | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_NOT_MIGRATABLE; TSS_HKEY sign; TSS_HPOLICY policy_sign; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_RSAKEY, init_flags, &sign); TSCALL(Tspi_Context_LoadKeyByBlob, stuff.ctx(), stuff.srk(), key.blob.size(), (BYTE*)key.blob.data(), &sign); TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy_sign); set_policy_secret(policy_sign, key_pin); TSCALL(Tspi_Policy_AssignToObject, policy_sign, sign); // === Sign === TSS_HHASH hash; UINT32 sig_size; BYTE* sig_blob; TSCALL(Tspi_Context_CreateObject, stuff.ctx(), TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &hash); TSCALL(Tspi_Hash_SetHashValue, hash, data.size(), (BYTE*)data.data()); if (false) { TSCALL(Tspi_SetAttribUint32, sign, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER); } TSCALL(Tspi_Hash_Sign, hash, sign, &sig_size, &sig_blob); return std::string{sig_blob, sig_blob+sig_size}; } std::string public_decrypt(const Key& key, const std::string& sig) { // Load key. RSAWrap rsa; if (RSA_set0_key(rsa.get(), string2bn(key.modulus), string2bn(key.exponent), NULL)) { throw std::runtime_error("RSA_set0_key failed"); } // Decrypt signature. std::vector d(RSA_size(rsa.get())); const int len = RSA_public_decrypt( sig.size(), reinterpret_cast(sig.data()), &d[0], rsa.get(), RSA_PKCS1_PADDING); if (len < 0) { throw std::runtime_error(xsprintf("RSA_public_decrypt failed: %s", ERR_error_string(ERR_get_error(), NULL))); } return std::string{&d[0], &d[len]}; } bool verify(const Key& key, const std::string& data, const std::string& sig) { // TODO: Make this comparison constant time. if (data != public_decrypt(key, sig)) { return false; } return true; } END_NAMESPACE(stpm); /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/common.h000066400000000000000000000100001300541321600165120ustar00rootroot00000000000000/** -*- c++ -*- * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Header file for all library functions. */ #ifndef __INCLUDE__SIMPLE_TPM_PK11_COMMON_H__ #define __INCLUDE__SIMPLE_TPM_PK11_COMMON_H__ #include #include #include"tss/tspi.h" namespace stpm { #if 0 } #endif // Exception type for TPM errors, adding helpful troubleshooting information // in extra(). class TSPIException: public std::runtime_error { public: TSPIException(const std::string& s, int code); virtual ~TSPIException() throw() {}; const std::string& extra() const { return extra_; } const int tspi_error; private: static std::string code_to_extra(int); static std::string code_to_string(int); const std::string extra_; }; // TPM key parts in binary. struct Key { std::string exponent; // Almost certainly 65537. std::string modulus; // std::string blob; // For HW keys, blob encrypted by SRK. }; // Software key parts in binary. struct SoftwareKey { std::string exponent; // Almost certainly 65537. std::string modulus; // std::string key; // The private key. }; // Convert binary to hex and back. std::string to_hex(const std::string& s); std::string to_bin(const std::string& s); // Like basename(3), but with std::string. std::string xbasename(const std::string& fullpath); std::string xgethostname(); // Parse a keyfile into a struct. Does not use the TPM. Key parse_keyfile(const std::string&); // Generate a signing key inside the TPM. // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). Key generate_key(const std::string* srk_pin, const std::string* key_pin, int bits); // Generate an RSA key in software. SoftwareKey generate_software_key(int bits); // Generate a signing key inside the TPM. // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). Key wrap_key(const std::string* srk_pin, const std::string* key_pin, const SoftwareKey& key); // Sign plain data. // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). std::string sign(const Key& key, const std::string& data, const std::string* srk_pin, const std::string* key_pin); // Verify signature. // This is a software-only operation. bool verify(const Key& key, const std::string& data, const std::string& sig); // Exfiltrate key // If a PIN is NULL, use the Well Known Secret (20 null bytes unhashed). SoftwareKey exfiltrate_key(const Key& key, const std::string* srk_pin, const std::string& owner_password, const std::string* key_pin); // Return true if key is password protected. bool auth_required(const std::string* srk_pin, const Key& key); std::string xctime(); // Read in a whole file. std::string slurp_file(const std::string& fn); void do_log(std::ostream* o, const std::string& msg); std::string xsprintf(const char* fmt, ...); // This function assumes std::cin is connected to STDIN_FILENO, // and that std::cout and std::cin are attached to "the terminal". std::string xgetpass(const std::string& prompt); void set_policy_secret(TSS_HPOLICY policy, const std::string* pin); } // namespace stpm // Pretty-print keys. std::ostream& operator<<(std::ostream&, const struct stpm::Key&); std::ostream& operator<<(std::ostream&, const struct stpm::SoftwareKey&); #endif /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/common_test.cc000066400000000000000000000021061300541321600177170ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include"gtest/gtest.h" #include"common.h" TEST(Common, ToHex) { std::vector> tests = { {"",""}, {"414243", "ABC"}, {"616263", "abc"}, {"303132", "012"}, {"20217b7dff01", " !{}\xff\x01"}, {"00414243006162630100", std::string("\0ABC\0abc\x1\0", 10)}, }; for (auto& test : tests) { EXPECT_EQ(test.first, stpm::to_hex(test.second)); EXPECT_EQ(test.second, stpm::to_bin(stpm::to_hex(test.second))); } } simple-tpm-pk11-0.06/src/exfiltrate.cc000066400000000000000000000053321300541321600175430ustar00rootroot00000000000000/** * Copyright 2014 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifdef HAVE_CONFIG_H #include"config.h" #endif #include #include #include #include"common.h" #include"internal.h" extern std::string argv0base; BEGIN_NAMESPACE(); int usage(int rc) { std::cout << PACKAGE_STRING << std::endl << "Usage: " << argv0base << " [ -hsOp ] -k \n" << " -h, --help Show this help text.\n" << " -k Key file.\n" << " -O Use Well Known Secret for owner password. Default is ask.\n" << " -p Ask for key password/PIN. Default is Well Known Secret.\n" << " -s Ask for SRK password/PIN. Default is Well Known Secret.\n"; return rc; } END_NAMESPACE(); int wrapped_main(int argc, char **argv) { bool set_srk_pin = false; bool set_key_pin = false; bool set_owner_pin = true; std::string srk_pin; std::string key_pin; std::string owner; std::string keyfile; int c; while (EOF != (c = getopt(argc, argv, "hk:Ops"))) { switch (c) { case 'h': return usage(0); case 'k': keyfile = optarg; break; case 's': set_srk_pin = true; break; case 'p': set_key_pin = true; break; case 'O': set_owner_pin = false; break; default: return usage(1); } } if (keyfile.empty()) { std::cerr << argv0base << ": Empty key file name." << std::endl; return usage(1); } if (set_owner_pin) { owner = stpm::xgetpass("Enter owner password"); } if (set_key_pin) { key_pin = stpm::xgetpass("Enter key password"); } if (set_srk_pin) { srk_pin = stpm::xgetpass("Enter SRK password"); } const auto key = stpm::parse_keyfile(stpm::slurp_file(keyfile)); const auto sw = stpm::exfiltrate_key(key, set_srk_pin ? &srk_pin : nullptr, owner, set_key_pin ? &key_pin : nullptr); std::cout << sw << std::endl; return 0; } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/fake_tspi.cc000066400000000000000000000120511300541321600173350ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Fake libtspi for testing. */ #include"tss/tspi.h" namespace fake_tspi_data { int keysize = 2048; } TSPICALL Tspi_Context_Create(TSS_HCONTEXT* phContext) { return TSS_SUCCESS; } TSPICALL Tspi_SetAttribUint32(TSS_HOBJECT hObject, TSS_FLAG attribFlag, TSS_FLAG subFlag, UINT32 ulAttrib) { return TSS_SUCCESS; } TSPICALL Tspi_GetAttribUint32(TSS_HOBJECT hObject, TSS_FLAG attribFlag, TSS_FLAG subFlag, UINT32* pulAttrib) { switch (subFlag) { case TSS_TSPATTRIB_KEYINFO_AUTHDATAUSAGE: *pulAttrib = 0; break; case TSS_TSPATTRIB_KEYINFO_RSA_KEYSIZE: *pulAttrib = fake_tspi_data::keysize; break; } return TSS_SUCCESS; } TSPICALL Tspi_GetAttribData(TSS_HOBJECT hObject, TSS_FLAG attribFlag, TSS_FLAG subFlag, UINT32* pulAttribDataSize, BYTE** prgbAttribData) { static BYTE buf[10]; *pulAttribDataSize = 10; *prgbAttribData = buf; return TSS_SUCCESS; } TSPICALL Tspi_Context_Connect(TSS_HCONTEXT hContext, TSS_UNICODE* wszDestination) { return TSS_SUCCESS; } TSPICALL Tspi_Key_CreateKey(TSS_HKEY hKey, TSS_HKEY hWrappingKey, TSS_HPCRS hPcrComposite) { return TSS_SUCCESS; } TSPICALL Tspi_Context_CreateObject(TSS_HCONTEXT hContext, TSS_FLAG objectType, TSS_FLAG initFlags, TSS_HOBJECT* phObject) { return TSS_SUCCESS; } TSPICALL Tspi_Context_GetTpmObject(TSS_HCONTEXT hContext, TSS_HTPM* phTPM) { return TSS_SUCCESS; } TSPICALL Tspi_TPM_StirRandom(TSS_HTPM hTPM, UINT32 ulEntropyDataLength, BYTE* rgbEntropyData) { return TSS_SUCCESS; } TSPICALL Tspi_Context_LoadKeyByBlob(TSS_HCONTEXT hContext, TSS_HKEY hUnwrappingKey, UINT32 ulBlobLength, BYTE* rgbBlobData, TSS_HKEY* phKey) { return TSS_SUCCESS; } TSPICALL Tspi_Context_LoadKeyByUUID(TSS_HCONTEXT hContext, TSS_FLAG persistentStorageType, TSS_UUID uuidData, TSS_HKEY* phKey) { return TSS_SUCCESS; } TSPICALL Tspi_Policy_SetSecret(TSS_HPOLICY hPolicy, TSS_FLAG secretMode, UINT32 ulSecretLength, BYTE* rgbSecret) { return TSS_SUCCESS; } TSPICALL Tspi_Policy_AssignToObject(TSS_HPOLICY hPolicy, TSS_HOBJECT hObject) { return TSS_SUCCESS; } TSPICALL Tspi_Hash_Sign(TSS_HHASH hHash, TSS_HKEY hKey, UINT32* pulSignatureLength, BYTE** prgbSignature) { static BYTE sign[] = {0x12, 0x34, 0x56, 0x78}; *pulSignatureLength = sizeof(sign); *prgbSignature = sign; return TSS_SUCCESS; } TSPICALL Tspi_Hash_SetHashValue(TSS_HHASH hHash, UINT32 ulHashValueLength, BYTE* rgbHashValue) { return TSS_SUCCESS; } TSPICALL Tspi_Hash_UpdateHashValue(TSS_HHASH hHash, UINT32 ulDataLength, BYTE* rgbData) { return TSS_SUCCESS; } TSPICALL Tspi_Key_GetPubKey(TSS_HKEY hKey, UINT32* pulPubKeyLength, BYTE** prgbPubKey) { return TSS_SUCCESS; } TSPICALL Tspi_SetAttribData(TSS_HOBJECT hObject, TSS_FLAG attribFlag, TSS_FLAG subFlag, UINT32 ulAttribDataSize, BYTE* rgbAttribData) { return TSS_SUCCESS; } TSPICALL Tspi_Key_WrapKey(TSS_HKEY hKey, TSS_HKEY hWrappingKey, TSS_HPCRS hPcrComposite) { return TSS_SUCCESS; } TSPICALL Tspi_TPM_AuthorizeMigrationTicket(TSS_HTPM hTPM, TSS_HKEY hMigrationKey, TSS_MIGRATE_SCHEME migrationScheme, UINT32* pulMigTicketLength, BYTE** prgbMigTicket) { return TSS_SUCCESS; } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/internal.h000066400000000000000000000023621300541321600170520ustar00rootroot00000000000000/** -*- c++ -*- * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Internal include to not pollute non-namespace parts. #ifndef __INCLUDE__SIMPLE_TPM_PK11_INTERNAL_H__ #define __INCLUDE__SIMPLE_TPM_PK11_INTERNAL_H__ #include #include"tss/tspi.h" #define BEGIN_NAMESPACE(x) namespace x { #define END_NAMESPACE(x) } // Jumpgate to tscall() #define TSCALL(x, ...) tscall(#x, [&]()->TSS_RESULT{return x(__VA_ARGS__);}) BEGIN_NAMESPACE(stpm); extern const TSS_UUID srk_uuid; TSS_RESULT tscall(const std::string& name, std::function func); END_NAMESPACE(stpm); #endif /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 8 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/keygen.cc000066400000000000000000000070061300541321600166560ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Key generator main(). #ifdef HAVE_CONFIG_H #include"config.h" #endif #include #include #include #include #include #include #include"common.h" #include"internal.h" extern std::string argv0base; BEGIN_NAMESPACE(); int usage(int rc) { std::cout << PACKAGE_STRING << std::endl << "Usage: " << argv0base << " [ -hsSp ] [ -b ] -o \n" << " -b Key size in bits. TPM chips tend to support up to 2048.\n" << " -h, --help Show this help text.\n" << " -o Output file to store the key information in.\n" << " -p Set a password/PIN on the generated key.\n" << " -s Ask for SRK password/PIN. Default is Well Known Secret.\n" << " -S Generate the key in software (see manpage).\n"; return rc; } END_NAMESPACE(); int wrapped_main(int argc, char **argv) { int c; std::string output; bool set_srk_pin{false}; bool set_key_pin{false}; int bits = 2048; bool software{false}; while (EOF != (c = getopt(argc, argv, "b:ho:sSp"))) { switch (c) { case 'b': bits = std::stoi(optarg); break; case 'h': return usage(0); case 's': set_srk_pin = true; break; case 'S': software = true; break; case 'p': set_key_pin = true; break; case 'o': output = optarg; break; default: return usage(1); } } if (optind != argc) { std::cerr << argv0base << ": Extra non-option args not allowed.\n"; return usage(1); } if (output.empty()) { std::cerr << argv0base << ": Empty output file name." << std::endl; return usage(1); } std::string srk_pin; if (set_srk_pin) { srk_pin = stpm::xgetpass("Enter SRK PIN"); } std::string key_pin; if (set_key_pin) { key_pin = stpm::xgetpass("Enter key PIN"); } stpm::Key key; if (software) { const auto sw = stpm::generate_software_key(bits); key = stpm::wrap_key(set_srk_pin ? &srk_pin : nullptr, set_key_pin ? &key_pin : nullptr, sw); } else { key = stpm::generate_key(set_srk_pin ? &srk_pin : nullptr, set_key_pin ? &key_pin : nullptr, bits); } std::ofstream fo(output); if (!fo) { std::cerr << "Unable to open '" << output << "': " << strerror(errno) << std::endl; return 1; } fo << "# Some sort of key.\n" << "# Key was generated in " << (software ? "software.\n" : "hardware.\n") << "exp " << stpm::to_hex(key.exponent) << std::endl << "mod " << stpm::to_hex(key.modulus) << std::endl << "blob " << stpm::to_hex(key.blob) << std::endl; return 0; } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/keygen_test.cc000066400000000000000000000101021300541321600177040ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include"config.h" #include"gtest/gtest.h" #include"test_util.h" extern std::string argv0base; static void reset_getopt() { #if HAVE_DECL_OPTRESET optreset = 1; #endif optind = 1; } namespace fake_tspi_data { extern int keysize; } extern int wrapped_main(int, char**); TEST(Usage, NoOpts) { argv0base = "foobinary"; CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", NULL, }; EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("foobinary: Empty output file name.\n", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Usage, HelpOpts) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", (char*)"-h", NULL, }; EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Keygen, EmptyOutputName) { argv0base = "foobinary"; CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", (char*)"-o", (char*)"", NULL, }; EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("foobinary: Empty output file name.\n", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Keygen, BadOutputName) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", (char*)"-o", (char*)"/non/existing/file/here/3ht.sn,hsn", NULL, }; EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_EQ("", s.stdout()); EXPECT_EQ("Unable to open '/non/existing/file/here/3ht.sn,hsn':" " No such file or directory\n", s.stderr()); EXPECT_EQ("Modulus size: 10\nExponent size: 10\nSize: 2048\nBlob size: 10\n", s.stdlog()); } TEST(Keygen, OK) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", (char*)"-o", (char*)"/dev/null", NULL, }; EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_EQ("", s.stdout()); EXPECT_EQ("", s.stderr()); EXPECT_EQ("Modulus size: 10\nExponent size: 10\nSize: 2048\nBlob size: 10\n", s.stdlog()); } TEST(Keygen, SWKeyOK) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", (char*)"-S", (char*)"-o", (char*)"/dev/null", NULL, }; EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_EQ("", s.stdout()); EXPECT_EQ("", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Keygen, SmallerKey) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", (char*)"-b", (char*)"1024", (char*)"-o", (char*)"/dev/null", NULL, }; fake_tspi_data::keysize = 1024; EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_EQ("", s.stdout()); EXPECT_EQ("", s.stderr()); EXPECT_EQ("Modulus size: 10\nExponent size: 10\nSize: 1024\nBlob size: 10\n", s.stdlog()); } TEST(Keygen, WrongTPMKeySize) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"keygen", (char*)"-b", (char*)"1024", (char*)"-o", (char*)"/dev/null", NULL, }; fake_tspi_data::keysize = 2048; EXPECT_THROW(wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv), std::exception); } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/libgtest.cc000066400000000000000000000014541300541321600172120ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #ifndef PRECOMPILED_GTEST #include"/usr/src/gtest/src/gtest_main.cc" #include"/usr/src/gtest/src/gtest-all.cc" #endif std::string argv0base = "test-binary"; simple-tpm-pk11-0.06/src/pk11.cc000066400000000000000000000265051300541321600161550ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * * * This file contains the PKCS#11 API, meaning C_GetFunctionList and the C * callbacks it supplies. * * In this file is the glue between the trousers C API and the rest of the * simple-tpm-pk11 code which is C++. */ #include #include #include #include #include #include #include #include #include #include #include #include"tss/tspi.h" #include"common.h" #include"internal.h" #include"session.h" using stpm::xctime; using stpm::xsprintf; BEGIN_NAMESPACE(); CK_FUNCTION_LIST funclist; const std::string config_dir = ".simple-tpm-pk11"; const char* env_debug = "SIMPLE_TPM_PK11_DEBUG"; const char* env_config = "SIMPLE_TPM_PK11_CONFIG"; const CK_SLOT_ID tpm_slot_id = 0x1234; // TODO: allocate and free sessions properly. std::vector sessions; Config get_config(); void log_error(const std::string& msg) { try { auto cfg = get_config(); stpm::do_log(cfg.logfile_.get(), xctime() + " " + msg); } catch(...) { std::cerr << "PK11 ERROR> " << msg << std::endl; } syslog(LOG_ERR, "%s", msg.c_str()); } bool debug_env() { const char *dbg{getenv(env_debug)}; return !!dbg; } void log_debug(const std::string& msg) { try { auto cfg = get_config(); if (cfg.debug_ || debug_env()) { stpm::do_log(cfg.logfile_.get(), xctime() + " DEBUG " + msg); } } catch (...) { if (debug_env()) { std::cerr << xctime() << " DEBUG " << msg << std::endl; } } } Config get_config() { const char* home{getenv("HOME")}; if (home == nullptr) { throw std::runtime_error(std::string(__func__) + "(): " + "getenv(HOME) failed."); } std::string config_path{std::string{home} + "/" + config_dir + "/config"}; const char* conf_env{getenv(env_config)}; if (conf_env) { config_path = conf_env; } auto ret = Config{config_path}; if (debug_env()) { ret.debug_ = true; } return ret; } CK_RV wrap_exceptions(const std::string& name, std::function f) { log_debug(name); try { f(); return CKR_OK; } catch (const PK11Error& e) { log_error(name + "(): " + e.what()); return e.code; } catch (const std::exception& e) { log_error(name + "(): " + e.what()); } catch (...) { log_error(name + "(): Unknown exception"); } return CKR_FUNCTION_FAILED; } CK_RV C_GetInfo(CK_INFO_PTR pInfo) { return wrap_exceptions(__func__, [&]{ memset(pInfo, 0, sizeof(CK_INFO)); pInfo->cryptokiVersion.major = 0; pInfo->cryptokiVersion.minor = 1; // TODO: flags strcpy((char*)pInfo->manufacturerID, "simple-tpm-pk11 manufacturer"); strcpy((char*)pInfo->libraryDescription, "simple-tpm-pk11 library"); // TODO: take these version numbers from somewhere canonical. pInfo->libraryVersion.major = 0; pInfo->libraryVersion.minor = 1; }); } CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pusCount) { return wrap_exceptions(__func__, [&]{ if (*pusCount) { *pSlotList = tpm_slot_id; } *pusCount = 1; }); } CK_RV C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_RV (*Notify) (CK_SESSION_HANDLE hSession, CK_NOTIFICATION event, CK_VOID_PTR pApplication), CK_SESSION_HANDLE_PTR phSession) { return wrap_exceptions(__func__, [&]{ sessions.emplace_back(get_config()); *phSession = sessions.size() - 1; }); } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { return wrap_exceptions(__func__, [&]{}); } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { return wrap_exceptions(__func__, [&]{ pInfo->slotID = 0; pInfo->state = CKS_RW_USER_FUNCTIONS; /* ? */ pInfo->flags = CKF_SERIAL_SESSION; pInfo->ulDeviceError = 0; }); } CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG usPinLen) { return wrap_exceptions(__func__, [&]{ sessions[hSession].Login(userType, std::string{pPin, pPin+usPinLen}); }); } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { return wrap_exceptions(__func__, [&]{}); } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { return wrap_exceptions(__func__, [&]{ // TODO: fill these out from slot. strcpy((char*)pInfo->slotDescription, "Simple-TPM-PK11 slot"); strcpy((char*)pInfo->manufacturerID, "manuf id"); pInfo->flags = CKF_TOKEN_PRESENT; pInfo->hardwareVersion = { 0, 0 }; pInfo->firmwareVersion = { 0, 0 }; }); } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { return wrap_exceptions(__func__, [&]{ // TODO: fill these out from token. strcpy((char*)pInfo->label, "Simple-TPM-PK11 token"); strcpy((char*)pInfo->manufacturerID, "manuf id"); strcpy((char*)pInfo->model, "model"); strcpy((char*)pInfo->serialNumber, "serial"); // TODO: Add CKF_RNG. pInfo->flags = CKF_TOKEN_INITIALIZED; auto config = get_config(); std::string kfs; try { kfs = stpm::slurp_file(config.keyfile_); } catch (...) { throw PK11Error(CKR_GENERAL_ERROR, "Failed to read key file '" + config.keyfile_ + "'"); } const stpm::Key key = stpm::parse_keyfile(kfs); if (stpm::auth_required(config.set_srk_pin_ ? &config.srk_pin_ : NULL, key)) { pInfo->flags |= CKF_LOGIN_REQUIRED; } pInfo->ulMaxSessionCount = 1000; pInfo->ulSessionCount = 0; pInfo->ulMaxRwSessionCount = 1000; pInfo->ulRwSessionCount = 0; pInfo->ulMaxPinLen = 64; pInfo->ulMinPinLen = 6; pInfo->ulTotalPublicMemory = 1000000; pInfo->ulFreePublicMemory = 1000000; pInfo->ulTotalPrivateMemory = 1000000; pInfo->ulFreePrivateMemory = 1000000; pInfo->hardwareVersion.major = 0; pInfo->firmwareVersion.major = 0; strcpy((char*)pInfo->utcTime, "bleh"); // TODO. }); } CK_RV C_GetMechanismList(CK_SLOT_ID slot_id, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { return wrap_exceptions(__func__, [&]{ // TODO: I'm making these mechanisms up. They are probably not correct. if (slot_id != tpm_slot_id) { throw PK11Error(CKR_GENERAL_ERROR, "Not supported."); } if (*pulCount > 0) { pMechanismList[0] = CKM_RSA_PKCS; } // PKCS#11 key generation not yet implemented. // if (*pulCount > 1) { // pMechanismList[1] = CKM_RSA_PKCS_KEY_PAIR_GEN; // } *pulCount = 1; }); } CK_RV C_GetMechanismInfo(CK_SLOT_ID slot_id, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO *info) { // TODO: I'm making these mechanisms up. They are probably not correct. return wrap_exceptions(__func__, [&]{ if (slot_id != tpm_slot_id) { throw PK11Error(CKR_GENERAL_ERROR, "Not supported."); } info->ulMinKeySize = 512; info->ulMaxKeySize = 2048; switch (type) { case CKM_RSA_PKCS: info->flags = CKF_SIGN | CKF_HW; break; case CKM_RSA_PKCS_KEY_PAIR_GEN: info->flags = CKF_GENERATE_KEY_PAIR | CKF_HW; break; default: throw PK11Error(CKR_GENERAL_ERROR, "Not supported."); } }); } CK_RV C_Finalize(CK_VOID_PTR pReserved) { return wrap_exceptions(__func__, [&]{}); } CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR filters, CK_ULONG nfilters) { return wrap_exceptions(__func__, [&]{ sessions[hSession].FindObjectsInit(filters, nfilters); }); } CK_RV C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG usMaxObjectCount, CK_ULONG_PTR nfound) { return wrap_exceptions(__func__, [&]{ *nfound = sessions[hSession].FindObjects(phObject, usMaxObjectCount); }); } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession) { return wrap_exceptions(__func__, [&]{}); } CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount) { return wrap_exceptions(xsprintf("%s(count=%d)", __func__, usCount), [&]{ sessions[hSession].GetAttributeValue(hObject, pTemplate, usCount); }); } CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { return wrap_exceptions(__func__, [&]{ sessions[hSession].SignInit(pMechanism, hKey); }); } CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG usDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pusSignatureLen) { return wrap_exceptions(__func__, [&]{ sessions[hSession].Sign(pData, usDataLen, pSignature, pusSignatureLen); }); } CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { return wrap_exceptions(__func__, [&]{}); } CK_RV C_InitToken(CK_SLOT_ID slot_id, unsigned char *pin, unsigned long pin_len, unsigned char *label) { return wrap_exceptions(__func__, [&]{ throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); }); } CK_RV C_InitPIN(CK_SESSION_HANDLE session, unsigned char *pin, unsigned long pin_len) { return wrap_exceptions(__func__, [&]{ throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); }); } CK_RV C_SetPIN(CK_SESSION_HANDLE session, unsigned char *old_pin, unsigned long old_len, unsigned char *new_pin, unsigned long new_len) { return wrap_exceptions(__func__, [&]{ throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); }); } CK_RV C_CloseAllSessions(CK_SLOT_ID slot_id) { return wrap_exceptions(__func__, [&]{ throw PK11Error(CKR_GENERAL_ERROR, "Not implemented."); }); } CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) { return wrap_exceptions(__func__, [&]{ // No random number generation is supported. throw PK11Error(CKR_RANDOM_NO_RNG, "Not supported."); }); } __attribute__((constructor)) void cons() { #define F(x) funclist.C_##x = C_##x F(GetInfo); F(Initialize); F(InitToken); F(InitPIN); F(SetPIN); F(Finalize); F(GetSlotList); F(GetSlotInfo); F(GetTokenInfo); F(GetMechanismList); F(GetMechanismInfo); F(Login); F(Logout); F(OpenSession); F(CloseSession); F(CloseAllSessions); F(GetSessionInfo); F(FindObjectsInit); F(FindObjects); F(FindObjectsFinal); F(GetAttributeValue); F(SignInit); F(Sign); F(SeedRandom); #undef F } END_NAMESPACE(); extern "C" CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { return wrap_exceptions(__func__, [&]{ *ppFunctionList = &funclist; }); } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/pk11_test.cc000066400000000000000000000116741300541321600172150ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Test the PKCS#11 module purely through the C API. #include"gtest/gtest.h" #include #include"common.h" #include"test_util.h" namespace fake_tspi_data { extern int keysize; } class PK11Test: public ::testing::Test { public: void SetUp() { setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.config", 1); setenv("SIMPLE_TPM_PK11_DEBUG", "on", 1); ASSERT_EQ(CKR_OK, C_GetFunctionList(&func_)); ASSERT_EQ(CKR_OK, func_->C_Initialize(h_)); } void TearDown() { ASSERT_EQ(CKR_OK, func_->C_Finalize(h_)); } protected: CaptureStreams cs; CK_FUNCTION_LIST *func_; void* h_; }; TEST_F(PK11Test, GetInfo) { CK_INFO info; ASSERT_EQ(CKR_OK, func_->C_GetInfo(&info)); ASSERT_EQ(0, info.cryptokiVersion.major); ASSERT_EQ(1, info.cryptokiVersion.minor); ASSERT_EQ(0, info.libraryVersion.major); ASSERT_EQ(1, info.libraryVersion.minor); ASSERT_EQ("simple-tpm-pk11 manufacturer", std::string((char*)info.manufacturerID)); ASSERT_EQ("simple-tpm-pk11 library", std::string((char*)info.libraryDescription)); } TEST_F(PK11Test, NoConfig) { setenv("SIMPLE_TPM_PK11_CONFIG", "/config/missing/here", 1); CK_SESSION_HANDLE s; ASSERT_EQ(CKR_FUNCTION_FAILED, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); } TEST_F(PK11Test, EmptyConfig) { setenv("SIMPLE_TPM_PK11_CONFIG", "/dev/null", 1); setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); CK_SESSION_HANDLE s; ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; CK_OBJECT_HANDLE key = 0; // TODO: Get first key. CK_BYTE data[35]; CK_BYTE signature[20]; CK_ULONG slen; ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); ASSERT_EQ(CKR_GENERAL_ERROR, func_->C_Sign(s, data, sizeof(data), signature, &slen)); ASSERT_NE(cs.stderr().find("/dev/" + stpm::xgethostname() + ".key"), std::string::npos); } TEST_F(PK11Test, MissingKeyfile) { setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.missingkeyfile.config", 1); setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); CK_SESSION_HANDLE s; ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; CK_OBJECT_HANDLE key = 0; // TODO: Get first key. CK_BYTE data[35]; CK_BYTE signature[20]; CK_ULONG slen; ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); ASSERT_EQ(CKR_GENERAL_ERROR, func_->C_Sign(s, data, sizeof(data), signature, &slen)); EXPECT_NE(std::string::npos, cs.stderr().find("Failed to read key file 'testdata/missing-file'")); } TEST_F(PK11Test, BadKeyfile) { setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.badkeyfile.config", 1); setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); CK_SESSION_HANDLE s; ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; CK_OBJECT_HANDLE key = 0; // TODO: Get first key. CK_BYTE data[35]; CK_BYTE signature[20]; CK_ULONG slen; ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); ASSERT_EQ(CKR_FUNCTION_FAILED, func_->C_Sign(s, data, sizeof(data), signature, &slen)); EXPECT_NE(std::string::npos, cs.stderr().find("Keyfile format error")); } TEST_F(PK11Test, AbsKeyfile) { setenv("SIMPLE_TPM_PK11_CONFIG", "testdata/pk11.abskeyfile.config", 1); setenv("SIMPLE_TPM_PK11_LOG_STDERR", "on", 1); CK_SESSION_HANDLE s; ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; CK_OBJECT_HANDLE key = 0; // TODO: Get first key. CK_BYTE data[35]; CK_BYTE signature[20]; CK_ULONG slen; ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); ASSERT_EQ(CKR_FUNCTION_FAILED, func_->C_Sign(s, data, sizeof(data), signature, &slen)); EXPECT_NE(std::string::npos, cs.stderr().find("Keyfile incomplete")); } TEST_F(PK11Test, Sign) { // TODO: actually test correct output. CK_SESSION_HANDLE s; ASSERT_EQ(CKR_OK, func_->C_OpenSession(0, 0, nullptr, nullptr, &s)); CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 }; CK_OBJECT_HANDLE key = 0; // TODO: Get first key. CK_BYTE data[35]; CK_BYTE signature[20]; CK_ULONG slen; ASSERT_EQ(CKR_OK, func_->C_SignInit(s, &mech, key)); ASSERT_EQ(CKR_OK, func_->C_Sign(s, data, sizeof(data), signature, &slen)); ASSERT_EQ(CKR_OK, func_->C_CloseSession(s)); } simple-tpm-pk11-0.06/src/session.cc000066400000000000000000000152101300541321600170530ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include"session.h" #include #include #include #include #include #include #include #include #include #include #include"common.h" #include"internal.h" BEGIN_NAMESPACE(); // Like dirname(3), but always returns a string ending in '/', thus // always being safe for appeding a filename to. std::string xdirname(const std::string& relative) { const size_t s = relative.size(); std::vector buf(s + 1); memcpy(&buf[0], relative.data(), s); const std::string ret{dirname(&buf[0])}; if (ret == "/") { return ret; } return ret + "/"; } END_NAMESPACE(); Config::Config(const std::string& fn) :configfile_(fn), logfile_(new std::ofstream), set_srk_pin_(false), set_key_pin_(false), debug_(false) { std::ifstream f{fn}; if (!f) { throw std::runtime_error("Opening config file " + fn + " failed"); } read_file(f); if (*logfile_) { logfile_->open(logfilename_, std::ofstream::app); if (!logfile_) { throw std::runtime_error("Unable to open logfile " + logfilename_); } } if (keyfile_.empty()) { // TODO: should use fqdn? keyfile_ = xdirname(configfile_) + stpm::xgethostname() + ".key"; } } void Config::read_file(std::ifstream& f) { while (!f.eof()) { std::string line; getline(f, line); if (line.empty() || line[0] == '#') { continue; } std::istringstream linetokens{line}; std::string cmd, rest; getline(linetokens, cmd, ' '); getline(linetokens, rest); if (cmd == "key") { keyfile_ = rest; if (keyfile_.substr(0, 1) != "/") { keyfile_ = xdirname(configfile_) + rest; } } else if (cmd == "log") { logfilename_ = rest; if (logfilename_.substr(0, 1) != "/") { logfilename_ = xdirname(configfile_) + rest; } } else if (cmd == "key_pin") { key_pin_ = rest; set_key_pin_ = true; } else if (cmd == "srk_pin") { srk_pin_ = rest; set_srk_pin_ = true; } else if (cmd == "debug") { debug_ = true; } else { throw std::runtime_error("Unknown config line: " + line); } } } Session::Session(const Config& config) :config_(config), findpos_(0) { } void Session::Login(CK_USER_TYPE type, const std::string& pin) { config_.key_pin_ = pin; config_.set_key_pin_ = true; } void Session::FindObjectsInit(CK_ATTRIBUTE_PTR filters, int nfilters) { findpos_ = 0; } int Session::FindObjects(CK_OBJECT_HANDLE_PTR obj, int maxobj) { if (findpos_ == 1) { return 0; } if (maxobj == 0) { return 0; } *obj = 0; findpos_++; return 1; } void Config::debug_log(const char* fmt, ...) { va_list args; va_start(args, fmt); va_list va2; va_copy(va2, args); size_t s = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); std::vector buf(s); vsnprintf(&buf[0], s, fmt, va2); va_end(va2); if (debug_) { stpm::do_log(logfile_.get(), stpm::xctime() + " DEBUG " + std::string(buf.begin(), buf.end())); } } void Session::GetAttributeValue(CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount) { std::string kfs; try { kfs = stpm::slurp_file(config_.keyfile_); } catch (...) { throw PK11Error(CKR_GENERAL_ERROR, "Failed to read key file '" + config_.keyfile_ + "'"); } const stpm::Key key = stpm::parse_keyfile(kfs); for (unsigned i = 0; i < usCount; i++) { switch (pTemplate[i].type) { case CKA_ID: config_.debug_log(" Attribute %d: ID", i); // TODO: populate properly. pTemplate[i].ulValueLen = 10; // ID break; case CKA_MODULUS: config_.debug_log(" Attribute %d: Modulus size %d", i, key.modulus.size()); pTemplate[i].ulValueLen = key.modulus.size(); if (pTemplate[i].pValue) { memcpy(pTemplate[i].pValue, key.modulus.data(), key.modulus.size()); } break; case CKA_PUBLIC_EXPONENT: config_.debug_log(" Attribute %d: Exponent size %d", i, key.exponent.size()); pTemplate[i].ulValueLen = key.exponent.size(); if (pTemplate[i].pValue) { memcpy(pTemplate[i].pValue, key.exponent.data(), key.exponent.size()); } break; case CKA_SUBJECT: config_.debug_log(" Attribute %d: Subject (unsupported)", i); pTemplate[i].ulValueLen = 0; break; case CKA_VALUE: config_.debug_log(" Attribute %d: Value (unsupported)", i); pTemplate[i].ulValueLen = 0; break; default: config_.debug_log(" Attribute %d: Unknown (%d)", i, pTemplate[i].type); pTemplate[i].ulValueLen = 0; std::stringstream ss; ss << stpm::xctime() << " unknown attribute: " << pTemplate[i].type; stpm::do_log(config_.logfile_.get(), ss.str()); } } } void Session::SignInit(CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { } void Session::Sign(CK_BYTE_PTR pData, CK_ULONG usDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pusSignatureLen) { std::string kfs; try { kfs = stpm::slurp_file(config_.keyfile_); } catch (...) { throw PK11Error(CKR_GENERAL_ERROR, "Failed to read key file '" + config_.keyfile_ + "'"); } const stpm::Key key = stpm::parse_keyfile(kfs); const std::string data{pData, pData+usDataLen}; const std::string signature{ stpm::sign(key, data, config_.set_srk_pin_ ? &config_.srk_pin_ : NULL, config_.set_key_pin_ ? &config_.key_pin_ : NULL)}; *pusSignatureLen = signature.size(); memcpy(pSignature, signature.data(), signature.size()); std::stringstream ss; ss << stpm::xctime() << " signing " << data.size() << " bytes."; stpm::do_log(config_.logfile_.get(), ss.str()); config_.debug_log("signing %s (len %d), output %d bytes", stpm::to_hex(data).c_str(), data.size(), *pusSignatureLen); } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/session.h000066400000000000000000000043101300541321600167140ustar00rootroot00000000000000/** -*- c++ -*- * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __INCLUDE__SIMPLE_TPM_PK11_SESSION_H__ #define __INCLUDE__SIMPLE_TPM_PK11_SESSION_H__ #include #include #include #include #include class PK11Error: public std::runtime_error { public: PK11Error(int incode, const std::string& msg) :std::runtime_error("Code=" + std::to_string(unsigned(incode)) + ": " + msg), code(incode) {} virtual ~PK11Error() throw() {} const int code; }; class Config { public: Config(const std::string&); std::string configfile_; std::string keyfile_; std::string logfilename_; std::shared_ptr logfile_; bool set_srk_pin_; bool set_key_pin_; std::string srk_pin_; std::string key_pin_; bool debug_; void debug_log(const char*,...); private: void read_file(std::ifstream&); }; class Session { public: Session(const Config&); void Login(CK_USER_TYPE type, const std::string& pin); void FindObjectsInit(CK_ATTRIBUTE_PTR filters, int nfilters); // Find a couple of objects. Returns number of objects supplied. int FindObjects(CK_OBJECT_HANDLE_PTR obj, int maxobj); void GetAttributeValue(CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount); void SignInit(CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey); void Sign(CK_BYTE_PTR pData, CK_ULONG usDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pusSignatureLen); private: Config config_; std::string pin_; int findpos_; }; #endif /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/sign.cc000066400000000000000000000060121300541321600163300ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifdef HAVE_CONFIG_H #include"config.h" #endif #include #include #include #include #include #include #include #include #include"tss/tspi.h" #include"common.h" #include"internal.h" extern std::string argv0base; BEGIN_NAMESPACE(); int usage(int rc) { std::cout << PACKAGE_STRING << std::endl << "Usage: " << argv0base << " [ -hrs ] -f -k \n" << " -f File to sign.\n" << " -h, --help Show this help text.\n" << " -k File containing key data.\n" << " -r Raw.\n" << " -s Prompt for SRK password/PIN.\n"; return rc; } END_NAMESPACE(); int wrapped_main(int argc, char **argv) { int c; std::string keyfile; std::string signfile; bool set_srk_pin{false}; bool set_key_pin{false}; bool raw = false; while (EOF != (c = getopt(argc, argv, "rhk:f:s"))) { switch (c) { case 'r': raw = true; break; case 'h': return usage(0); case 'k': keyfile = optarg; break; case 's': set_srk_pin = true; break; case 'f': signfile = optarg; break; default: return usage(1); } } if (optind != argc) { std::cerr << "stpm-sign: Extra non-option args not allowed.\n"; return usage(1); } if (keyfile.empty() || signfile.empty()) { std::cerr << "stpm-sign: Need to specify keyfile and data file" << std::endl; return usage(1); } const auto key = stpm::parse_keyfile(stpm::slurp_file(keyfile)); const auto to_sign = stpm::slurp_file(signfile); std::string srk_pin; if (set_srk_pin) { srk_pin = stpm::xgetpass("Enter SRK PIN"); } if (stpm::auth_required(set_srk_pin ? &srk_pin : NULL, key)) { set_key_pin = true; } std::string key_pin; if (set_key_pin) { key_pin = stpm::xgetpass("Enter key PIN"); } auto out = sign(key, to_sign, set_srk_pin ? &srk_pin : NULL, set_key_pin ? &key_pin : NULL); if (raw) { std::cout << out; } else { std::cout << "Loaded key: " << key << std::endl << "--- Signature ---\n" << stpm::to_hex(out) << std::endl; } return 0; } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/sign_test.cc000066400000000000000000000103221300541321600173660ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include"config.h" #include #include"gtest/gtest.h" #include"test_util.h" static void reset_getopt() { #if HAVE_DECL_OPTRESET optreset = 1; #endif optind = 1; } extern int wrapped_main(int, char**); TEST(Usage, NoOpts) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", NULL, }; EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("stpm-sign: Need to specify keyfile and data file\n", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Sign, NoDataFile) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", (char*)"-k", (char*)"", NULL, }; EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("stpm-sign: Need to specify keyfile and data file\n", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Sign, NoKeyFile) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", (char*)"-f", (char*)"", NULL, }; EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("stpm-sign: Need to specify keyfile and data file\n", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Usage, HelpOpts) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", (char*)"-h", NULL, }; EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Sign, BadKeyfileName) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", (char*)"-k", (char*)"/non/existing/file/here/3ht.sn,hsn", (char*)"-f", (char*)"/dev/null", NULL, }; EXPECT_THROW(wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv), std::runtime_error); EXPECT_EQ("", s.stdout()); EXPECT_EQ("", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Sign, BadDatafileName) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", (char*)"-k", (char*)"testdata/correct.key", (char*)"-f", (char*)"/non/existing/file/here/3ht.sn,hsn", NULL, }; EXPECT_THROW(wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv), std::runtime_error); EXPECT_EQ("", s.stdlog()); EXPECT_EQ("", s.stdout()); EXPECT_EQ("", s.stderr()); EXPECT_EQ("", s.stdlog()); } TEST(Sign, BadKeyfile) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", (char*)"-k", (char*)"testdata/broken.key", (char*)"-f", (char*)"/dev/null", NULL, }; bool threw = false; try { wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv); } catch (const std::runtime_error& e) { EXPECT_EQ("Keyfile format error(line 2: typo 010001)", std::string(e.what())); EXPECT_EQ("", s.stdlog()); EXPECT_EQ("", s.stdout()); EXPECT_EQ("", s.stderr()); threw = true; } EXPECT_TRUE(threw); } TEST(Sign, OK) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", (char*)"-k", (char*)"testdata/correct.key", (char*)"-f", (char*)"/dev/null", NULL, }; EXPECT_EQ(0, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_EQ("Loaded key: mod=010203,exp=010001,blob=010203040506\n--- Signature ---\n12345678\n", s.stdout()); EXPECT_EQ("", s.stderr()); EXPECT_EQ("", s.stdlog()); } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/test_util.h000066400000000000000000000030711300541321600172500ustar00rootroot00000000000000/** -*- c++ -*- * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __INCLUDE__SIMPLE_TPM_PK11_TEST_UTIL_H__ #define __INCLUDE__SIMPLE_TPM_PK11_TEST_UTIL_H__ #include #include struct CaptureStreams { CaptureStreams() :stopped_(false), outold_(std::cout.rdbuf(outbuf_.rdbuf())), errold_(std::cerr.rdbuf(errbuf_.rdbuf())), logold_(std::clog.rdbuf(logbuf_.rdbuf())) { } void stop() { if (!stopped_) { std::cout.rdbuf(outold_); std::cerr.rdbuf(errold_); std::clog.rdbuf(logold_); stopped_ = true; } } std::string stdout() const { return outbuf_.str(); } std::string stderr() const { return errbuf_.str(); } std::string stdlog() const { return logbuf_.str(); } ~CaptureStreams() { stop(); } private: bool stopped_; std::stringstream outbuf_, errbuf_, logbuf_; std::streambuf *outold_, *errold_, *logold_; }; #endif /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/tspiwrap.cc000066400000000000000000000043241300541321600172450ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include"tspiwrap.h" #include"common.h" #include"internal.h" BEGIN_NAMESPACE(stpm); TspiContext::TspiContext() :ctx_(0) { TSCALL(Tspi_Context_Create, &ctx_); try { TSCALL(Tspi_Context_Connect, ctx_, NULL); } catch (...) { TSCALL(Tspi_Context_FreeMemory, ctx_, NULL); TSCALL(Tspi_Context_Close, ctx_); throw; } } TspiContext::~TspiContext() { Tspi_Context_FreeMemory(ctx_, NULL); Tspi_Context_Close(ctx_); } TspiTPM::TspiTPM(TspiContext&ctx) :tpm_(0) { TSCALL(Tspi_Context_GetTpmObject, ctx.ctx(), &tpm_); } TspiTPM::~TspiTPM() { // TODO: Something should be freed here, right? } TspiKey::TspiKey(TspiContext& ctx, TSS_UUID uuid, const std::string* pin) :ctx_(ctx), key_(0), policy_(0) { try { TSCALL(Tspi_Context_CreateObject, ctx_.ctx(), TSS_OBJECT_TYPE_RSAKEY, TSS_KEY_TSP_SRK, &key_); TSCALL(Tspi_Context_LoadKeyByUUID, ctx_.ctx(), TSS_PS_TYPE_SYSTEM, uuid, &key_); TSCALL(Tspi_Context_CreateObject, ctx_.ctx(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy_); set_policy_secret(policy_, pin); TSCALL(Tspi_Policy_AssignToObject, policy_, key_); } catch (...) { destroy(); throw; } } TspiKey::~TspiKey() { destroy(); } void TspiKey::destroy() { if (policy_) { Tspi_Context_CloseObject(ctx_.ctx(), policy_); } if (key_) { Tspi_Context_CloseObject(ctx_.ctx(), key_); } } TPMStuff::TPMStuff(const std::string* srk_pin) :tpm_(ctx_), srk_(ctx_, srk_uuid, srk_pin) { } END_NAMESPACE(stpm); /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/tspiwrap.h000066400000000000000000000041201300541321600171010ustar00rootroot00000000000000/** -*- c++ -*- * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Wrapping classes to make cleanup easier. #ifndef __INCLUDE__SIMPLE_TPM_PK11_TSPIWRAP_H__ #define __INCLUDE__SIMPLE_TPM_PK11_TSPIWRAP_H__ #include #include"tss/tspi.h" namespace stpm { #if 0 } #endif class TspiContext { public: TspiContext(const TspiContext&) = delete; TspiContext& operator=(const TspiContext&) = delete; TspiContext(); ~TspiContext(); TSS_HCONTEXT ctx() const { return ctx_; } private: TSS_HCONTEXT ctx_; }; class TspiTPM { public: TspiTPM(const TspiTPM&) = delete; TspiTPM& operator=(const TspiTPM&) = delete; TspiTPM(TspiContext&ctx); ~TspiTPM(); TSS_HTPM tpm() { return tpm_; } private: TSS_HTPM tpm_; }; class TspiKey { public: TspiKey(const TspiKey&) = delete; TspiKey& operator=(const TspiKey&) = delete; TspiKey(TspiContext&, TSS_UUID uuid, const std::string* pin); ~TspiKey(); TSS_HKEY key() const { return key_; } private: TspiContext& ctx_; TSS_HKEY key_; TSS_HPOLICY policy_; void destroy(); }; class TPMStuff { public: TPMStuff(const TPMStuff&) = delete; TPMStuff& operator=(const TPMStuff&) = delete; TPMStuff(const std::string* srk_pin); TSS_HCONTEXT ctx() { return ctx_.ctx(); } TSS_HTPM tpm() { return tpm_.tpm(); } TSS_HKEY srk() { return srk_.key(); } private: // Order matters. Do not change. TspiContext ctx_; TspiTPM tpm_; TspiKey srk_; }; } // namespace stpm #endif /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/verify.cc000066400000000000000000000053161300541321600167020ustar00rootroot00000000000000/** * Copyright 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifdef HAVE_CONFIG_H #include"config.h" #endif #include #include #include #include #include #include #include #include #include"tss/tspi.h" #include"common.h" #include"internal.h" extern std::string argv0base; BEGIN_NAMESPACE(); int usage(int rc) { std::cout << PACKAGE_STRING << std::endl << "Usage: " << argv0base << " [ -hq ] -f -s -k \n" << " -f File to verify.\n" << " -s File containing signature.\n" << " -h, --help Show this help text.\n" << " -k File containing key data.\n" << " -q Don't output any messages.\n"; return rc; } END_NAMESPACE(); int wrapped_main(int argc, char **argv) { int c; std::string keyfile; std::string signfile; std::string signaturefile; bool quiet{false}; while (EOF != (c = getopt(argc, argv, "hk:f:s:q"))) { switch (c) { case 'h': return usage(0); case 'k': keyfile = optarg; break; case 'f': signfile = optarg; break; case 's': signaturefile = optarg; break; case 'q': quiet = true; break; default: return usage(1); } } if (optind != argc) { std::cerr << argv0base << ": Extra non-option args not allowed.\n"; return usage(1); } if (keyfile.empty() || signfile.empty() || signaturefile.empty()) { std::cerr << argv0base << ": Need to specify keyfile, data file, and signature file." << std::endl; return usage(1); } const auto key = stpm::parse_keyfile(stpm::slurp_file(keyfile)); const auto to_sign = stpm::slurp_file(signfile); const auto signature = stpm::slurp_file(signaturefile); if (!verify(key, to_sign, signature)) { if (!quiet) { std::cout << "fail\n"; } return 1; } if (!quiet) { std::cout << "success\n"; } return 0; } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/verify_test.cc000066400000000000000000000025031300541321600177340ustar00rootroot00000000000000/** * Copyright 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * TODO: Add more tests. */ #include"config.h" #include #include"gtest/gtest.h" #include"test_util.h" static void reset_getopt() { #if HAVE_DECL_OPTRESET optreset = 1; #endif optind = 1; } extern int wrapped_main(int, char**); TEST(Usage, NoOpts) { CaptureStreams s; reset_getopt(); char *argv[] = { (char*)"sign", NULL, }; EXPECT_EQ(1, wrapped_main(sizeof(argv)/sizeof(void*) - 1, argv)); EXPECT_TRUE(s.stdout().find("\nUsage: ") != std::string::npos); EXPECT_EQ("test-binary: Need to specify keyfile, data file, and signature file.\n", s.stderr()); EXPECT_EQ("", s.stdlog()); } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/src/wrap_main.cc000066400000000000000000000033051300541321600173470ustar00rootroot00000000000000/** * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include"common.h" std::string argv0base = "unknown-binary"; extern int wrapped_main(int argc, char **argv); int main(int argc, char **argv) { argv0base = stpm::xbasename(argv[0]); try { // Support --help without relying on getopt_long. for (int c = 1; c < argc; c++) { if (!strcmp(argv[c], "--help")) { char *a[] = { argv[0], (char*)"-h", NULL, }; return wrapped_main(2, a); } } return wrapped_main(argc, argv); } catch (const stpm::TSPIException& e) { std::cerr << argv0base << ": Exception:\n " << e.what() << std::endl; if (!e.extra().empty()) { std::cerr << e.extra() << std::endl; } } catch (const std::exception& e) { std::cerr << argv0base << ": Exception: " << e.what() << std::endl; } catch (...) { // Shouldn't happen. std::cerr << argv0base << ": Exception of unknown type!\n"; } return 1; } /* ---- Emacs Variables ---- * Local Variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */ simple-tpm-pk11-0.06/testdata/000077500000000000000000000000001300541321600161045ustar00rootroot00000000000000simple-tpm-pk11-0.06/testdata/broken.key000066400000000000000000000000701300541321600200730ustar00rootroot00000000000000# comment here typo 010001 mod 010203 blob 010203040506 simple-tpm-pk11-0.06/testdata/correct.key000066400000000000000000000000671300541321600202620ustar00rootroot00000000000000# comment here exp 010001 mod 010203 blob 010203040506 simple-tpm-pk11-0.06/testdata/pk11.abskeyfile.config000066400000000000000000000000161300541321600221610ustar00rootroot00000000000000key /dev/null simple-tpm-pk11-0.06/testdata/pk11.badkeyfile.config000066400000000000000000000000171300541321600221430ustar00rootroot00000000000000key broken.key simple-tpm-pk11-0.06/testdata/pk11.config000066400000000000000000000000201300541321600200370ustar00rootroot00000000000000key correct.key simple-tpm-pk11-0.06/testdata/pk11.missingkeyfile.config000066400000000000000000000000211300541321600230610ustar00rootroot00000000000000key missing-file simple-tpm-pk11-0.06/testscripts/000077500000000000000000000000001300541321600166625ustar00rootroot00000000000000simple-tpm-pk11-0.06/testscripts/all.sh000077500000000000000000000002231300541321600177660ustar00rootroot00000000000000#!/bin/sh set -e "$(dirname "$0")/simple-key.exp" "$(dirname "$0")/pin-key.exp" echo "========================" echo "=== All tests passed ===" simple-tpm-pk11-0.06/testscripts/pin-key.exp000077500000000000000000000020111300541321600207510ustar00rootroot00000000000000#!/usr/bin/expect -f # # Warning: this script will try the wrong PIN and could trigger dictionary # attack mode in the TPM. # set timeout -1 set tmpdir "test-tmp" spawn "mkdir" "-p" "$tmpdir" expect eof send_user "=== Generating key with PIN ===\n" spawn "./stpm-keygen" "-po" "$tmpdir/pin-key" expect "Enter key PIN: " send "12345678\r" expect "Modulus size: 256" expect "Exponent size: 3" expect "Size: 2048" expect "Blob size: 559" expect eof send_user "=== Signing with key with PIN ===\n" spawn "dd" "if=/dev/urandom" "of=$tmpdir/to-sign" "count=35" "bs=1" expect eof spawn "./stpm-sign" "-k" "$tmpdir/pin-key" "-f" "$tmpdir/to-sign" expect "Enter key PIN: " send "12345678\r" expect "Loaded key:" expect -- "--- Signature ---" expect eof send_user "=== Signing with key with wrong PIN ===\n" spawn "dd" "if=/dev/urandom" "of=$tmpdir/to-sign" "count=35" "bs=1" expect eof spawn "./stpm-sign" "-k" "$tmpdir/pin-key" "-f" "$tmpdir/to-sign" expect "Enter key PIN: " send "87654321\r" expect "Authentication failed" expect eof simple-tpm-pk11-0.06/testscripts/simple-key.exp000077500000000000000000000010401300541321600214550ustar00rootroot00000000000000#!/usr/bin/expect -f set timeout -1 set tmpdir "test-tmp" spawn "mkdir" "-p" "$tmpdir" expect eof send_user "=== Generating key ===\n" spawn "./stpm-keygen" "-o" "$tmpdir/simple-key" expect "Modulus size: 256" expect "Exponent size: 3" expect "Size: 2048" expect "Blob size: 559" expect eof send_user "=== Signing with key ===\n" spawn "dd" "if=/dev/urandom" "of=$tmpdir/to-sign" "count=35" "bs=1" expect eof spawn "./stpm-sign" "-k" "$tmpdir/simple-key" "-f" "$tmpdir/to-sign" expect "Loaded key:" expect -- "--- Signature ---" expect eof