Skip to content

Commit 7aab215

Browse files
authored
Merge pull request #686 from podlesrafal/tpm-attestation-ecc-fix
fix: fixed issues in TPM attestation for ECC
2 parents b47438c + 923eeca commit 7aab215

File tree

3 files changed

+70
-11
lines changed

3 files changed

+70
-11
lines changed

vertx-auth-webauthn/src/main/java/io/vertx/ext/auth/webauthn/impl/attestation/TPMAttestation.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,21 @@ public AttestationCertificates validate(WebAuthnOptions options, MetaData metada
168168
}
169169
} else if (pubArea.getType() == TPM_ALG_ECC) {
170170
// extract the RSA parameters from the COSE CBOR
171-
byte[] crv = base64UrlDecode(cosePublicKey.getString("-1"));
172-
byte[] x = base64UrlDecode(cosePublicKey.getString("-2"));
173-
byte[] y = base64UrlDecode(cosePublicKey.getString("-3"));
174-
// Do some bit shifting to get to an integer
175-
if (pubArea.getCurveID() != crv[0] + (crv[1] << 8)) {
171+
int crv;
172+
if(cosePublicKey.getValue("-1") instanceof String){
173+
byte[] byteCrv = base64UrlDecode(cosePublicKey.getString("-1"));
174+
// Do some bit shifting to get to an integer
175+
crv = byteCrv[0] + (byteCrv[1] << 8);
176+
}
177+
else{
178+
crv = cosePublicKey.getInteger("-1");
179+
}
180+
if (mapCurveId(pubArea.getCurveID()) != crv) {
176181
throw new AttestationException("Unexpected public key crv");
177182
}
183+
byte[] x = base64UrlDecode(cosePublicKey.getString("-2"));
184+
byte[] y = base64UrlDecode(cosePublicKey.getString("-3"));
185+
178186
// 4. Check that pubArea.unique is set to the same public key,
179187
// as the one in “authData” struct.
180188
if (!MessageDigest.isEqual(pubArea.getUnique(), Buffer.buffer().appendBytes(x).appendBytes(y).getBytes())) {
@@ -410,4 +418,13 @@ public AttestationCertificates validate(WebAuthnOptions options, MetaData metada
410418
throw new AttestationException(e);
411419
}
412420
}
421+
// TPM-2.0-1.83-Part-2-Structures. 6.4 Table10
422+
private int mapCurveId(int curveId){
423+
switch (curveId){
424+
case 3: return 1; //ECC_NIST_P256
425+
case 4: return 2; //ECC_NIST_P384
426+
case 5: return 3; //ECC_NIST_P521
427+
}
428+
return 0;
429+
}
413430
}

vertx-auth-webauthn/src/main/java/io/vertx/ext/auth/webauthn/impl/attestation/tpm/PubArea.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,34 @@ public PubArea(Buffer pubBuffer) {
5353
pos += 2;
5454
exponent = pubBuffer.getUnsignedInt(pos);
5555
pos += 4;
56+
// Slice out unique of dynamic length
57+
len = pubBuffer.getUnsignedShort(pos);
58+
pos += 2;
59+
unique = pubBuffer.getBytes(pos, pos + len);
5660
} else if (type == TPM_ALG_ECC) {
5761
// read 8 bytes
5862
symmetric = pubBuffer.getUnsignedShort(pos);
5963
pos += 2;
6064
scheme = pubBuffer.getUnsignedShort(pos);
6165
pos += 2;
6266
curveID = pubBuffer.getUnsignedShort(pos);
63-
pos += 4;
67+
pos+=2;
6468
kdf = pubBuffer.getUnsignedShort(pos);
6569
pos += 2;
70+
//Unique structure: x length + x data + y length + y data
71+
len = pubBuffer.getUnsignedShort(pos);
72+
pos += 2;
73+
byte[] uniqueX = pubBuffer.getBytes(pos, pos + len);
74+
pos += len;
75+
len = pubBuffer.getUnsignedShort(pos);
76+
pos += 2;
77+
byte[] uniqueY = pubBuffer.getBytes(pos, pos + len);
78+
unique = new byte[uniqueX.length + uniqueY.length];
79+
System.arraycopy(uniqueX, 0, unique, 0, uniqueX.length);
80+
System.arraycopy(uniqueY, 0, unique, uniqueX.length, uniqueY.length);
6681
} else {
6782
throw new IllegalArgumentException("Unexpected type: " + type);
6883
}
69-
70-
// Slice out unique of dynamic length
71-
len = pubBuffer.getUnsignedShort(pos);
72-
pos += 2;
73-
unique = pubBuffer.getBytes(pos, pos + len);
7484
}
7585

