1
0
mirror of https://github.com/Yubico/yubikey-ksm.git synced 2025-03-16 03:29:20 +01:00

Commit initial version.

This commit is contained in:
Simon Josefsson 2009-02-24 21:27:28 +00:00
parent ee046e6611
commit fbd6ac6b33
7 changed files with 508 additions and 0 deletions

3
TODO Normal file
View File

@ -0,0 +1,3 @@
- Do we really need the 'accessed' column? It leads to database
writes, and it seems this information could be stored on the
validation server anyway.

6
ykksm-config.php Normal file
View File

@ -0,0 +1,6 @@
<?php
$dbhost = "localhost";
$dbname = "ykksm";
$dbuser = "ykksmreader";
$dbpasswd = "password";
?>

38
ykksm-db.sql Normal file
View File

@ -0,0 +1,38 @@
drop database ykksm;
create database ykksm;
use ykksm;
create table yubikeys (
id int not null auto_increment,
-- identities:
serialNr int not null,
publicName varchar(16) unique not null,
-- timestamps:
created datetime not null,
accessed datetime,
-- the data:
internalName varchar(12) not null,
aesKey varchar(32) not null,
lockCode varchar(12) not null,
-- key creator, typically pgp key id of key generator
creator varchar(8) not null,
-- various flags:
active boolean default true,
hardware boolean default true,
primary key (id)
-- MySQL generates an index automatically for columns marked 'unique'
-- if this doesn't seem to happen, add: 'key (publicName)'
-- use, e.g., explain select * from yubikeys where publicName = 'foo';
-- and make sure it uses a key to find the column.
);
drop user ykksmreader;
create user ykksmreader;
grant select, update(accessed) on ykksm.yubikeys to 'ykksmreader'@'localhost';
flush privileges;

115
ykksm-decrypt.php Normal file
View File

@ -0,0 +1,115 @@
<?php
# Written by Simon Josefsson <simon@josefsson.org>.
# Copyright (c) 2009 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.
include 'ykksm-config.php';
include 'ykksm-utils.php';
openlog("ykksm", LOG_PID, LOG_AUTHPRIV)
or die("ERR Syslog open error\n");
$otp = $_REQUEST["otp"];
if (!$otp) {
syslog(LOG_INFO, "No OTP provided");
die("ERR No OTP provided\n");
}
if (!preg_match("/^([cbdefghijklnrtuv]{0,16})([cbdefghijklnrtuv]{32})$/",
$otp, $matches)) {
syslog(LOG_INFO, "Invalid OTP format: $otp");
die("ERR Invalid OTP format\n");
}
$id = $matches[1];
$modhex_ciphertext = $matches[2];
$dbconn = mysql_connect($dbhost, $dbuser, $dbpasswd);
if (!$dbconn) {
syslog(LOG_ERR, "Database connect error: " . mysql_error());
die("ERR Database error\n");
}
$db_selected = mysql_select_db($dbname);
if (!$db_selected) {
syslog(LOG_ERR, "Database select error: " . mysql_error());
die("ERR Database error\n");
}
$sql = "SELECT aesKey, internalName FROM yubikeys " .
"WHERE publicName = '$id' AND active";
$result = mysql_query($sql);
if (!$result) {
syslog(LOG_ERR, "Database query error: " . mysql_error());
die("ERR Database error\n");
}
if (mysql_num_rows($result) != 1) {
syslog(LOG_INFO, "Unknown yubikey: " . $otp);
die("ERR Unknown yubikey\n");
}
$row = mysql_fetch_assoc($result);
$aesKey = $row['aesKey'];
$internalName = $row['internalName'];
$ciphertext = modhex2hex($modhex_ciphertext);
$plaintext = aes128ecb_decrypt($aesKey, $ciphertext);
syslog(LOG_INFO, "OTP $otp OUT $plaintext")
or die("ERR Log error\n");
if (!crc_is_good($plaintext)) {
syslog(LOG_ERR, "CRC error: $otp");
die("ERR Corrupt OTP\n");
}
if (strcmp(substr($plaintext, 0, 12), $internalName) != 0) {
syslog(LOG_ERR, "Internal name mismatch: $otp");
die("ERR Corrupt OTP\n");;
}
$sql = "UPDATE yubikeys SET accessed = NOW() " .
"WHERE publicName = '$id'";
$result = mysql_query($sql);
if (!$result) {
syslog(LOG_ERR, "Database update error: " . mysql_error());
die("ERR Database error\n");
}
# Mask out interesting fields
$counter = substr($plaintext, 14, 2) . substr($plaintext, 12, 2);
$tstamphi = substr($plaintext, 18, 2) . substr($plaintext, 16, 2);
$tstamplo = substr($plaintext, 20, 2);
$sessionuse = substr($plaintext, 22, 2);
print "OK counter=$counter tstamphi=$tstamphi tstamplo=$tstamplo sessionuse=$sessionuse\n";
mysql_close()
or syslog(LOG_ERR, "Database close error (otp $otp): " . mysql_error());
?>

