1#!/usr/bin/perl
2use strict;
3use Digest::SHA qw(sha1);
4use Math::BigInt only => 'GMP';
5
6my $s2 = Digest::SHA->new("SHA256");
7
8# The key below is an example; its private key is (obviously) not private. This
9# key must be protected at least as well as the vTPM's secrets, since it can
10# approve the release of these secrets to a new TCB.  It may make sense to
11# modify this script to use a TPM or some other hardware key storage device to
12# hold the private key instead of holding the key in plaintext; such integration
13# is beyond the scope of this example script.
14#
15# The public exponent of this key must be 65537 (0x10001); this is the default
16# for TPM-generated RSA keys.
17#
18# The manage-tpmmgr.pl script expects the modulus of this RSA key to be
19# available; this may be done using:
20#
21# open KEY, '>rsa-modulus-file';
22# print KEY pack 'H*', $rsa_n;
23# close KEY;
24
25my $rsa_n = 'c1580b4ea118a6c2f0a56d5af59b080928a9de7267f824457a1e9d7216013b5a322ff67f72153cd4b58693284490aced3a85d81da909ffe544f934c80340020b5bf514e8850926c6ce3314c3283e33cb79cb6aecf041726782013d07f8171fde4ea8165c6a7050af534ffc1b11ae37ace2ed6436c626edb49bf5bd70ee71f74bf2c132a99e5a6427343dbe46829961755558386436ebea90959161295c78df0127d4e468f9a188b3c1e9b68e5b1e78a450ea437ac7930dab294ede8117f6849d53f11e0bbc8ccef44b7fc9ebd6d7c7532875b3225a9106961771001be618ab3f991ba18edc1b73d73b6b80b5df854f9c9113d0b0cd1fec81a85da3638745fd29';
26my $rsa_d = '3229508daed80173f4114744e111beccf982d0d6a7c8c6484c3da3259535ee9b21083690ac1d7c71c742c9ed1994db7894c562e39716a4106c8ba738f936e310e563b96ff60c00c6757ae53918b8c2a158d100c5c63384a5fc21ac1ee42bc3b5de7c5788d4889d364f8c21e137fe162dc1964b78b682250bc5a6c4e686c6849cf8f0020f6ca383d784e5ffb85da56c2b89dc2e879509b1916c8b51f5907a0dbb7e2f9e5fabc500588ef7db6f78ba4605da86d907493648017ac46a1571ffe9b6a68babeeb277e3a96d346cddc996a94163f1e8393d88f710ff64369a62d3edfc62dbdeae57ee12a33adbb9b9d48d575158117f29fc991cbbbaaa4a47ee974f31';
27
28sub rsa_sign {
29	my $m = '1'.('ff'x218).'003021300906052b0e03021a05000414';
30	$m .= unpack 'H*', sha1(shift);
31	$m = Math::BigInt->from_hex($m);
32	my $n = Math::BigInt->from_hex($rsa_n);
33	my $e = Math::BigInt->from_hex($rsa_d);
34	$m->bmodpow($e, $n);
35	$m = $m->as_hex();
36	$m =~ s/^0x//;
37	$m =~ s/^/0/ while length $m < 512;
38	pack 'H*', $m;
39}
40
41sub auth_update_file {
42	my($dst,$seq) = (shift, shift);
43	my(@plt, @pcrs, @kerns, $cfg);
44	open my $update, '>', $dst or die $!;
45	for (@_) {
46		if (/^([0-9a-fA-F]+)=([0-9a-fA-F]+)$/) {
47			push @pcrs, pack 'V', hex $1;
48			push @plt, pack 'H*', $2;
49		} elsif (/^[0-9a-fA-F]{40}$/) {
50			push @kerns, pack 'H*', $_;
51		} elsif (length $_ == 20) {
52			push @kerns, $_;
53		} else {
54			print "Bad argument: $_";
55			exit 1;
56		}
57	}
58	$cfg = pack 'Q>', $seq;
59	$cfg .= pack 'N/(a20)', @plt;
60	$cfg .= pack 'N/(a20)', @kerns;
61
62	printf "cfg_hash for %s: %s\n", $dst, Digest::SHA::sha1_hex($cfg);
63
64	print $update rsa_sign($cfg);
65	print $update $cfg;
66	print $update map { pack 'n/a3', $_ } @pcrs;
67	close $update;
68}
69
70my $out = shift;
71my $seq = $ENV{SEQ} || time;
72
73if (!$out) {
74	print <<EOF;
75Usage: $0 <output> {<pcrs>=<composite>}* {<kernel>}*
76	<output> is the file that will contain the signed configuration
77	<pcrs> is a 24-bit PCR mask in hexadecimal
78	<composite> is a PCR_COMPOSITE_HASH in hexadecimal
79	<kernel> is a 160-bit vTPM kernel hash in hexadecimal
80
81The sequence number may be specified using the SEQ environment variable,
82otherwise the current UNIX timestamp will be used.  The sequence number of a
83vTPM group must increase on each update.
84
85When the vTPM Manager is compiled without support for a domain builder, the
86SHA-1 hash of the vTPM domain's XSM label is used in place of its kernel hash.
87
88Example:
89	A configuration with two valid command lines and one valid vTPM kernel
90	PCRs 0-7 and 17-19 are being validated (static RTM and TBOOT).
91	$0 auth-0 0e00ff=0593ecb564f532df6ef2f4d7272489da52c4c840 0e00ff=0593ecb564f532df6ef2f4d7272489da52c4c840 2bc65001d506ce6cd12cab90a4a2ad9040d641e1
92EOF
93	exit 0;
94}
95print "Sequence: $seq\n";
96
97auth_update_file $out, $seq, @ARGV;
98