forked from Yubico/yubikey-ksm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathykksm-gen-keys
executable file
·308 lines (272 loc) · 9.47 KB
/
ykksm-gen-keys
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#!/usr/bin/perl
# Written by Simon Josefsson <[email protected]>.
# Copyright (c) 2009-2013 Yubico AB
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use strict;
use POSIX qw(strftime);
use MIME::Base64;
use DBI;
use POSIX qw(strftime);
my $device = "/dev/random";
sub usage {
print "Usage: ykksm-gen-keys [--verbose] [--help] [--urandom] [--progflags PROGFLAGS] [--pskc] [--nolockpw] [--pub-prefix PREFIX] [--database DBI] [--db-user USER] [--db-passwd PASSWD] START [END]\n";
print "\n";
print "Tool to generate keys on the YKKSM-KEYPROV format.\n";
print "The KSM database can also be populated with this tool.\n";
print "\n";
print "START: Decimal start point.\n";
print " If --database is used, set to 0 to start at the next available serial number.\n";
print "\n";
print "END: Decimal end point, if absent START is used as END.\n";
print " If --database is used and START is set to 0, set this to the COUNT of\n";
print " keys to generate (defaults to 1)\n";
print "\n";
print " --urandom: Use /dev/urandom instead of /dev/random as entropy source.\n";
print "\n";
print " --progflags PROGFLAGS: Add a final personalization configuration string.\n";
print "\n";
print " --pskc: Output keys on the YubiKey PSKC format.\n";
print "\n";
print " --database DBI: Database identifier to insert keys to\n";
print " Use the string USE_CONFIG to refer to your config-db.cfg\n";
print " file instead.\n";
print "\n";
print " --db-user USER: Database username to use, defaults to empty string.\n";
print "\n";
print " --db-passwd PASSWD: Database password to use, defaults to empty string.\n";
print "\n";
print " --no-lockpw: Use a lockpw of all zeros (no lockpw)\n";
print "\n";
print " --pub-prefix PREFIX: A modhex prefix to prepend to the public ID string.\n";
print " This MUST be a modhex string.\n";
print " Note that this prefix + serial number must be 16 or less characters long.\n";
print "\n";
print "Usage example:\n";
print "\n";
print " ./ykksm-gen-keys --urandom 1 10 |\n";
print " gpg -a --sign --encrypt -r 1D2F473E > keys.txt\n";
print "\n";
exit 1;
}
sub hex2modhex {
$_ = shift;
tr/0123456789abcdef/cbdefghijklnrtuv/;
return $_;
}
sub getrand {
my $cnt = shift;
my $buf;
open (FH, $device) or die "Cannot open $device for reading";
read (FH, $buf, $cnt) or die "Cannot read from $device";
close FH;
return $buf;
}
sub gethexrand {
my $cnt = shift;
my $buf = getrand ($cnt);
return lc(unpack("H*", $buf));
}
sub getb64rand {
my $cnt = shift;
my $buf = getrand ($cnt);
return encode_base64($buf, '');
}
# main
if ($#ARGV==-1) {
usage();
}
my $verbose = 0;
my $pskc = 0;
my $progflags;
my $start = "";
my $end = "";
my $nolockpw = "";
my $pubprefix = "";
my $db ="";
our ($dbuser, $dbpass, $basepath, $dbname, $dbserver, $dbport, $dbtype);
while (defined($ARGV[0])) {
my $cmd = shift @ARGV;
if (($cmd eq "-v") || ($cmd eq "--verbose")) {
$verbose = 1;
} elsif (($cmd eq "-h") || ($cmd eq "--help")) {
usage();
} elsif ($cmd eq "--urandom") {
$device = "/dev/urandom";
} elsif ($cmd eq "--progflags") {
$progflags = "," . shift;
} elsif ($cmd eq "--pskc") {
$pskc = 1;
} elsif ($cmd eq "--database") {
$db = shift;
} elsif ($cmd eq "--db-user") {
$dbuser = shift;
} elsif ($cmd eq "--db-passwd") {
$dbpass = shift;
} elsif ($cmd eq "--nolockpw") {
$nolockpw = 1;
} elsif ($cmd eq "--pub-prefix") {
$pubprefix = shift;
} elsif ($cmd =~ m/^[0-9]+/) {
if ($start eq "") {
$start = $cmd;
} elsif ($end eq "") {
$end = $cmd;
} else {
die "Invalid extra argument: $cmd";
}
}
}
die "Missing START parameter, try --help" if ($start eq "");
$end = $start if (!$end);
my $now = strftime "%Y-%m-%dT%H:%M:%S", localtime;
if ($pskc) {
$now .= 'Z';
}
my $ctr;
my $inserth;
my $dbh;
my $creator;
# Yanked from ykksm-import
# read from file if needed
if ($db eq "USE_CONFIG") {
if (-r '/etc/yubico/ksm/config-db.cfg') {
require '/etc/yubico/ksm/config-db.cfg';
} else {
$dbtype = 'mysql';
$dbname = 'ykksm';
}
$db = "dbi:$dbtype:$dbname";
}
# Connect to the DB and prepare to do stuff
if ($db) {
$dbh = DBI->connect($db, $dbuser, $dbpass, {'RaiseError' => 1});
die "Unable to connect to database" unless $dbh;
die "Unable to connect to database: " . $dbh->errstr if $dbh->err;
# because some things might fail, we don't want the db in
# an inconsistent state.
$dbh->begin_work;
# pick the next available serial, if directed to.
if ($start == 0) {
my @row = $dbh->selectrow_array("SELECT max(serialnr), count(*) from yubikeys");
if ($row[0] >= 0 && $row[1] > 0) {
$start = $row[0] + 1;
}
# end is a counter in this case, remember?
$end-- if ($end > 0);
$end = $start + $end;
}
# and get ready to insert stuff
$inserth = $dbh->prepare_cached(qq{
INSERT INTO yubikeys (creator, created, serialnr,
publicname, internalname, aeskey, lockcode)
VALUES (?, ?, ?, ?, ?, ?, ?)
});
# we'll want to record the account running this program for records
if (exists $ENV{'SUDO_USER'}){
$creator = $ENV{'SUDO_USER'};
} else {
$creator = `id -nu`;
chomp $creator;
}
}
if ($pskc) {
print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
print "<KeyContainer Version=\"1.0\"\n";
if ($start == $end) {
print " Id=\"yk-$start-pskc\"\n";
} else {
print " Id=\"yk-$start-to-$end-pskc\"\n";
}
print " xmlns=\"urn:ietf:params:xml:ns:keyprov:pskc\">\n";
} else {
print "# ykksm 1\n";
print "# start $start end $end device $device\n" if ($verbose);
print "# serialnr,identity,internaluid,aeskey,lockpw,created,accessed[,progflags]\n";
}
$ctr = $start;
while ($ctr <= $end) {
my $hexctr = sprintf "%012x", $ctr;
my $modhexctr = hex2modhex($hexctr);
my $internaluid = gethexrand(6);
my $aeskey = $pskc ? getb64rand(16) : gethexrand(16);
my $lockpw = "000000000000";
$lockpw = gethexrand(6) unless ($nolockpw);
# use a public id prefix if provided
my $pubprefixbytes = length($pubprefix);
if ($pubprefixbytes > 0) {
my $rembytes = 16 - $pubprefixbytes;
die "Public prefix is longer than 14 characters" if ($pubprefixbytes > 14);
die "Public prefix is an odd number of characters" if ($pubprefixbytes % 2);
$modhexctr = $pubprefix . hex2modhex(sprintf("%0" . $rembytes . "x", $ctr));
}
if ($db) {
$inserth->execute($creator, $now, $ctr,
$modhexctr, $internaluid,
$aeskey, $lockpw)
or die "Database insert error: " . $dbh->errstr;
}
if ($pskc) {
print " <KeyPackage>\n";
print " <DeviceInfo>\n";
print " <Manufacturer>oath.UB</Manufacturer>\n";
print " <SerialNo>$ctr</SerialNo>\n";
print " <StartDate>$now</StartDate>\n";
print " </DeviceInfo>\n";
print " <CryptoModuleInfo>\n";
print " <Id>1</Id>\n";
print " </CryptoModuleInfo>\n";
print " <Key Id=\"yk-key-$ctr-slot-1\"\n";
print " Algorithm=\"http://www.yubico.com/#yubikey-aes\">\n";
print " <Issuer>Yubico</Issuer>\n";
print " <AlgorithmParameters>\n";
print " <ResponseFormat Encoding=\"ALPHANUMERIC\" Length=\"44\"/>\n";
print " </AlgorithmParameters>\n";
print " <Data>\n";
print " <Secret>\n";
print " <PlainValue>\n";
print " $aeskey\n";
print " </PlainValue>\n";
print " </Secret>\n";
print " </Data>\n";
print " <UserId>CN=$modhexctr, UID=$internaluid</UserId>\n";
print " </Key>\n";
print " </KeyPackage>\n";
} else {
print "# hexctr $hexctr modhexctr $modhexctr\n" if ($verbose);
printf "$ctr,$modhexctr,$internaluid,$aeskey,$lockpw,$now,$progflags\n";
}
$ctr++;
}
if ($db) {
$dbh->commit;
}
if ($pskc) {
print "</KeyContainer>\n";
} else {
print "# the end\n";
}
exit 0;