109
ykksm-gen-keys.pl Executable file
View File

@ -0,0 +1,109 @@
#!/usr/bin/perl
# Written by Simon Josefsson <simon@josefsson.org>.
# Copyright (c) 2009 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);
sub usage {
print "Usage: $0 [--verbose] [--help] [--urandom] START [END]\n";
print "\n";
print " Tool to generate keys on the YKKSM-KEYPROV format.\n";
print "\n";
print " START: Decimal start point.\n";
print "\n";
print " END: Decimal end point, if absent START is used as END.\n";
print "\n";
print "Usage example:\n";
print "\n";
print " ./ykksm-gen-keys.pl --urandom 1 10 |\n";
print " gpg -a --encrypt -r 1D2F473E > keys.txt\n";
print "\n";
exit 1;
}
if ($#ARGV==-1) {
usage();
}
my $verbose = 0;
my $device = "/dev/random";
while ($ARGV[0] =~ m/^-(.*)/) {
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";
}
}
sub hex2modhex {
my $_ = shift;
tr/0123456789abcdef/cbdefghijklnrtuv/;
return $_;
}
sub gethexrand {
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 lc(unpack("H*", $buf));
}
# main
my $now = strftime "%Y-%m-%dT%H:%M:%S", localtime;
my $start = shift @ARGV;
my $end = shift @ARGV || $start;
my $ctr;
print "# ykksm 1\n";
print "# start $start end $end device $device\n" if ($verbose);
print "# serialnr,identity,internaluid,aeskey,lockpw,created,accessed,flags\n";
$ctr = $start;
while ($ctr <= $end) {
my $hexctr = sprintf "%012x", $ctr;
my $modhexctr = hex2modhex($hexctr);
my $internaluid = gethexrand(6);
my $aeskey = gethexrand(16);
my $lockpw = gethexrand(6);
print "# hexctr $hexctr modhexctr $modhexctr\n" if ($verbose);
printf "$ctr,$modhexctr,$internaluid,$aeskey,$lockpw,$now,,0\n";
$ctr++;
}
exit 0;

159
ykksm-import.pl Executable file
View File

