[java-idp-plugin-webauthn] branch main updated: Update yubico-webauthn-server version to 2.5.1-RC1
Phil Smart
philip.smart at jisc.ac.uk
Mon Feb 26 18:06:34 UTC 2024
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-webauthn.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-webauthn.git;a=commit;h=c8b25e8d217171ed7becd9516b9bd8fe7efc5cdb
The following commit(s) were added to refs/heads/main by this push:
new c8b25e8 Update yubico-webauthn-server version to 2.5.1-RC1
c8b25e8 is described below
commit c8b25e8d217171ed7becd9516b9bd8fe7efc5cdb
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Feb 26 18:06:31 2024 +0000
Update yubico-webauthn-server version to 2.5.1-RC1
- Remove the dependency on cose-java.
- Update the mock authenticator
---
pom.xml | 8 +-
webauthn-impl/pom.xml | 5 -
.../authn/webauthn/impl/MockAuthenticator.java | 175 ++++++++++++++++++---
3 files changed, 150 insertions(+), 38 deletions(-)
diff --git a/pom.xml b/pom.xml
index ed38756..7a00bd2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,10 +34,9 @@
<shib-metadata.groupId>net.shibboleth</shib-metadata.groupId>
<shib-metadata.version>5.0.0</shib-metadata.version>
<yubico.groupId>com.yubico</yubico.groupId>
- <yubico-webauthn.version>2.5.0</yubico-webauthn.version>
+ <yubico-webauthn.version>2.5.1-RC1</yubico-webauthn.version>
<jackson-dataformat-cbor.verison>2.15.3</jackson-dataformat-cbor.verison>
<com-upokecenter.version>4.5.2</com-upokecenter.version>
- <augustcellars-cose.version>1.1.0</augustcellars-cose.version>
<numbers.groupId>com.github.peteroupc</numbers.groupId>
<numbers.version>1.8.2</numbers.version>
<checkstyle.configLocation>
@@ -103,11 +102,6 @@
<version>${com-upokecenter.version}</version>
<scope>runtime</scope>
</dependency>
- <dependency>
- <groupId>com.augustcellars.cose</groupId>
- <artifactId>cose-java</artifactId>
- <version>${augustcellars-cose.version}</version>
- </dependency>
<dependency>
<groupId>${numbers.groupId}</groupId>
<artifactId>numbers</artifactId>
diff --git a/webauthn-impl/pom.xml b/webauthn-impl/pom.xml
index 0c18c8b..6932239 100644
--- a/webauthn-impl/pom.xml
+++ b/webauthn-impl/pom.xml
@@ -57,11 +57,6 @@
<artifactId>numbers</artifactId>
<scope>runtime</scope>
</dependency>
- <dependency>
- <groupId>com.augustcellars.cose</groupId>
- <artifactId>cose-java</artifactId>
- <scope>runtime</scope>
- </dependency>
<!-- Service API and Plugin Description dependencies -->
<dependency>
<groupId>${spring-webflow.groupId}</groupId>
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/MockAuthenticator.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/MockAuthenticator.java
index c73e677..56993e0 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/MockAuthenticator.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/MockAuthenticator.java
@@ -16,13 +16,22 @@ package net.shibboleth.idp.plugin.authn.webauthn.impl;
import static org.testng.Assert.assertEquals;
+import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECPoint;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
@@ -48,13 +57,12 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.upokecenter.cbor.CBORObject;
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
-import COSE.AlgorithmID;
-import COSE.OneKey;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.codec.Base64Support;
import net.shibboleth.shared.codec.EncodingException;
@@ -79,8 +87,11 @@ public class MockAuthenticator {
/** Fix to using this algorithm for key creation and signing. Must match KEY_ALGO. */
private static final String JCA_ALGO ="SHA256withECDSA";
- /** Fix to using this algorithm for key creation and signing. Must match JCA_ALGO.*/
- private static final AlgorithmID KEY_ALGO = AlgorithmID.ECDSA_256;
+ /**
+ * Fix to using this algorithm for key creation and signing. Must match JCA_ALGO.
+ * -7 is ES256 in the IANA COSE Algorithms registry.
+ */
+ private static final int KEY_ALGO = -7;
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(MockAuthenticator.class);
@@ -156,7 +167,7 @@ public class MockAuthenticator {
createAuthenticatorAttestationResponse(@Nonnull @NotEmpty final String challenge,
final Map<String, String> clientData, final byte[] userHandle) throws Exception {
- final OneKey createdKey = OneKey.generateKey(KEY_ALGO);
+ final CombinedKey createdKey = generateECDSA_256_P256_Key();
assert createdKey != null;
final String aaguidHex = generateRandomIdentifierHex(32);
@@ -168,8 +179,8 @@ public class MockAuthenticator {
final var publicKeyCredential = new PublicKeyCredential<Attestation, AuthenticatonExtensionsClientOutputs>(
credentialId,
- new Attestation(clientDataJsonString.getBytes(), createdKey, attestationObject, userHandle),
- new AuthenticatonExtensionsClientOutputs());
+ new Attestation(clientDataJsonString.getBytes(), createdKey.getPrivateKey(), attestationObject,
+ userHandle),new AuthenticatonExtensionsClientOutputs());
//convert from the test type to the correct type for the rest of the system
final var attestationAsJson = jsonMapper.writeValueAsString(publicKeyCredential);
@@ -206,7 +217,7 @@ public class MockAuthenticator {
final byte[] authenticatorData = createAuthDataForAssertion(rawCredentialIdentifier);
final byte[] signature =
sign(authenticatorData, clientDataCompactSerialization,
- publicKeyAttestation.getResponse().getKey().AsPrivateKey());
+ publicKeyAttestation.getResponse().getPrivateKey());
final var pkCred = new PublicKeyCredential<Assertion, AuthenticatonExtensionsClientOutputs>(rawCredentialIdentifier,
new Assertion(clientDataCompactSerialization.getBytes(), authenticatorData, signature,
@@ -245,8 +256,8 @@ public class MockAuthenticator {
final Signature sig = Signature.getInstance(JCA_ALGO);
if (produceBadAssertionSignatures) {
// Use a different key for signing
- final OneKey newKey = OneKey.generateKey(KEY_ALGO);
- sig.initSign(newKey.AsPrivateKey());
+ final CombinedKey newKey = generateECDSA_256_P256_Key();
+ sig.initSign(newKey.getPrivateKey());
} else {
sig.initSign(key);
}
@@ -255,10 +266,6 @@ public class MockAuthenticator {
return signatureBytes;
}
-
-
-
-
/**
* Create the CBOR encoded version of the JSON attestation object.
* See https://www.w3.org/TR/webauthn-2/#sctn-attestation.
@@ -272,7 +279,7 @@ public class MockAuthenticator {
*
* @throws Exception on error
*/
- private byte[] createAttestationObject(final OneKey createdKey, final String aaguidHex,
+ private byte[] createAttestationObject(final CombinedKey createdKey, final String aaguidHex,
final byte[] credentialId) throws Exception{
final HashMap<String, Object> attObj = new HashMap<>();
attObj.put("authData", createAuthData(createdKey, aaguidHex, credentialId));
@@ -292,7 +299,7 @@ public class MockAuthenticator {
* @return the authData
* @throws Exception on error
*/
- private byte[] createAuthDataForAssertion( @Nonnull final byte[] credentialId) throws Exception {
+ private byte[] createAuthDataForAssertion(@Nonnull final byte[] credentialId) throws Exception {
return createAuthData(null, null, credentialId);
}
@@ -309,7 +316,7 @@ public class MockAuthenticator {
* @return the authData
* @throws NoSuchAlgorithmException on error.
*/
- private byte[] createAuthData(@Nullable final OneKey createdKey, @Nullable final String aaguidHex,
+ private byte[] createAuthData(@Nullable final CombinedKey createdKey, @Nullable final String aaguidHex,
@Nonnull final byte[] credentialId) throws Exception {
final byte[] rpIdHash = createRpIdHash();
@@ -378,12 +385,12 @@ public class MockAuthenticator {
* @return the attestedCredentialData
* @throws Exception on error
*/
- private byte[] createAttestedCredentialData(final OneKey createdKey,
+ private byte[] createAttestedCredentialData(final CombinedKey createdKey,
final String aaguidHex, final byte[] credentialId) throws Exception {
final byte[] aaguid = createAaguid(aaguidHex);
final byte[] credentialIdAndLength = createCredentialId(credentialId);
- // OneKey is from the COSE-JAVA lib that Yubico use, but is not perhaps well maintained
- final byte[] coseKeyAsCborBytes = createdKey.AsCBOR().EncodeToBytes();
+
+ final byte[] coseKeyAsCborBytes = createdKey.getKeyMap().EncodeToBytes();
final byte[] attestedCredentialDataCombined = new byte[aaguid.length + credentialIdAndLength.length
+ coseKeyAsCborBytes.length];
@@ -512,6 +519,64 @@ public class MockAuthenticator {
}
}
+
+ /** Create an ECDSA_256_P256 key.*/
+ private CombinedKey generateECDSA_256_P256_Key() throws NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException{
+
+
+ final String curveName = "secp256r1";
+ final int curveSize = 256;
+ final CBORObject EC2_P256 = CBORObject.FromObject(1);
+
+
+ final ECGenParameterSpec paramSpec = new ECGenParameterSpec(curveName);
+ final KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
+ gen.initialize(paramSpec);
+
+ final KeyPair keyPair = gen.genKeyPair();
+ final ECPoint pubPoint = ((ECPublicKey) keyPair.getPublic()).getW();
+
+ final byte[] rgbX = ArrayFromBigNum(pubPoint.getAffineX(), curveSize);
+ final byte[] rgbY = ArrayFromBigNum(pubPoint.getAffineY(), curveSize);
+ final byte[] rgbD = ArrayFromBigNum(((ECPrivateKey) keyPair.getPrivate()).getS(), curveSize);
+
+ final CBORObject keyMap = CBORObject.NewMap();
+ // See https://www.iana.org/assignments/cose/cose.xhtml for what these key parameters are
+ keyMap.Add(CBORObject.FromObject(1), CBORObject.FromObject(2));
+ // Add EC2_Curve
+ keyMap.Add(CBORObject.FromObject(-1), EC2_P256);
+ // Add EC2_X
+ keyMap.Add(CBORObject.FromObject(-2), CBORObject.FromObject(rgbX));
+ // Add EC2_Y
+ keyMap.Add(CBORObject.FromObject(-3), CBORObject.FromObject(rgbY));
+ // Add EC2_D
+ keyMap.Add(CBORObject.FromObject(-4), CBORObject.FromObject(rgbD));
+ // Add Algorithm
+ keyMap.Add(CBORObject.FromObject(3), CBORObject.FromObject(KEY_ALGO));
+
+ final PublicKey publicKey = keyPair.getPublic();
+ final PrivateKey privateKey = keyPair.getPrivate();
+ assert publicKey != null;
+ assert privateKey != null;
+
+ return new CombinedKey(keyMap, privateKey, publicKey);
+
+ }
+
+ /** Convert a biginteger into an array.*/
+ private byte[] ArrayFromBigNum(final BigInteger n, final int curveSize) {
+ final byte[] rgb = new byte[(curveSize+7)/8];
+ final byte[] rgb2 = n.toByteArray();
+ if (rgb.length == rgb2.length) return rgb2;
+ if (rgb2.length > rgb.length) {
+ System.arraycopy(rgb2, rgb2.length-rgb.length, rgb, 0, rgb.length);
+ }
+ else {
+ System.arraycopy(rgb2, 0, rgb, rgb.length-rgb2.length, rgb2.length);
+ }
+ return rgb;
+ }
@JsonIgnoreProperties(ignoreUnknown = true)
/** Public key credential container. See https://w3c.github.io/webauthn/#iface-pkcredential */
@@ -562,6 +627,64 @@ public class MockAuthenticator {
return clientExtensions;
}
+ }
+
+ /** Wrapper for a created key.*/
+ final class CombinedKey {
+
+ /** The key as a CBOR object.*/
+ @Nonnull private final CBORObject keyMap;
+
+ /** The private key component.*/
+ @Nonnull private final PrivateKey privateKey;
+
+ /** The public key component.*/
+ @Nonnull private final PublicKey publicKey;
+
+ /**
+ * Constructor.
+ *
+ * @param keyMap the CBOR keymap
+ * @param privateKey the private key
+ * @param publicKey the public key
+ */
+ protected CombinedKey(@Nonnull final CBORObject keyMap, @Nonnull final PrivateKey privateKey,
+ @Nonnull final PublicKey publicKey) {
+ super();
+ this.keyMap = keyMap;
+ this.privateKey = privateKey;
+ this.publicKey = publicKey;
+ }
+
+ /**
+ * Get the CBOR keymap.
+ *
+ * @return the keyMap.
+ */
+ @Nonnull public final CBORObject getKeyMap() {
+ return keyMap;
+ }
+
+ /**
+ * Get the private key.
+ *
+ * @return the privateKey.
+ */
+ @Nonnull public final PrivateKey getPrivateKey() {
+ return privateKey;
+ }
+
+ /**
+ * Get the public key.
+ *
+ * @return the publicKey.
+ */
+ @Nonnull public final PublicKey getPublicKey() {
+ return publicKey;
+ }
+
+
+
}
/** Simple assertion return type so the caller can access the underlying values easily.*/
@@ -614,7 +737,7 @@ public class MockAuthenticator {
/** Stash the userHandle for easy extraction.*/
private final byte[] userHandle;
/** Stash the key for easy extraction.*/
- private final OneKey key;
+ private final PrivateKey privateKey;
private final byte[] attestationObjectCose;
private final byte[] clientDataJSON;
@@ -622,17 +745,17 @@ public class MockAuthenticator {
* Constructor.
*
* @param clientDataJSON the client data in compact JSON serialization format.
- * @param key the key
+ * @param privateKey the private key
* @param aaguidHex the aaguid
* @param attestationObjectCose the attestation object
*/
public Attestation(@JsonProperty("clientDataJSON") final byte[] clientDataJSON,
- final OneKey key,
+ final PrivateKey privateKey,
@JsonProperty("attestationObject") final byte[] attestationObjectCose,
final byte[] userHandle) {
super();
this.clientDataJSON = clientDataJSON;
- this.key = key;
+ this.privateKey = privateKey;
this.attestationObjectCose = attestationObjectCose;
this.userHandle = userHandle;
}
@@ -643,8 +766,8 @@ public class MockAuthenticator {
}
@JsonIgnore
- public OneKey getKey() {
- return key;
+ public PrivateKey getPrivateKey() {
+ return privateKey;
}
@JsonProperty("attestationObject")
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list