From 9c93865799f6db7a5d19a3342bf06125c2c6042a Mon Sep 17 00:00:00 2001 From: Simon Josefsson Date: Wed, 11 Mar 2009 01:37:07 +0000 Subject: [PATCH] Use YKKSM instead of local secret. Remove more code. --- AES128.php | 961 ---------------------------------------------- common.php | 47 ++- config.php.sample | 1 + verify.php | 13 +- yubikey.php | 219 ----------- 5 files changed, 34 insertions(+), 1207 deletions(-) delete mode 100644 AES128.php delete mode 100644 yubikey.php diff --git a/AES128.php b/AES128.php deleted file mode 100644 index 37aa0bf..0000000 --- a/AES128.php +++ /dev/null @@ -1,961 +0,0 @@ -10 - - var $Nb=4;//Número de columnas en el state (siempre 4) - - var $Nk=4;//Longitud de la clave 128b=>4 - - var $state=array(array()); - - var $shifts_r=array(array(0, 1, 2, 3),array(3, 0, 1, 2),array(2, 3, 0, 1),array(1, 2, 3, 0)); - - var $shifts_l=array(array(0, 1, 2, 3),array(1, 2, 3, 0),array(2, 3, 0, 1),array(3, 0, 1, 2)); - - var $debug=false; - - var $toHex=false; - - - - ////////////////////////////////////////////////////////////////////////////// - - // Inner workings - - ///////////////////////////////////////////////////////////////////////////// - - - - - - /** - - * Realiza una XOR entre S-box y la clave i-esima rk. - - * - - * @param rk Array de caracteres (4x4) con la clave i-esima - - */ - - private function KeyAddition($rk) { - - //Para cada ronda hacemos una XOR entre Sbox[i][j] y rk[round][i][j]. - - for($i = 0; $i < 4; $i++) - - for($j = 0; $j < $this->Nb; $j++) - - $this->state[$i][$j] ^= $rk[$i][$j]; - - } - - - - /** - - * Realiza las funciones SubBytes() y ShiftRow() como se define en fips-198 - - * - - */ - - private function ByteSubShiftRow(){ - - $tmp= array(array()); - - for($i = 0; $i < 4; $i++) - - for($j = 0; $j < $this->Nb; $j++) - - $tmp[$i][$this->shifts_r[$i][$j]]= $this->S[$this->state[$i][$j]]; - - $this->state=$tmp; - - } - - - - /** - - * Realiza las funciones MixColumn() y KeyAddition() como se define en fips-198. - - * - - * @param rk Array de caracteres (4x4) con la clave i-esima - - */ - - private function MixColumnKeyAddition($rk){ - - $b= array(array()); - - for($j = 0; $j < 4; $j++) - - for($i = 0; $i < $this->Nb; $i++){ - - $b[$i][$j] = $this->T2[$this->state[$i][$j]] ^ $this->T3[$this->state[($i + 1) % 4][$j]] ^ $this->state[($i + 2) % 4][$j] ^ $this->state[($i + 3) % 4][$j]; - - $b[$i][$j]^=$rk[$i][$j]; - - } - - $this->state = $b; - - } - - - - /** - - * Realiza la funcion inversa de MixColumn() - - * - - */ - - private function InvMixColumn() { - - $b= array(array()); - - for($j = 0; $j < 4; $j++) - - for($i = 0; $i < $this->Nb; $i++) - - $b[$i][$j] = $this->T14[$this->state[$i][$j]] ^ $this->T11[$this->state[($i + 1) % 4][$j]] ^ $this->T13[$this->state[($i + 2) % 4][$j]] ^ $this->T9[$this->state[($i + 3) % 4][$j]]; - - $this->state = $b; - - } - - - - /** - - * Realiza la funcion inversa de SubBytes() y ShiftRow(). - - * - - */ - - private function InvShiftRowInvByteSub(){ - - $tmp= array(array()); - - for($i = 0; $i < 4; $i++) - - for($j = 0; $j < $this->Nb; $j++) - - $tmp[$i][$this->shifts_l[$i][$j]]= $this->Si[$this->state[$i][$j]]; - - $this->state=$tmp; - - } - - - - - - ///////////////////////////////////////////////////////////////////////// - - // Funciones de cifrado - - ///////////////////////////////////////////////////////////////////////// - - - - /** - - * Constructor class - - * - - * @param hex If true, returns values in hexadecimal - - * @param If true, displays information for debug - - */ - - function __construct($hex=false, $debug=false){ - - $this->debug=$debug; - - $this->toHex=$hex; - } - - - - /** - - * It creates the matrix encryption keys. - - * - - * @param hash: Encryption key length equal to 16 characters - - * @return Matrix keys to encrypt / decrypt. - - */ - - public function makeKey($hash){ - - $rconpocharer = 0; - - $tk=array(array());; - - $rk=array(array(array())); - - for($j = 0; $j < $this->Nk; $j++) - - for($i = 0; $i < 4; $i++) - - $tk[$i][$j] = ord($hash{$j*4+$i})>256 ? ord($hash{$j*4+$i})%256 : ord($hash{$j*4+$i}); - - $t = 0; - - - - for($j = 0; ($j < $this->Nk) && ($t < ($this->Nr+1)*$this->Nb); $j++, $t++) - - for($i = 0; $i < 4; $i++) - - $rk[$t / $this->Nb][$i][$t % $this->Nb] = $tk[$i][$j]; - - while ($t < ($this->Nr+1)*$this->Nb) { - - - - for($i = 0; $i < 4; $i++) - - $tk[$i][0] ^= $this->S[$tk[($i+1)%4][$this->Nk-1]]; - - $tk[0][0] ^= $this->rcon[$rconpocharer++]; - - for($j = 1; $j < $this->Nk; $j++) - - for($i = 0; $i < 4; $i++){ - - $tk[$i][$j] ^= $tk[$i][$j-1]; - - } - - for($j = 0; ($j < $this->Nk) && ($t < ($this->Nr+1)*$this->Nb); $j++, $t++) - - for($i = 0; $i < 4; $i++) { - - $rk[$t / $this->Nb][$i][$t % $this->Nb] = $tk[$i][$j]; - - } - - } - - return $rk; - - } - - - - /** - - * Realiza del cifrado de 1 bloque de datos (128 bits). - - * - - * @param in Datos a cifrar (longitud 128 bits). - - * @param keyin Array de claves de cifrado (generadas con makeKey()) - - * @return La cadena de datos cifrada. - - */ - - public function blockEncrypt($in, $key){ - - - - for ($i=0; $i<4; $i++){ - - for ($j=0; $j<$this->Nb; $j++){ - - $this->state[$j][$i]=ord($in{$i*4+$j}); - - } - - } - - $this->showInt("Cifrado:
State Inicial"); - - $this->KeyAddition($key[0]); - - $this->showInt("Ronda 0 :: KeyAddition"); - - for($r = 1; $r < $this->Nr; $r++) { - - $this->ByteSubShiftRow(); - - $this->showInt("Ronda $r :: ByteSubShiftRow"); - - $this->MixColumnKeyAddition($key[$r]); - - $this->showInt("Ronda $r :: MixColumnKeyAddition"); - - } - - $this->ByteSubShiftRow(); - - $this->showInt("Ronda final :: ByteSubShiftRow"); - - $this->KeyAddition($key[$this->Nr]); - - $this->showInt("Ronda final :: KeyAddition ==> Resultado"); - - $out=""; - - for($i=0; $i<4; $i++) - - for ($j=0; $j<4; $j++) - - $out.=chr($this->state[$j][$i]); - - if($this->toHex){ - - $out=$this->toHexString($out); - - } - - return $out; - - } - - - - /** - - * Realiza el descifrado de 1 bloque de datos (128 bits). - - * - - * @param in Datos a descifrar (longitud 128 bits). - - * @param keyin Array de claves de cifrado (generadas con makeKey()) - - * @return La cadena de datos descifrada. - - */ - - public function blockDecrypt($in, $key) { - - - - for ($i=0; $i<4; $i++){ - - for ($j=0; $j<$this->Nb; $j++){ - - $this->state[$j][$i]=ord($in{$i*4+$j}); - - } - - } - - $this->showInt("Descifrado:
State Inicial"); - - $this->KeyAddition($key[$this->Nr]); - - $this->showInt("Ronda 0 :: KeyAddition"); - - for($r = $this->Nr-1; $r > 0; $r--) { - - $this->InvShiftRowInvByteSub(); - - $this->showInt("Ronda ". ($this->Nr - $r)." :: InvShiftRowInvByteSub"); - - $this->KeyAddition($key[$r]); - - $this->showInt("Ronda ".($this->Nr - $r)." :: KeyAddition"); - - $this->InvMixColumn(); - - $this->showInt("Ronda ".($this->Nr - $r)." :: InvMiColumn"); - - } - - $this->InvShiftRowInvByteSub(); - - $this->showInt("Ronda final :: InvShiftRowInvByteSub"); - - $this->KeyAddition($key[0]); - - $this->showInt("Ronda final :: KeyAddition ==> Resultado"); - - $out=""; - - for($i=0; $i<4; $i++) - - for ($j=0; $j<4; $j++) - - $out.=chr($this->state[$j][$i]); - - if($this->toHex){ - - $out=$this->toHexString($out); - - } - - return $out; - - } - - - // - //////////////////////// ML end - - //////////////////////////////////////////////////////////////////////// - - //Funcion de test - - //////////////////////////////////////////////////////////////////////// - - - - /** - - * Realiza un auto-test. - * - - * - - */ - - public function self_test(){ - - $hash=""; - - $in=""; - - for ($i = 0; $i < 16; $i++){ - - $hash .= chr($i); - } - - $in .= chr(0x00); - $in .= chr(0x11); - $in .= chr(0x22); - $in .= chr(0x33); - $in .= chr(0x44); - $in .= chr(0x55); - $in .= chr(0x66); - $in .= chr(0x77); - $in .= chr(0x88); - $in .= chr(0x99); - $in .= chr(0xaa); - $in .= chr(0xbb); - $in .= chr(0xcc); - $in .= chr(0xdd); - $in .= chr(0xee); - $in .= chr(0xff); - - echo("===============================
"); - - echo("KEYSIZE= 128 bits
"); - - echo("KEY= 0x".$this->toHexString($hash)."
"); - - echo("Plaintext= 0x".$this->toHexString($in)."
"); - - - - $key = $this->makeKey($hash); - - $ct = $this->blockEncrypt($in, $key); - - echo("Ciphertext= 0x".$this->toHexString($ct)."
"); - - - - $cpt = $this->blockDecrypt($ct, $key); - - echo ("Descifrado= 0x".$this->toHexString($cpt)."
"); - - if(!strcmp($cpt,$in)){ - if(!$this->debug){ - - $start=microtime(true); - - for($i=0; $i<1024; $i++){ - - $cpt = $this->blockEncrypt($ct, $key); - - } - - $end=(microtime(true)-$start)*1000; - - echo("Tiempo cifrado 16 KB (1024 bloques): ".$end." ms
"); - } - - echo("Cifrado de 128 bits correcto
"); - - } else echo("Ha fallado el cifrado de 128 bits"); - -} - - - - /////////////////////////////////////////////////////////////////////// - - //Funciones auxiliares - - ////////////////////////////////////////////////////////////////////// - - - - public function toHexString ($sa) { - - $buf=""; - - for ($i=0; $idebug){ - - echo($texto.": "); - - for($i=0; $i<4; $i++){ - - for($j=0; $j<4; $j++){ - - echo(dechex($this->state[$j][$i])." "); - - } - - } - - echo ("
"); - - } - } -} -?> diff --git a/common.php b/common.php index 75f3e10..8b6a2a5 100644 --- a/common.php +++ b/common.php @@ -13,7 +13,6 @@ define('TS_REL_TOLERANCE', 0.3); define('TS_ABS_TOLERANCE', 20); define('DEVICE_ID_LEN', 12); -require_once 'yubikey.php'; require_once 'config.php'; function unescape($s) { @@ -108,30 +107,42 @@ function sign($a, $apiKey, $debug=false) { } // sign an array of query string -function modhexToB64($modhex_str) { - $s = ModHex::Decode($modhex_str); - return base64_encode($s); +function hex2b64 ($hex_str) { + $bin = pack("H*", $hex_str); + return base64_encode($bin); } -function b64ToModhex($b64_str) { - $s = base64_decode($b64_str); - return ModHex::Encode($s); +function modhex2b64 ($modhex_str) { + $hex_str = strtr ($modhex_str, "cbdefghijklnrtuv", "0123456789abcdef"); + return hex2b64($hex_str); } -function b64ToHex($b64_str) { - $s = ''; - $tid = base64_decode($b64_str); - $a = str_split($tid); - for ($i=0; $i < count($a); $i++) { - $s .= dechex(ord($a[$i])); - } - return $s; -} +// decryptOTP using YK-KSM +function decryptOTP($otp) { + global $baseParams; + + $url = $baseParams['__YKKMS_URL__'] . $otp; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_USERAGENT, "YK-VAL"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + + debug($response); + + if (sscanf ($response, + "OK counter=%04x high=%02x low=%04x use=%02x", + $ret["session_counter"], $ret["high"], + $ret["low"], $ret["session_use"]) != 4) { + return false; + } + return $ret; +} // End decryptOTP // $devId: The first 12 chars from the OTP function getAuthData($devId) { - $tokenId = modhexToB64($devId); - $stmt = 'SELECT id, client_id, secret, active, counter, '. + $tokenId = modhex2b64($devId); + $stmt = 'SELECT id, client_id, active, counter, '. 'sessionUse, low, high, accessed FROM yubikeys WHERE active '. 'AND tokenId='.mysql_quote($tokenId); $r = query($stmt); diff --git a/config.php.sample b/config.php.sample index 156464f..4d15c7a 100644 --- a/config.php.sample +++ b/config.php.sample @@ -7,5 +7,6 @@ $baseParams['__DB_HOST__'] = 'localhost'; $baseParams['__DB_USER__'] = 'username'; $baseParams['__DB_PW__'] = 'password'; $baseParams['__DB_NAME__'] = 'yubico'; +$baseParams['__YKKMS_URL__'] = "http://ykkms.example.com/wsapi/decrypt/?otp="; ?> diff --git a/verify.php b/verify.php index 233842c..dc944cb 100644 --- a/verify.php +++ b/verify.php @@ -69,19 +69,14 @@ if ($ad == null) { debug($ad); } -$k = b64ToModhex($ad['secret']); -//debug('aes key in modhex = '.$k); -$key16 = ModHex :: Decode($k); -//debug('aes key in hex = ['.$key16.'], length = '.strlen($key16)); - //// Decode OTP from input // -$otpinfo = Yubikey :: Decode($otp, $key16); -debug($otpinfo); +$otpinfo = decryptOTP($otp); if (!is_array($otpinfo)) { - sendResp(S_BAD_OTP); + sendResp(S_BACKEND_ERROR); exit; } +debug($otpinfo); //// Check the session counter // @@ -118,7 +113,7 @@ query($stmt); //// Check the time stamp // if ($sessionCounter == $seenSessionCounter && $sessionUse > $seenSessionUse) { - $ts = $otpinfo['timestamp']; + $ts = ($otpinfo['high'] << 16) + $otpinfo['low']; $seenTs = ($ad['high'] << 16) + $ad['low']; $tsDiff = $ts - $seenTs; $tsDelta = $tsDiff * TS_SEC; diff --git a/yubikey.php b/yubikey.php deleted file mode 100644 index 21ee8fa..0000000 --- a/yubikey.php +++ /dev/null @@ -1,219 +0,0 @@ -> 4) & 0xf]; - $encoded .= ModHex::$TRANSKEY[ (int)$bin & 0xf]; - } - return $encoded; - } - - // ModHex decodes the string $token. Returns the decoded string if successful, - // or zero if an encoding error was found. - static function decode($token) { - $tokLen = strlen($token); // length of the token - $decoded = ""; // decoded string to be returned - - // strings must have an even length - if ( $tokLen % 2 != 0 ) { return FALSE; } - - for ($i = 0; $i < $tokLen; $i=$i+2 ) { - $high = strpos(ModHex::$TRANSKEY, $token[$i]); - $low = strpos(ModHex::$TRANSKEY, $token[$i+1]); - - // if there's an invalid character in the encoded $token, fail here. - if ( $high === FALSE || $low === FALSE ) - return FALSE; - - $decoded .= chr(($high << 4) | $low); - } - return $decoded; - } - - -} - -/* - * Class Yubikey - * This class does most of the hard work to give you useable data. - * - * Please refer to the documentation for further information on usage. - * - */ - -class Yubikey { - - // Some magic numbers for processing the keys - const OTP_STRING_LENGTH = 32; // # of characters in the encrypted token of the OTP - const UID_SIZE = 12; // # of characters in the private ID - const CRC_OK_RESIDUE = 0xf0b8; - - // Error codes - const ERROR_TOO_SHORT = 1; - const ERROR_BAD_CRC = 2; - const ERROR_MODHEX_FAILED = 3; - - // Decrypts a ModHexed one-time-password $ModHexOTP with the given $key - static function decrypt_otp($ModHexOTP, $key) { - $aes = new AES128(true); - $decoded = ModHex::Decode($ModHexOTP); - if ( $decoded === FALSE ) - return FALSE; - return $aes->blockDecrypt($decoded, $aes->makeKey($key)); - } - - static 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; - } - - static function crc_is_good($token) { - global $trace; - $crc = Yubikey::calculate_crc($token); - if ($trace) echo 'Calculated CRC='.$crc."\n"; - return $crc == Yubikey::CRC_OK_RESIDUE; - } - - /** - * Returns the public ID of the given ModHexed one-time-password - */ - static function get_public_id($otp) { - return substr($otp, 0, strlen($otp) - Yubikey::OTP_STRING_LENGTH); - } - - /** - * Decode - */ - static function decode( $otp, $key ) { - global $trace; - $otpLen = strlen($otp); - - $decoded = array(); // hold our return values - - // if the $otp is longer than 32, assume the extra characters - // are the yubikey's unchanging public ID. If the $otp is too short, - // return immediately. - if ($otpLen > Yubikey::OTP_STRING_LENGTH) { - $decoded["public_id"] = substr($otp, 0, $otpLen - Yubikey::OTP_STRING_LENGTH); - } elseif ( $otpLen < Yubikey::OTP_STRING_LENGTH ) { - if ($trace) echo 'OTP too short'; - return Yubikey::ERROR_TOO_SHORT; - } - - // Decrypt the token (the last 32 characters of the $otp) - $decoded["token"] = Yubikey::decrypt_otp(substr($otp, 0-Yubikey::OTP_STRING_LENGTH), $key); - if ( $decoded["token"] === FALSE ) { - if ($trace) echo 'Decryption failure'; - return Yubikey::ERROR_MODHEX_FAILED; - } - - //// Raw values extracted from the decoded OTP - // - - // Private ID - $start = 0; - $decoded["private_id"] = substr($decoded["token"], $start, Yubikey::UID_SIZE); - $start += Yubikey::UID_SIZE; - - // Session Counter - $scounter = hexdec(substr($decoded["token"], $start+2, 2).substr($decoded["token"], $start, 2)) & 0xffff; - $start += 4; - $decoded['session_counter'] = $scounter; - if ($trace) echo 'Sess ctr='.$scounter."\n"; - - // Time stamp LOW - $timelow = hexdec(substr($decoded["token"], $start+2, 2).substr($decoded["token"], $start, 2)) & 0xffff; - $start += 4; - $decoded["low"] = $timelow; - if ($trace) echo 'TS lo='.$timelow."\n"; - - // Time stamp HIGH - $timehigh = hexdec(substr($decoded["token"], $start, 2)) & 0xff; - $start += 2; - $decoded["high"] = $timehigh; - if ($trace) echo 'TS hi='.$timehigh."\n"; - - // Session Use - $session_use = hexdec(substr($decoded["token"], $start, 2)) & 0xff; - $start += 2; - $decoded["session_use"] = $session_use; - if ($trace) echo 'Use='.$session_use."\n"; - - // Randomness - No need to return this - $decoded["random"] = hexdec(substr($decoded["token"], $start+2, 2).substr($decoded["token"], $start, 2)); - $start += 4; - if ($trace) echo 'Rand='.$decoded["random"]."\n"; - - // CRC - $decoded["crc"] = hexdec(substr($decoded["token"], $start+2, 2).substr($decoded["token"], $start, 2)); - if ($trace) echo 'CRC='.$decoded["crc"]."\n"; - - //// Derived values for convenient use - // - - // Full time stamp - $decoded["timestamp"] = ($timehigh << 16) + $timelow; - if ($trace) echo 'Timestamp='.$decoded["timestamp"]."\n"; - - // Session Counter - $decoded["counter"] = ($scounter<<8) + $session_use; - - if ( Yubikey::crc_is_good($decoded["token"]) ) { - if ($trace) echo "Good CRC\n"; - return $decoded; - } - if ($trace) echo "Bad CRC\n"; - return Yubikey::ERROR_BAD_CRC; - } - -} -?>