@ -0,0 +1,159 @@
#!/usr/bin/perl
# Written by Simon Josefsson <simon@josefsson.org>.
# Copyright (c) 2009 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 DBI;
use POSIX qw(strftime);
sub usage {
print "Usage: $0 [--verbose] [--help]\n";
print " [--database DBI] [--db-user USER] [--db-passwd PASSWD]\n";
print " [--creator CREATOR]\n";
print "\n";
print " Tool to import key data on the YKKSM-KEYPROV format.\n";
print "\n";
print " DBI: Database identifier, see http://dbi.perl.org/\n";
print " defaults to a MySQL database ykksm on localhost,.";
print " i.e., DBI::mysql:ykksm.";
print "\n";
print " USER: Database username to use, defaults to empty string.";
print "\n";
print " PASSWD: Database password to use, defaults to empty string.";
print "\n";
print " CREATOR: Short string with creator info.\n";
print " Defaults to using the PGP signer key id, normally.\n";
print " you don't change this.\n";
print "\n";
print "Usage example:\n";
print "\n";
print " ./ykksm-import.pl < keys.txt\n";
print "\n";
exit 1;
}
my $verbose = 0;
my $creator;
my $db = "dbi:mysql:ykksm";
my $dbuser;
my $dbpasswd;
while ($ARGV[0] =~ m/^-(.*)/) {
my $cmd = shift @ARGV;
if (($cmd eq "-v") || ($cmd eq "--verbose")) {
$verbose = 1;
} elsif (($cmd eq "-h") || ($cmd eq "--help")) {
usage();
} elsif ($cmd eq "--creator") {
$creator = shift;
} elsif ($cmd eq "--database") {
$db = shift;
} elsif ($cmd eq "--db-user") {
$dbuser = shift;
} elsif ($cmd eq "--db-passwd") {
$dbpasswd = shift;
}
}
if ($#ARGV>=0) {
usage();
}
my $infilename = "tmp.$$";
my $verify_status;
my $encrypted_to;
my $signed_by;
# Read input into temporary file.
open TMPFILE, ">$infilename"
or die "Cannot open $infilename for writing";
while (<>) {
print TMPFILE $_;
}
close TMPFILE;
# Get status
open(GPGV, "gpg --status-fd 1 --output /dev/null < $infilename 2>&1 |")
or die "Cannot launch gpg";
while (<GPGV>) {
$verify_status .= $_;
$encrypted_to = $1 if m,^\[GNUPG:\] ENC_TO ([0-9A-F]+) ,;
$signed_by = $1 if m,^\[GNUPG:\] VALIDSIG ([0-9A-F]+) ,;
}
close GPGV;
print "Verification output:\n" . $verify_status;
print "encrypted to: " . $encrypted_to . "\n";
print "signed by: " . $signed_by . "\n";
die "Input not signed?" if !$signed_by;
my $dbh = DBI->connect($db, $dbuser, $dbpasswd, {'RaiseError' => 1});
my $inserth = $dbh->prepare_cached(qq{
INSERT INTO yubikeys (creator, created, accessed, serialNr,
publicName, internalName, aesKey, lockCode)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
});
my $now = strftime "%Y-%m-%dT%H:%M:%S", localtime;
$creator = $signed_by if !$creator;
open(GPGV, "gpg < $infilename 2>/dev/null |")
or die "Cannot launch gpg";
while (<GPGV>) {
next if m:^#:;
my ($serialNr, $publicName, $internalName, $aesKey,
$lockCode, $created, $accessed) =
m%^([0-9]+),([cbdefghijklnrtuv]+),([0-9a-f]+),([0-9a-f]+),([0-9a-f]+),([T:0-9 -]*),([T:0-9 -]*),0%;
if ($verbose) {
print "line: $_";
}
print "\tserialnr $serialNr publicName $publicName " .
"internalName $internalName aesKey $aesKey lockCode $lockCode " .
"created $created accessed $accessed eol";
if ($verbose) {
print "\n";
} else {
print "\r";
}
$created = $now if !$created;
$accessed = "NULL" if !$accessed;
$inserth->execute($creator, $created, $accessed, $serialNr,
$publicName, $internalName,
$aesKey, $lockCode)
or die "Database insert error: " . $dbh->errstr;
}
print "\n";
unlink $infilename;
close GPGV;
$dbh->disconnect();
exit 0;

78
ykksm-utils.php Normal file
View File

@ -0,0 +1,78 @@
<?php
# Written by Simon Josefsson <simon@josefsson.org>.
# Copyright (c) 2009 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.
function hex2bin($h)
{
if (!is_string($h)) return null;
$r='';
for ($a=0; $a<strlen($h); $a+=2) {
$r.=chr(hexdec($h{$a}.$h{($a+1)}));
}
return $r;
}
function modhex2hex($m)
{
return strtr ($m, "cbdefghijklnrtuv", "0123456789abcdef");
}
function aes128ecb_decrypt($key,$in)
{
return bin2hex(mcrypt_ecb(MCRYPT_RIJNDAEL_128,
hex2bin($key),
hex2bin($in),
MCRYPT_DECRYPT,
hex2bin('00000000000000000000000000000000')));
}
function calculate_crc($token)
{
$crc = 0xffff;
for ($i = 0; $i < 16; $i++ ) {
$b = hexdec($token[$i*2].$token[($i*2)+1]);
$crc = $crc ^ ($b & 0xff);
for ($j = 0; $j < 8; $j++) {
$n = $crc & 1;
$crc = $crc >> 1;
if ($n != 0) {
$crc = $crc ^ 0x8408;
}
}
}
return $crc;
}
function crc_is_good($token) {
$crc = calculate_crc($token);
return $crc == 0xf0b8;
}
?>