7686
public int getType() {

vertx-auth-webauthn/src/test/java/io/vertx/tests/attestation/AttestationTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,38 @@ public void testTPM2Attestation(TestContext should) {
271271
});
272272
}
273273

274+
@Test
275+
public void testTPMAttestationWithECC(TestContext should){
276+
final Async test = should.async();
277+
278+
WebAuthn webAuthN = WebAuthn.create(
279+
rule.vertx(),
280+
new WebAuthnOptions().setRelyingParty(new RelyingParty().setName("FIDO Examples Corporation")))
281+
.authenticatorFetcher(database::fetch)
282+
.authenticatorUpdater(database::store);
283+
284+
JsonObject packedFullAttestationWebAuthnSample = new JsonObject()
285+
.put("rawId", "QgnU71eJSchY96ne1hnJq_pJoweUbVssQ_THlP3MA6I")
286+
.put("id", "QgnU71eJSchY96ne1hnJq_pJoweUbVssQ_THlP3MA6I")
287+
.put("type", "public-key")
288+
.put("response", new JsonObject()
289+
.put("clientDataJSON", "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQ3pGd2k2cVF1TDBJa3ktNURJUksyOEhzRGZTazI4cWhMODVjOThtNFYxaTk5YTllc0RWM2ZjYURuTzRucHF6NVMzTUxadVc1eEp6M2hNdDFhS1lhbGciLCJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ")
290+
.put("attestationObject", "o2NmbXRjdHBtZ2F0dFN0bXSmY2FsZzn__mNzaWdZAQBjFp5484Y4u8jdKDY3o1osD4ha5dpMbpe5gLz7oCg86KxESDgC2DQVsUzVs9guXFIUPvPFBVcV1MxqJRdKLfBUixQhmbDaZK3eYpxGjZzSTiIhQdwnxDT_vQx0yU0Cg2XC28Qe35rdWmnD-7gjcKcXFfw9YGdQPN7uQibB6jWZPtyjj2iKsWA3A5gQGR_WwJ2MwfAzZmVG8PzXn9DtwBAh5tIs8wbz8nTN3ligMqgyvorQcX0Tx1NKE8HDPvg2jqhrXVDf30TpDE90ZZWcabcK3CQ3yaBpxItBj2eaxf5aWDbRJIHPxHB2dzF3RuPGI93gERdWzWNGXIOrrt-eQPsQY3ZlcmMyLjBjeDVjglkFxDCCBcAwggOooAMCAQICEBRtpFnSuE8VlVmSdiGF_98wDQYJKoZIhvcNAQELBQAwQTE_MD0GA1UEAxM2TkNVLVNUTS1LRVlJRC1GQjE3RDcwRDczNDg3MEU5MTlDNEU4RTYwMzk3NUU2NjRFMEU0M0RFMB4XDTI0MDcyNDA3MjIzMVoXDTI3MDYwMzE5NDAzMlowADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOJypYVvKkZHhV1DYAfzwfXrrffdRh9WVNtnn6L9b1MhPVzOHq7FnS-WIoSPqmbG50JdmmUtRkEtgKL6Dp2uHauh_2ahSwbRdVYMHV7emb9oL9zw6h7GBJ9JLuFBOlG50mZmc2S5jBYAvyuwL6NoX5vT3ZBevqdFyeZGIG6wDyg3UZ5Nu1vBIZ_fYLy26QDf-ZSwy7lcyiDxuJ62mTSpFCXNTQM1-5ok1IftKujs_GIDt_VD2RTwJvLvSpShp9tbncXVMGVf4sJMYdqHdy1O7qc6lgeRQbBCrNsIJ4tHKXDU1as6hOcRg8plOMRF9hQaacgxrowUSf9LuqSz5hczOWsCAwEAAaOCAfMwggHvMA4GA1UdDwEB_wQEAwIHgDAMBgNVHRMBAf8EAjAAMG0GA1UdIAEB_wRjMGEwXwYJKwYBBAGCNxUfMFIwUAYIKwYBBQUHAgIwRB5CAFQAQwBQAEEAIAAgAFQAcgB1AHMAdABlAGQAIAAgAFAAbABhAHQAZgBvAHIAbQAgACAASQBkAGUAbgB0AGkAdAB5MBAGA1UdJQQJMAcGBWeBBQgDMFkGA1UdEQEB_wRPME2kSzBJMRYwFAYFZ4EFAgEMC2lkOjUzNTQ0RDIwMRcwFQYFZ4EFAgIMDFNUMzNIVFBIQUhEODEWMBQGBWeBBQIDDAtpZDowMDAxMDEwMjAfBgNVHSMEGDAWgBRaCiuOAq_sWTlHW5htW5P_P2vZ6DAdBgNVHQ4EFgQUqhXWnz_VgS-uAI-2g5Gp56kkZiwwgbIGCCsGAQUFBwEBBIGlMIGiMIGfBggrBgEFBQcwAoaBkmh0dHA6Ly9hemNzcHJvZG5jdWFpa3B1Ymxpc2guYmxvYi5jb3JlLndpbmRvd3MubmV0L25jdS1zdG0ta2V5aWQtZmIxN2Q3MGQ3MzQ4NzBlOTE5YzRlOGU2MDM5NzVlNjY0ZTBlNDNkZS9kYmQ5NjBjOS01NDgyLTQ1MjAtYTY4ZC01ODQ4MWVhMWMwNjguY2VyMA0GCSqGSIb3DQEBCwUAA4ICAQBZT2wQyij5WIF_FPUho5f19Vv0YdXwFtcl4srlD9nd8pw4O9Hn569ghU2YnkHy2oFPMi6riAQ_Ar07Q0G1CNIMJqocF-plPJy5BfyoqmCXzM4C0mKRUbZBXvao3Bag3bXHnfymzU1mr2h_f1fc0rzbWSCsdoeypoQzv57Ule2FgjWKiSa8y5pQsORp2hrM5qz2SbYHAVWsx3bXDEQhwcnD5VAWnmhzqf4GMy4VcuJyPZ9ssoiTKKN1fUoLNAJlgzo5i63vGeOmBfQeSyVv4iWIjrmY8oyVqG-luCAita_BTCaySHOjPGr8oFQpO-zqvf4PVW1O8dNdRwycN6rH0fnvO0cR8S0DZl3DuRDC2zd-UyPWJsiIYFOQjr6FJpRW9qj6nlZKg-0eu7YNP8MMhjQJx3wKGnDPcL45GdbDiojGh71cb3YIUtlr8XVnX9vtxIlNiHFDRYVp0JwMdKO0vnIbwSYzURya4h4jL58H5b38BbrWX3hzkYua0uFS-A8LqSa-xpHE00WaJuFBoMscXWl-QrNtKIwQjQKbF8Hwy0U88kRa4v6uoI0rORAdoeGyZd71QSLzorMaOuCcM5PrnK0G9FO1uQ-Zu-s8GFZFWFOkTB6Mz1UIA_zcdauIq_rK43rB_w-djELSCE-Ivq1MTRH6x4BXQhFGW9tqZdc2JHXVDVkG7zCCBuswggTToAMCAQICEzMAAAU4KrGurbiNtnoAAAAABTgwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNjA0BgNVBAMTLU1pY3Jvc29mdCBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxNDAeFw0yMTA2MDMxOTQwMzJaFw0yNzA2MDMxOTQwMzJaMEExPzA9BgNVBAMTNk5DVS1TVE0tS0VZSUQtRkIxN0Q3MEQ3MzQ4NzBFOTE5QzRFOEU2MDM5NzVFNjY0RTBFNDNERTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMez3a8ZBgGWY2FuNy3dQnWN7FQzCQYsu3ggB1RC_bguziH11BnJY6Qa607SI9IGwNliaSTfwqKiUXvOUD_UYPlXez8yr9lAJOnl-EH6pkmvmhk2bI4ArW5TGZ6948zXj33oEHrQ2mj_aVkWfofGwuD-jpCG2EESuPyFyatnkTvldEqp4EcMj2P0A-S28AYhGZW70oy4oRJoRWX6SEfnnA1kMAsxrbYuI1J76_qAEwPKVDz1-zHl0KM2nMouEjnn2m5O94auzviuN5rYO2kuOUkRBrccYJ3acoFlNzLdrvBdrSQEqMHuvYIk11x5B19xHBF0O3TqSBWcdE-5zeyFhbrLMJvb130NQxiBQQwZ-_oOvCfOvCQ7Zj_sa9bmCb4XamAY0Tss005tnlvr1bP6q22DrsLx0yeQ87iEFh49CG6riYv6SCbDKmeWOYp19anA5xKP6RXaSnouja9BUKWc4MhzbLiv0x7lLPzxuFJvBd6wj8Vwa75hsnn8n_JrN_CvyljAsXmDFeGHhmd1UyfemOAA6C1MeLxGDiIMeOy5bbWPqOGc5i3YxYjaIVPsgwymBhcwglfIemHj2ik8yXjoCk9StaznVUgP62uMALYhPwzdAgUymkvARgAI7DlNpzDodToquPXod6Z0OCZKT8pRUq3bwpxgJeXDUVIuRTpkyqcRAgMBAAGjggGOMIIBijAOBgNVHQ8BAf8EBAMCAoQwGwYDVR0lBBQwEgYJKwYBBAGCNxUkBgVngQUIAzAWBgNVHSAEDzANMAsGCSsGAQQBgjcVHzASBgNVHRMBAf8ECDAGAQH_AgEAMB0GA1UdDgQWBBRaCiuOAq_sWTlHW5htW5P_P2vZ6DAfBgNVHSMEGDAWgBR6jArOL0hiF-KU0a5VwVLscXSkVjBwBgNVHR8EaTBnMGWgY6Bhhl9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUUE0lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDE0LmNybDB9BggrBgEFBQcBAQRxMG8wbQYIKwYBBQUHMAKGYWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVFBNJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAxNC5jcnQwDQYJKoZIhvcNAQELBQADggIBABIqgccy5CUJk5qWtW2E5iZMVuYoMh0AyLai0K3KBF23csWE_NQpnJiUY6wZwW1h9Gr99PURuNUzprDf0lZ77IkTjUderZ3zTw8pgH2qP5UPSk9-o-0ciyZfpsOuywFYdEtcK2ig3ZkESffWZWyqtfX8qMrE0TVHQJ6UapROxiZwUgX_1izil-BxfpNPm3x_lFfktQax6sIQODasSRmd9WDBRaO_3aPTHAHn5tTYC8pI_pRM94J7MUnbRKVXfuEhRynXF7sfVjXuHoDi22sgIQP6mUJ1uH-LxaaAbnv77oQ3tQL5edSG-Mxjj60-lWEVfoTM8FRBbUX7Zs0NTVXWat_ms0ZHbSnFzeO7BQf4EEAnVrfEpj7Vv16V-ZbmVNeqUBoKrEqpVLHkbfD3mTndavPbXt4ojs1UBgdtDpzO6YaUCx5HYAVuPUIbH1UFB0VM_B_yZkkP1lSWLnHZWy1WKTa7shwqLv2-KnrijF4esEH8CbAPwXJ6g8agmibD0XVxbReEtWpBKcUFfo6eFilxrj1m4z4GvQS-BWS1zD1DWyvMotFAdKAHFUXZ9Kmmp6GownqT6c2GOlbVtG64Ws5oU9VsgQMsIxMLtkBqDDE2oVEvyD5gzYbpogC3Fv5MiCPbwvhtSvOZJ431jMbnVh-0fc-NB3311sd2vFGxzBXfGOMOZ3B1YkFyZWFYdgAjAAsABAByACCd_8vzbDg65pn7mGjcbcuJ1xU4hL4oA5IsEkFYv60irgAQABAAAwAQACC_FQYfuRB_d8Ip477iHrFU9bTr42yvVP8c-mFzDj4L6gAgN25UJ9Luw9OK5y3bvh8H8zQTZlxzMCUAlODzMudenftoY2VydEluZm9Yof9UQ0eAFwAiAAs9bWDORZijXjf1k12yjvAmfNwDlPL8eZkYgArYSmwTvQAURFCOcGTLqZlRBR1eTQy2WJDPpAQAAAAAIdPP7p24PiE03sl4Abaadv2IcEGkACIAC2baR9eR-Da6JY8xOmkHxE8YlHbyWAY9c83UcnNkg6pzACIAC8suHiNiE5awWdCquSRqOtTk7xNywpVD9o0SqMWf4VEyaGF1dGhEYXRhWKR0puqSE8mcL3SyJJKzIM9AJiqUwalQoDl_KSULYIQe8EUAAAAACJhwWMrcS4G24TDeUNy-lgAgQgnU71eJSchY96ne1hnJq_pJoweUbVssQ_THlP3MA6KlAQIDJiABIVggvxUGH7kQf3fCKeO-4h6xVPW06-Nsr1T_HPphcw4-C-oiWCA3blQn0u7D04rnLdu-HwfzNBNmXHMwJQCU4PMy516d-w"));
291+
292+
webAuthN.authenticate(
293+
new WebAuthnCredentials()
294+
.setUsername("rafal")
295+
.setOrigin("https://webauthn.io")
296+
.setDomain("webauthn.io")
297+
.setWebauthn(packedFullAttestationWebAuthnSample)
298+
.setChallenge("CzFwi6qQuL0Iky-5DIRK28HsDfSk28qhL85c98m4V1i99a9esDV3fcaDnO4npqz5S3MLZuW5xJz3hMt1aKYalg"))
299+
.onFailure(should::fail)
300+
.onSuccess(user -> {
301+
should.assertNotNull(user);
302+
test.complete();
303+
});
304+
}
305+
274306
@Test
275307
public void testBrokenSafetyNetAttestation(TestContext should) {
276308
final Async test = should.async();

0 commit comments

Comments
 (0)