Add a function to get the public key
Currently missing inside of this implementation of the Yubikey, is a function that parses out the public key. Commonly used within "two factor authentication".
The first 12 characters of the string according to the Yubico documentation and also known as "prefix" within the data array of Yubico's own php implementation.
With that, dont forget the password. So parsing out the first 12 characters, wont work in every situation.
Isn't the prefix always the same length? I'm not sure what you mean by "password" in this case.
Perfectly understandable, the technical documentation available at Yubico is really sloppy. But the strings are defined as followed within the documentation @ https://www.yubico.com/wp-content/uploads/2012/10/YubiKey-Authentication-Module-Design-Guideline-v1.0.pdf:
3.1 YubiKey One - Factor and Two Factor Authentication
The YubiKey emits a 44 character One Time Password (OTP) of which the first 12 characters is a unique public identifier of the YubiKey itself and the characters that follows is the dynamic part of the OTP. A sample output from a YubiKey where the button has been pressed three times may look like this:
Public ID - OTP fifjgjgkhchbirdrfdnlnghhfgrtnnlgedjlftrbdeut fifjgjgkhchbgefdkbbditfjrlniggevfhenublfnrev fifjgjgkhchblechfkfhiiuunbtnvgihdfiktncvlhck
This fact that the YubiKey emits both a static identifier part and a dynamic ever changing part of the OTP makes it possible to develop combinations of two factor authentication and one factor authentication system.
There is also "Advanced Encryption Standard" option available which is described within 3.3> Then within the git repo of Yubico @ https://github.com/Yubico/yubico-php-client/blob/master/YubiCloud.php, the following parsing script is used:
function parsePasswordOTP($str, $delim = '[:]')
{
if (!preg_match("/^((.*)" . $delim . ")?" .
"(([cbdefghijklnrtuv]{0,16})" .
"([cbdefghijklnrtuv]{32}))$/i",
$str, $matches)) {
/* Dvorak? */
if (!preg_match("/^((.*)" . $delim . ")?" .
"(([jxe\.uidchtnbpygk]{0,16})" .
"([jxe\.uidchtnbpygk]{32}))$/i",
$str, $matches)) {
return false;
} else {
$ret['otp'] = strtr($matches[3], "jxe.uidchtnbpygk", "cbdefghijklnrtuv");
}
} else {
$ret['otp'] = $matches[3];
}
$ret['password'] = $matches[2];
$ret['prefix'] = $matches[4];
$ret['ciphertext'] = $matches[5];
return $ret;
}
Seeing the coding style it doesnt hint on perfection.... But it should give you the full scope.
Without knowing any identifier of the used key the usage of OTP does not make any sense. Anybody with any valid yubico key could auhtorize to a system that should be secured by a second authentication factor.
Not comparing the used OTP public ID with the account data stored by your application opens the door for any account to anybody using any OTP public ID. The result is not having a proper second factor.
There has to be the possibility to not only get an info about the success of the validation operation but the used OTP or better the used public ID. As long as this issue is not solved using the API provided by this repo is dangerous! Especially for devs not really knowing what goes under the hood.
I created a pull request that adds a simple parsing function to extract the ID
https://github.com/enygma/yubikey/pull/22
Actually, it doesn't need to be part of the library, the library consumer can (easily) do this by extracting the 'identifier' part of the OTP provided by the YubiKey, it is a helpful little feature to have though :)
Perfectly true. I suspect that the API in the actual state combined with the imperfect documentation misleads devs to not use it properly. On the other hand an API should provide methods and functionality that mirrors the common use cases. Getting the public ID from the result is definitively a common case if using the mechanism properly.