Yubico Forum

...visit our web-store at store.yubico.com
It is currently Tue Jan 30, 2018 12:53 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 13 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Mar 09, 2015 8:35 pm 
Offline

Joined: Tue Oct 21, 2014 7:53 pm
Posts: 13
A couple of sanity check questions regarding implementation of U2F by a replying party.

(1) I have decoded the attestation certificate returned during registration of a Yubikey NEO and found sha256RSA listed as the SignatureAlgorithm. I was expecting ECDSA not RSA. Please confirm which digital signature algorithm is implemented on the Yubikey.

(2) A correct implementation of U2F by a relying party to authenticate users would require the following, (a) persistence of the key handle, public key and attestation certificate created during registration (from window.u2f.register), (b) no error code returned during authentication (from window.u2f.sign), and (c) verify the digital signature returned from the Yubikey sign operation using a crypto library such as Bouncy Castle.

Step (2)(c) is where I am having difficulty. I cannot get _signer.VerifySignature(signature) to return expected boolean results. It should return true if the inputs (public key, bytes to sign, signer algorithm, signature from Yubikey) are correct, otherwise false.

I am using the .NET library published on your developer pages.

Thank you.


Last edited by bbartlett on Thu Mar 12, 2015 10:57 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  

Share On:

Share on Facebook FacebookShare on Twitter TwitterShare on Tumblr TumblrShare on Google+ Google+

PostPosted: Tue Mar 10, 2015 10:50 am 
Offline
Yubico Team
Yubico Team

Joined: Wed Aug 06, 2014 2:40 pm
Posts: 38
bbartlett wrote:
(1) I have decoded the attestation certificate returned during registration of a Yubikey NEO and found sha256RSA listed as the SignatureAlgorithm. I was expecting ECDSA not RSA. Please confirm which digital signature algorithm is implemented on the Yubikey.

All U2F attestation certificates are ECDSA (in accordance with the spec), but they are signed by our CA, which is RSA.


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 10, 2015 11:17 am 
Offline
Yubico Team
Yubico Team

Joined: Wed Aug 06, 2014 2:40 pm
Posts: 38
bbartlett wrote:
I am using the .NET library published on your developer pages.

Just to be clear: We are linking to the .NET library from developers.yubico.com. Yubico is not the publisher of that library.

bbartlett wrote:
(2) A correct implementation of U2F by a relying party to authenticate users would require the following, (a) persistence of the key handle, public key and attestation certificate created during registration (from window.u2f.register)

They key handle and the public key has to be persisted, but usually a U2F library will collect this data in an object (called DeviceRegistration or similar). As a user of a library, you should only have to persist this object. See code example here.

The attestation certificate can, but does not have to, be persisted.

bbartlett wrote:
(b) no error code returned during authentication (from window.u2f.sign)

Well... If an error code is returned by the browser, there will be no signature and thus the next step will fail.

bbartlett wrote:
(c) verify the digital signature returned from the Yubikey sign operation using a crypto library such as Bouncy Castle.

Once again, this should be handled by the U2F library. You should not have to deal with crypto libraries yourself. The only thing you should have to do is something like this:
Code:
u2f_lib.finish_authentication(challenge, device_response, registered_devices)

This will code will throw an exception if the signature was invalid.

I'm not familiar with the .NET library, but it seems like this is the way the demo server of that libarary does it:
Code:
memberShipService.AuthenticateUser(model.UserName.Trim(), model.DeviceResponse.Trim());

(this line returns a boolean instead of throwing an exception)


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 10, 2015 6:12 pm 
Offline

Joined: Tue Oct 21, 2014 7:53 pm
Posts: 13
Thank you for the prompt and thorough reply. I understand the .NET code is not Yubico code. I have been working directly with the .NET developer for several weeks to troubleshoot the calls into the Bouncy Castle library to verify the signature returned from the Yubikey. The .NET code has been ported from your Java solution. We have tried two methods to load the public key, directly and from the attestation certificate. Both attempts fail to produce expected results. We just wanted to make sure that we are using the correct algorithm selection in Bouncy Castle.

There is a bug somewhere in the .NET implementation and we are trying to determine if is the signing algorithm, the public key, the bytes to be signed, or the signature.

The signing algorithm is defined here.
private readonly ISigner _signer = SignerUtilities.GetSigner("SHA-256withECDSA");

The signature comes right out of Yubikey device response JSON object.

The public key is retrieved from the device registration information, and decoded from the byte array encodedPublicKey[]. The DecodePublicKey method is not throwing errors, so we are confident that the public key is loading properly.
private readonly DerObjectIdentifier _curve = SecObjectIdentifiers.SecP256r1;
X9ECParameters curve = SecNamedCurves.GetByOid(_curve);
ECPoint point = curve.Curve.DecodePoint(encodedPublicKey);
ECDomainParameters ecP = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
return new ECPublicKeyParameters(point, ecP);

