Yubico / Yubico.NET.SDK

A YubiKey SDK for .NET developers
Apache License 2.0
96 stars 48 forks source link

Possible compatibility issue between this library and libfido2 #61

Closed balazserdeszlogmein closed 1 month ago

balazserdeszlogmein commented 9 months ago

[Note: this has also been posted on the libfido2 github as an issue here.]

Hi,

I have noticed a possible compatibility issue between this library and your C library, libfido2.

I am creating (registering) a U2F credential with the libfido2 command line tool like this on a macOS operating system.

bash

% echo credential challenge | openssl sha256 -binary | base64 > cred_param    
% echo relying_party >> cred_param                   
% echo username >> cred_param                       
% dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param  

then bash:

% fido2-cred -M -u -i cred_param ioreg://4295049894 | fido2-cred -V -o cred

which results in a file called "cred" in the following format:

Hy8EHTL3JjA0UpNavz[...]TiLCi/5WeGnvzHyq8bonq7y0YQ==
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj[...]09K4FOmgk3dHXzSgcA==
-----END PUBLIC KEY-----

Then, we also have a credential assertion (authentication) application, written with this .NET SDK, roughly doing the following on a Windows operating system:

  1. Getting two inputs from the user, private key handle and public key as bas64 strings:

C#

var privateKeyHandle = Convert.FromBase64String(s: PrivateKeyHandleBase64);
var publicKey = Convert.FromBase64String(s: PublicKeyBase64);
  1. Authenticating with these credentials

C#

IYubiKeyDevice yubiKey = YubiKeyDevice.FindAll().FirstOrDefault();

if (yubiKey is null) { return null; }

using (var session = new U2fSession(yubiKey: yubiKey))
{
    byte[] hashOrigin = U2fSession.EncodeAndHashString(data: "relying_party");

    var clientData = new FidoU2fClientData
    {
        Challenge = GenerateChallengeDataBase64(), // Not putting the implementation here, it is unnecessary
        Origin = "relying_party"
    };

    string jsonClientData = JsonConvert.SerializeObject(value: clientData);

    byte[] hashClientData = U2fSession.EncodeAndHashString(data: jsonClientData);

    AuthenticationData authenticationData = session.Authenticate(
        applicationId: hashOrigin,
        clientDataHash: hashClientData,
        keyHandle: privateKeyHandle,
        timeout: TimeSpan.FromSeconds(10)
    );
}

When passing the credential id (private key handle) created on macOS with fido2-cred, calling the last session.Authenticate function throws an error, namely new ArgumentException(ExceptionMessages.UnsupportedAlgorithm); in line 401 of the EcdsaVerify.cs file, because at line 368 in the private static ECDsa ConvertPublicKey(ReadOnlyMemory<byte> encodedEccPoint) the (encodedEccPoint.Span[0] == EncodedPointTag) condition is not met.

Based on this, what we realized after a while, is that if we base64 decode the string of the credential id (key handle), prepend a 04 byte, then base64 encode it back to a string, the above will work and the authentication is going to be successful. Why is there a difference between the U2F implementation for LibFido2 and the .NET SDK?

Let me know if anything else is needed from my side to investigate this issue.

Thanks in advance, Balazs

GregDomzalski commented 9 months ago

Thanks @balazserdeszlogmein for the report.

The "why is there a difference" is pretty easy to answer: The project is maintained by two different teams. 😄 We do our best to compare notes and maintain compatibility - but given that we use different fundamental base libraries (including crypto, json, etc.) some differences like this do crop up.

Right - if memory serves, the 04 denotes the encoding of the elliptic curve coordinate. We'll double check the U2F spec to see what it says. I'm guessing we just missed something on the .NET side. libfido2 is a far more mature implementation, so we typically assume that they are doing the correct thing.

I'll raise this with the team and see if we can get this addressed one way or the other.

DennisDyallo commented 1 month ago

Closing old issues. Feel free to reopen if this is still important to you.