redDwarf
redDwarf

Reputation: 398

OTP Validation with Yubikey - issue with HMAC-SHA1 signature

I'm meeting an understanding issue with OTP validation for Yubikey I'm trying to develop in flutter the Validation Protocol V2 (https://developers.yubico.com/OTP/Specifications/OTP_validation_protocol.html)

I don't understand how you obtain the signature I used the vector test from https://developers.yubico.com/OTP/Specifications/Test_vectors.html

my code is

String apiKey = 'mG5be6ZJU1qBGz24yPh/ESM3UdU=';
String keyValue = 'id=1&nonce=jrFwbaYFhn0HoxZIsd9LQ6w2ceU&otp=vvungrrdhvtklknvrtvuvbbkeidikkvgglrvdgrfcdft';
var hmacSha1 = crypto.Hmac(crypto.sha1, apiKey.codeUnits);
crypto.Digest sha1Result = hmacSha1.convert(keyValue.codeUnits);
String hEncode64 = base64.encode(sha1Result.bytes);
final http.Response responseHttp = await http.get(
        Uri.parse('https://api.yubico.com/wsapi/2.0/verify?' +
            keyValue +
            '&h=' +
            hEncode64),
);

I don't find the same result as Vector Test (h=%2Bja8S3IjbX593/LAgTBixwPNGX4%3D). -> +ja8S3IjbX593/LAgTBixwPNGX4= But my misunderstanding thing is : when I try to base64 decode your vector test, I can't because it is not utf8 format

and when i try with the test vector available on site, it doesn,'t work

https://api.yubico.com/wsapi/2.0/verify?id=1&otp=vvungrrdhvtklknvrtvuvbbkeidikkvgglrvdgrfcdft&nonce=jrFwbaYFhn0HoxZIsd9LQ6w2ceU&h=%2Bja8S3IjbX593/LAgTBixwPNGX4%3D
h=JE5WcMcXV7vooWkeN2/7A4DpMFo=
t=2021-12-15T12:51:37Z0635
status=BAD_SIGNATURE

Could you help me to understand please my pb

Upvotes: 2

Views: 394

Answers (1)

Mihai Galos
Mihai Galos

Reputation: 1897

TL;DR: See this bash implementation.

I guess you finally managed to get it running with Dart, but the API can even be curled.

I'm posting this here for future reference for whoever encounters the BAD_SIGNATURE with yubikey in bash.

To correctly generate the HMAC SHA1 with openssl, I had to:

  • Take the secret key generated when registering a key for an app (i.e.: mG5be6ZJU1qBGz24yPh/ESM3UdU= in the OP's post) and decode it from base64. This generates raw binary data.
  • Convert the raw binary data to a hex string via od.
  • Convert the hex string to uppercase via sed.
  • Feed everything into openssl to generate the HMAC SHA1 result.
  • Reinterpret the openssl result as binary via xxd.
  • Reencode it to base64.

This finally generated the correct signature.

I've published the steps in the TL;DR in the beginning.

Here's a snapshot:

#!/bin/bash
echo -n "YubiKey token: "
read token
id=$(cat creds | head -1 | cut -d':' -f2)
secret_key=$(cat creds | tail -1 | cut -d':' -f2 | base64 -d | od -A n -v -t x1 | tr -d ' \n')
nonce=$(echo -n $RANDOM | sha256sum | head -c 40 | cut -d ' ' -f1)
challenge="id=$id&nonce=$nonce&otp=$token&sl=100&timeout=8"

signature=$(echo -n $challenge | openssl dgst -sha1 -mac HMAC -macopt hexkey:$(echo -n "$secret_key") | sed 's/^(stdin)= //' | sed -E 's/([[:lower:]])|([[:upper:]])/\U\1\U\2/g'| xxd -r -p | base64)
response=$(curl --silent "https://api.yubico.com/wsapi/2.0/verify?$challenge&h=$signature" | tr '\r' '\n')

echo $response

This uses a file called creds (obtained by registering an API key here), which contains the following:

Client ID:XXXXX
Secret key:XXXXXXXXXXXXXXXX

Upvotes: 1

Related Questions