[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