The bytes to be signed are assembled as follows. We have read the FIDO specification and believe this is correct. 32 bytes the hash of the appid. 1 byte indicating UserPresence 0x01 (binary 1). 4 bytes indicating the counter in big endian format such as 0x0009 (binary 9). 32 bytes the hash of the clientData. This results in 69 bytes. Since this pattern has to match exactly what takes place on the Yubikey to generate the signature, it is most likely where the problem occurs.
byte[] signedBytes = PackBytesToSign(
U2F.Crypto.Hash(Encoding.ASCII.GetBytes(appId)),
UserPresence,
Counter,
U2F.Crypto.Hash(Encoding.ASCII.GetBytes(clientData)));

Let me describe what I perceive the expected results to be. If the 4 values (algorithm, public key, bytes to sign, signature) are correctly assembled, the verification should be true. Changing 1 or more bytes in the signature byte array should return false. We have not been able to achieve these results. If we load the public key directly, the result is always false. If we load the load the X509 certificate, the result is always true.

The fact that the results change depending on how we load the public key with the other 3 values unchanged, indicates that the public key is not being loaded correctly in one of the two methods, but we can't determine which one is incorrect. In the case where the signature verifies true, when we reverse the entire signature array, the result is still true. That is clearly not valid.

I know it is not Yubico's responsibility to fix this, but I am hoping there is another .NET programmer who I might get in contact with through the forum.


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 11, 2015 8:27 am 
Offline
Yubico Team
Yubico Team

Joined: Wed Aug 06, 2014 2:40 pm
Posts: 38
Ok, now I understand the background :)

A few thoughts:

  • Have you seen this new .NET U2F library? Not sure how complete it is, but it might be useful to have a look at the code.
  • In U2F registrations, the data returned from the U2F device is signed by the attestation certificate. In U2F authentications, the data returned from the U2F device is signed by the public key (this public key is
    completely unrelated to the attestation certificate).
  • I extracted some data from SoftKeyTest.shouldAuthenticate() in the Java implementation. Let me know if the data below signs successfully for you as well.
Code:
signedBytes: TJp1zKcX77aMhW8muoJQc39E5pgNMxk8lPoLjpzXYYwBAAAAAepBdB9rg8Ena3vY5hyD09-N3vva-7yGE8LvydFuPTkM
signature: MEUCIQCHR7QtUrcPfPY3UEF_IxmxECKc2Ody8b-M3I1LAahuTgIgWz0Oc_3SovRj1kLflA2wjvox3pb1n1UuhcPD1YDH1FU=
publicKey: BAARhpOKSZZ4RBs_uPUq79rttBCjJMPsmK9Guquc2_ugfZPz60TBqKRLl-2FSYKN-HEEGf7AfMtYGeaMWIDO3jA=
publicKey decoded: EC Public Key
            X: 1186938a499678441b3fb8f52aefdaedb410a324c3ec98af46baab9cdbfba0
            Y: 7d93f3eb44c1a8a44b97ed8549828df8710419fec07ccb5819e68c5880cede30


This is how this data was used:
Code:
System.out.println("signedBytes: " + BaseEncoding.base64Url().encode(signedBytes));
System.out.println("signature: " + BaseEncoding.base64Url().encode(signature));
System.out.println("publicKey: " + BaseEncoding.base64Url().encode(publicKey));
System.out.println("publicKey decoded: " + crypto.decodePublicKey(publicKey));

crypto.checkSignature(
        crypto.decodePublicKey(publicKey),
        signedBytes,
        signature
);


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 11, 2015 11:17 pm 
Offline

Joined: Tue Oct 21, 2014 7:53 pm
Posts: 13
Yes, these values return a true result and if I reverse the signature it returns a false result (Check2 is true and Check3 is false). I also checked the public key x and y values and they matched your values. This indicates the Bouncy Castle code is working properly. We must marshaling the values incorrectly in our code. I also noticed that the signedBytes[33-36] stored the counter in big endian as we expected. Thank you.

Signature = Utils.Base64StringToByteArray("MEUCIQCHR7QtUrcPfPY3UEF_IxmxECKc2Ody8b-M3I1LAahuTgIgWz0Oc_3SovRj1kLflA2wjvox3pb1n1UuhcPD1YDH1FU=");
publicKey = Utils.Base64StringToByteArray("BAARhpOKSZZ4RBs_uPUq79rttBCjJMPsmK9Guquc2_ugfZPz60TBqKRLl-2FSYKN-HEEGf7AfMtYGeaMWIDO3jA=");
signedBytes = Utils.Base64StringToByteArray("TJp1zKcX77aMhW8muoJQc39E5pgNMxk8lPoLjpzXYYwBAAAAAepBdB9rg8Ena3vY5hyD09-N3vva-7yGE8LvydFuPTkM");

