Skip to content

Commit e573f14

Browse files
committed
Add support for ES384 and ES512 algorithms
1 parent 47c9841 commit e573f14

File tree

8 files changed

+103
-13
lines changed

8 files changed

+103
-13
lines changed

webauthn-server-core/src/main/java/com/yubico/webauthn/RelyingParty.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,13 @@ public class RelyingParty {
210210
* <p>This is a list of acceptable public key algorithms and their parameters, ordered from most
211211
* to least preferred.
212212
*
213-
* <p>The default is the following list:
213+
* <p>The default is the following list, in order:
214214
*
215215
* <ol>
216216
* <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#ES256 ES256}
217217
* <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#EdDSA EdDSA}
218+
* <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#ES256 ES384}
219+
* <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#ES256 ES512}
218220
* <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#RS256 RS256}
219221
* </ol>
220222
*
@@ -228,6 +230,8 @@ public class RelyingParty {
228230
Arrays.asList(
229231
PublicKeyCredentialParameters.ES256,
230232
PublicKeyCredentialParameters.EdDSA,
233+
PublicKeyCredentialParameters.ES384,
234+
PublicKeyCredentialParameters.ES512,
231235
PublicKeyCredentialParameters.RS256));
232236

233237
/**
@@ -417,6 +421,8 @@ private static List<PublicKeyCredentialParameters> filterAvailableAlgorithms(
417421
break;
418422

419423
case ES256:
424+
case ES384:
425+
case ES512:
420426
KeyFactory.getInstance("EC");
421427
break;
422428

webauthn-server-core/src/main/java/com/yubico/webauthn/TpmAttestationStatementVerifier.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,14 @@ private void validateCertInfo(
216216
expectedExtraData = Crypto.sha256(attToBeSigned);
217217
break;
218218

219+
case ES384:
220+
expectedExtraData = Crypto.sha384(attToBeSigned);
221+
break;
222+
223+
case ES512:
224+
expectedExtraData = Crypto.sha512(attToBeSigned);
225+
break;
226+
219227
case RS1:
220228
try {
221229
expectedExtraData = Crypto.sha1(attToBeSigned);
@@ -312,6 +320,14 @@ private void verifyPublicKeysMatch(AttestationObject attestationObject, TpmtPubl
312320
tpmAlgId = COSEAlgorithmIdentifier.ES256;
313321
break;
314322

323+
case TpmEccCurve.NIST_P384:
324+
tpmAlgId = COSEAlgorithmIdentifier.ES384;
325+
break;
326+
327+
case TpmEccCurve.NIST_P521:
328+
tpmAlgId = COSEAlgorithmIdentifier.ES512;
329+
break;
330+
315331
default:
316332
throw new UnsupportedOperationException(
317333
"Unsupported elliptic curve: " + params.curve_id);

webauthn-server-core/src/main/java/com/yubico/webauthn/WebAuthnCodecs.java

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,39 +50,73 @@ final class WebAuthnCodecs {
5050
new ByteArray(new byte[] {0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70});
5151

5252
static ByteArray ecPublicKeyToRaw(ECPublicKey key) {
53+
54+
final int fieldSizeBytes =
55+
Math.toIntExact(
56+
Math.round(Math.ceil(key.getParams().getCurve().getField().getFieldSize() / 8.0)));
5357
byte[] x = key.getW().getAffineX().toByteArray();
5458
byte[] y = key.getW().getAffineY().toByteArray();
55-
byte[] xPadding = new byte[Math.max(0, 32 - x.length)];
56-
byte[] yPadding = new byte[Math.max(0, 32 - y.length)];
59+
byte[] xPadding = new byte[Math.max(0, fieldSizeBytes - x.length)];
60+
byte[] yPadding = new byte[Math.max(0, fieldSizeBytes - y.length)];
5761

5862
Arrays.fill(xPadding, (byte) 0);
5963
Arrays.fill(yPadding, (byte) 0);
6064

6165
return new ByteArray(
6266
Bytes.concat(
6367
new byte[] {0x04},
64-
Bytes.concat(xPadding, Arrays.copyOfRange(x, Math.max(0, x.length - 32), x.length)),
65-
Bytes.concat(yPadding, Arrays.copyOfRange(y, Math.max(0, y.length - 32), y.length))));
68+
Bytes.concat(
69+
xPadding, Arrays.copyOfRange(x, Math.max(0, x.length - fieldSizeBytes), x.length)),
70+
Bytes.concat(
71+
yPadding,
72+
Arrays.copyOfRange(y, Math.max(0, y.length - fieldSizeBytes), y.length))));
6673
}
6774

6875
static ByteArray rawEcKeyToCose(ByteArray key) {
6976
final byte[] keyBytes = key.getBytes();
70-
if (!(keyBytes.length == 64 || (keyBytes.length == 65 && keyBytes[0] == 0x04))) {
77+
final int len = keyBytes.length;
78+
final int lenSub1 = keyBytes.length - 1;
79+
if (!(len == 64
80+
|| len == 96
81+
|| len == 132
82+
|| (keyBytes[0] == 0x04 && (lenSub1 == 64 || lenSub1 == 96 || lenSub1 == 132)))) {
7183
throw new IllegalArgumentException(
7284
String.format(
73-
"Raw key must be 64 bytes long or be 65 bytes long and start with 0x04, was %d bytes starting with %02x",
85+
"Raw key must be 64, 96 or 132 bytes long, or start with 0x04 and be 65, 97 or 133 bytes long; was %d bytes starting with %02x",
7486
keyBytes.length, keyBytes[0]));
7587
}
76-
final int start = (keyBytes.length == 64) ? 0 : 1;
88+
final int start = (len == 64 || len == 96 || len == 132) ? 0 : 1;
89+
final int coordinateLength = (len - start) / 2;
7790

7891
final Map<Long, Object> coseKey = new HashMap<>();
7992
coseKey.put(1L, 2L); // Key type: EC
8093

81-
coseKey.put(3L, COSEAlgorithmIdentifier.ES256.getId());
82-
coseKey.put(-1L, 1L); // Curve: P-256
94+
final COSEAlgorithmIdentifier coseAlg;
95+
final int coseCrv;
96+
switch (len - start) {
97+
case 64:
98+
coseAlg = COSEAlgorithmIdentifier.ES256;
99+
coseCrv = 1;
100+
break;
101+
case 96:
102+
coseAlg = COSEAlgorithmIdentifier.ES384;
103+
coseCrv = 2;
104+
break;
105+
case 132:
106+
coseAlg = COSEAlgorithmIdentifier.ES512;
107+
coseCrv = 3;
108+
break;
109+
default:
110+
throw new RuntimeException(
111+
"Failed to determine COSE EC algorithm. This should not be possible, please file a bug report.");
112+
}
113+
coseKey.put(3L, coseAlg.getId());
114+
coseKey.put(-1L, coseCrv);
83115

84-
coseKey.put(-2L, Arrays.copyOfRange(keyBytes, start, start + 32)); // x
85-
coseKey.put(-3L, Arrays.copyOfRange(keyBytes, start + 32, start + 64)); // y
116+
coseKey.put(-2L, Arrays.copyOfRange(keyBytes, start, start + coordinateLength)); // x
117+
coseKey.put(
118+
-3L,
119+
Arrays.copyOfRange(keyBytes, start + coordinateLength, start + 2 * coordinateLength)); // y
86120

87121
return new ByteArray(CBORObject.FromObject(coseKey).EncodeToBytes());
88122
}
@@ -152,6 +186,10 @@ static String getJavaAlgorithmName(COSEAlgorithmIdentifier alg) {
152186
return "EDDSA";
153187
case ES256:
154188
return "SHA256withECDSA";
189+
case ES384:
190+
return "SHA384withECDSA";
191+
case ES512:
192+
return "SHA512withECDSA";
155193
case RS256:
156194
return "SHA256withRSA";
157195
case RS1:

webauthn-server-core/src/main/java/com/yubico/webauthn/data/COSEAlgorithmIdentifier.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
public enum COSEAlgorithmIdentifier {
4343
EdDSA(-8),
4444
ES256(-7),
45+
ES384(-35),
46+
ES512(-36),
4547
RS256(-257),
4648
RS1(-65535);
4749

webauthn-server-core/src/main/java/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ private static List<PublicKeyCredentialParameters> filterAvailableAlgorithms(
376376
break;
377377

378378
case ES256:
379+
case ES384:
380+
case ES512:
379381
KeyFactory.getInstance("EC");
380382
break;
381383

@@ -405,6 +407,14 @@ private static List<PublicKeyCredentialParameters> filterAvailableAlgorithms(
405407
Signature.getInstance("SHA256withECDSA");
406408
break;
407409

410+
case ES384:
411+
Signature.getInstance("SHA384withECDSA");
412+
break;
413+
414+
case ES512:
415+
Signature.getInstance("SHA512withECDSA");
416+
break;
417+
408418
case RS256:
409419
Signature.getInstance("SHA256withRSA");
410420
break;

webauthn-server-core/src/main/java/com/yubico/webauthn/data/PublicKeyCredentialParameters.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ private PublicKeyCredentialParameters(
7272
public static final PublicKeyCredentialParameters ES256 =
7373
builder().alg(COSEAlgorithmIdentifier.ES256).build();
7474

75+
/**
76+
* Algorithm {@link COSEAlgorithmIdentifier#ES384} and type {@link
77+
* PublicKeyCredentialType#PUBLIC_KEY}.
78+
*/
79+
public static final PublicKeyCredentialParameters ES384 =
80+
builder().alg(COSEAlgorithmIdentifier.ES384).build();
81+
82+
/**
83+
* Algorithm {@link COSEAlgorithmIdentifier#ES512} and type {@link
84+
* PublicKeyCredentialType#PUBLIC_KEY}.
85+
*/
86+
public static final PublicKeyCredentialParameters ES512 =
87+
builder().alg(COSEAlgorithmIdentifier.ES512).build();
88+
7589
/**
7690
* Algorithm {@link COSEAlgorithmIdentifier#RS1} and type {@link
7791
* PublicKeyCredentialType#PUBLIC_KEY}.

webauthn-server-core/src/test/scala/com/yubico/webauthn/RegistrationTestData.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,8 @@ case class RegistrationTestData(
779779
List(
780780
PublicKeyCredentialParameters.ES256,
781781
PublicKeyCredentialParameters.EdDSA,
782+
PublicKeyCredentialParameters.ES384,
783+
PublicKeyCredentialParameters.ES512,
782784
PublicKeyCredentialParameters.RS256,
783785
).asJava
784786
)

webauthn-server-core/src/test/scala/com/yubico/webauthn/TestAuthenticator.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,9 @@ object TestAuthenticator {
874874
def generateKeypair(algorithm: COSEAlgorithmIdentifier): KeyPair =
875875
algorithm match {
876876
case COSEAlgorithmIdentifier.EdDSA => generateEddsaKeypair()
877-
case COSEAlgorithmIdentifier.ES256 => generateEcKeypair()
877+
case COSEAlgorithmIdentifier.ES256 => generateEcKeypair("secp256r1")
878+
case COSEAlgorithmIdentifier.ES384 => generateEcKeypair("secp384r1")
879+
case COSEAlgorithmIdentifier.ES512 => generateEcKeypair("secp521r1")
878880
case COSEAlgorithmIdentifier.RS256 => generateRsaKeypair()
879881
case COSEAlgorithmIdentifier.RS1 => generateRsaKeypair()
880882
}

0 commit comments

Comments
 (0)