bool Check2 = U2F.Crypto.CheckSignature(
U2F.Crypto.DecodePublicKey(publicKey),
signedBytes,
Signature
);

Array.Reverse(Signature);

bool Check3 = U2F.Crypto.CheckSignature(
U2F.Crypto.DecodePublicKey(publicKey),
signedBytes,
Signature
);


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 12, 2015 1:18 am 
Offline

Joined: Tue Oct 21, 2014 7:53 pm
Posts: 13
The FIDO specification for RawMessages Registration section 4.3 states "The signature is to be verified by the relying party using the public key certified in the attestation certificate." Shouldn't the public key in the attestation certificate match the public key returned in the device response?

The following code checks the signature during Registration. pKey1 is the public key from the device RegistrationData bytes 1-65 and pKey2 is the AttestationCertificate public key value. They are not equal (pKeyVerify is false). I try to verify the signature with pKey1 and pKey2 and both results are false. pKey1 is the value that is persisted in the database for the user device and later retrieved for authentication.

public bool CheckSignature(String appId, String clientData)
{
byte[] signedBytes = PackBytesToSign(
U2F.Crypto.Hash(appId),
U2F.Crypto.Hash(clientData),
KeyHandle,
UserPublicKey
);

ICipherParameters pKey1 = U2F.Crypto.DecodePublicKey(UserPublicKey);
ICipherParameters pKey2 = AttestationCertificate.GetPublicKey();
bool pKeyVerify = (pKey1 == pKey2);

bool check1 = U2F.Crypto.CheckSignature(
pKey1,
signedBytes,
Signature
);

bool check2 = U2F.Crypto.CheckSignature(
pKey2,
signedBytes,
Signature
);

return (check1 || check2);
}


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 12, 2015 2:31 am 
Offline

Joined: Tue Oct 21, 2014 7:53 pm
Posts: 13
Here are some values from a registration.

RegistrationData (745 bytes):
BQS4N1NGYUOrziL-FJEBiB4LlbzXoBrf4I9k-ltUspOOYrNDwRtYvPRcyxcxrCuWDvAOK2ngvjuEJgp2ip4nk9HwQIqdQW_lN7zWuWDJvOXri6Q4IVup3QkAsA4NV9c1TuTpfc9VeyEHyhaDTObuau5nlx9eHpGFaiF3qBEp-bc3TNUwggIcMIIBBqADAgECAgQ4Zt91MAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKzEpMCcGA1UEAwwgWXViaWNvIFUyRiBFRSBTZXJpYWwgMTM4MzExNjc4NjEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ3jfx0DHOblHJO09Ujubh2gQZWwT3ob6-uzzjZD1XiyAob_gsw3FOzXefQRblty48r-U-o4LkDFjx_btwuSHtxoxIwEDAOBgorBgEEAYLECgEBBAAwCwYJKoZIhvcNAQELA4IBAQIaR2TKAInPkq24f6hIU45yzD79uzR5KUMEe4IWqTm69METVio0W2FHWXlpeUe85nGqanwGeW7U67G4_WAnGbcd6zz2QumNsdlmb_AebbdPRa95Z8BG1ub_S04JoxQYNLaa8WRlzN7POgqAnAqkmnsZQ_W9Tj2uO9zP3mpxOkkmnqz7P5zt4Lp5xrv7p15hGOIPD5V-ph7tUmiCJsq0LfeRA36X7aXi32Ap0rt_wyfnRef59YYr7SmwaMuXKjbIZSLesscZZTMzXd-uuLb6DbUCasqEVBkGGqTRfAcOmPov1nHUrNDCkOR0obR4PsJG4PiamIfApNeoXGYpGbok6nucMEQCIHA2eDMSEyuCBPKfBD5PkZROH_qvk8_57WNIV3rzdMupAiA8f3mPVHvO-DWh5xwl-6zZrTHBtadEBlxdogCvkWCiQw


Header (1 byte): 0x05

Public Key (65 bytes):
BLg3U0ZhQ6vOIv4UkQGIHguVvNegGt_gj2T6W1Syk45is0PBG1i89FzLFzGsK5YO8A4raeC-O4QmCnaKnieT0fA

Key handle length (1 byte): 0x40 (64)

Key handle (64 bytes):
ip1Bb-U3vNa5YMm85euLpDghW6ndCQCwDg1X1zVO5Ol9z1V7IQfKFoNM5u5q7meXH14ekYVqIXeoESn5tzdM1Q

X509 certificate (544 bytes):

Signature (70 bytes):
MEQCIHA2eDMSEyuCBPKfBD5PkZROH_qvk8_57WNIV3rzdMupAiA8f3mPVHvO-DWh5xwl-6zZrTHBtadEBlxdogCvkWCiQw

1+65+1+64+544+70 = 745

Verify Register Signature

BytesToSign (194 bytes):
1 byte 0x00

32 bytes hash(appId)
http://localhost:52701

32 bytes hash(clientdata):
{\"typ\":\"navigator.id.finishEnrollment\",\"challenge\":\"2ScxHgONm6HWi-69dl-u8MHJStWczxjyqVYc1Jywnyc\",\"origin\":\"http://localhost:52701\",\"cid_pubkey\":\"\"}

64 bytes key handle:
ip1Bb-U3vNa5YMm85euLpDghW6ndCQCwDg1X1zVO5Ol9z1V7IQfKFoNM5u5q7meXH14ekYVqIXeoESn5tzdM1Q

65 bytes public key:
BLg3U0ZhQ6vOIv4UkQGIHguVvNegGt_gj2T6W1Syk45is0PBG1i89FzLFzGsK5YO8A4raeC-O4QmCnaKnieT0fA

BytesToSign (194 bytes):
ABDSG0CFmfWR62OPK5J72_z4x0ukRjfsJHCjP59IIwh8zGCun1fxQS1HuBc1lXcbueGEoLZ2ToBbDJUUnzvCbwCKnUFv5Te81rlgybzl64ukOCFbqd0JALAODVfXNU7k6X3PVXshB8oWg0zm7mruZ5cfXh6RhWohd6gRKfm3N0zVBLg3U0ZhQ6vOIv4UkQGIHguVvNegGt_gj2T6W1Syk45is0PBG1i89FzLFzGsK5YO8A4raeC-O4QmCnaKnieT0fA


VerifySignature fails!!


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 12, 2015 1:33 pm 
Offline
Yubico Team
Yubico Team

Joined: Wed Aug 06, 2014 2:40 pm
Posts: 38
I fed the Java library with the data you posted above. It verifies the signature successfully. KeyHandle and publicKey are the same but not bytesToSign:

Code:
// bytesToSign from the .NET library:
ABDSG0CFmfWR62OPK5J72_z4x0ukRjfsJHCjP59IIwh8zGCun1fxQS1HuBc1lXcbueGEoLZ2ToBbDJUUnzvCbwC
KnUFv5Te81rlgybzl64ukOCFbqd0JALAODVfXNU7k6X3PVXshB8oWg0zm7mruZ5cfXh6RhWohd6gRKfm3N0zVBLg3U0ZhQ6vOIv4UkQGIHguVvNegGt_gj2T6W1Syk45is0PBG1i89FzLFzGsK5YO8A4raeC-O4QmCnaKnieT0fA

// bytesToSign from the Java library:
ANZ1LeIhomdsUMHDU4Qb4PY4BpyEjPsNyt7HvXLKEp-7LBQ8iUMFH6ZfAucUwHa0rwO23Uwnzcdp5bWNQ3T57Xe
KnUFv5Te81rlgybzl64ukOCFbqd0JALAODVfXNU7k6X3PVXshB8oWg0zm7mruZ5cfXh6RhWohd6gRKfm3N0zVBLg3U0ZhQ6vOIv4UkQGIHguVvNegGt_gj2T6W1Syk45is0PBG1i89FzLFzGsK5YO8A4raeC-O4QmCnaKnieT0fA

I added line breaks to highlight which parts that differs.


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 12, 2015 1:39 pm 
Offline
Yubico Team
Yubico Team

Joined: Wed Aug 06, 2014 2:40 pm
Posts: 38
bbartlett wrote:
32 bytes hash(appId)
http://localhost:52701

32 bytes hash(clientdata):
{\"typ\":\"navigator.id.finishEnrollment\",\"challenge\":\"2ScxHgONm6HWi-69dl-u8MHJStWczxjyqVYc1Jywnyc\",\"origin\":\"http://localhost:52701\",\"cid_pubkey\":\"\"}

Are you not hashing these values (you have to)? Please give me the hash of them. Judging from where the difference occurs in bytesToSign, I suspect that these two values are the problem. Maybe brucedog/u2flib's BouncyCastleCrypto.Hash(string) does not output the same as yubico/java-u2flib-server's BouncyCastleCrypto.hash(String).

The correct hashes of the strings you posted are:
Code:
appIdHash: 1nUt4iGiZ2xQwcNThBvg9jgGnISM-w3K3se9csoSn7s
clientDataHash: LBQ8iUMFH6ZfAucUwHa0rwO23Uwnzcdp5bWNQ3T57Xc


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 13 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group