diff --git a/fido/src/main/java/com/yubico/yubikit/fido/client/BasicWebAuthnClient.java b/fido/src/main/java/com/yubico/yubikit/fido/client/BasicWebAuthnClient.java index de173658..0a4fdec5 100644 --- a/fido/src/main/java/com/yubico/yubikit/fido/client/BasicWebAuthnClient.java +++ b/fido/src/main/java/com/yubico/yubikit/fido/client/BasicWebAuthnClient.java @@ -62,7 +62,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -450,20 +449,19 @@ protected WithExtensionResults ctapMakeCredential( Map ctapOptions = getCreateCtapOptions(options, pin); - List registrationProcessors = - extensions.stream() - .map(e -> e.makeCredential(ctap, options, clientPin.getPinUvAuth())) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - int permissions = - registrationProcessors.stream() - .map(Extension.RegistrationProcessor::getPermissions) - .reduce(ClientPin.PIN_PERMISSION_MC | - (options.getExcludeCredentials().isEmpty() - ? ClientPin.PIN_PERMISSION_NONE - : ClientPin.PIN_PERMISSION_GA), - (perms, perm) -> perms | perm); + int permissions = ClientPin.PIN_PERMISSION_MC; + if (!options.getExcludeCredentials().isEmpty()) { + permissions |= ClientPin.PIN_PERMISSION_GA; + } + List registrationProcessors = new ArrayList<>(); + for (Extension extension : extensions) { + Extension.RegistrationProcessor processor = + extension.makeCredential(ctap, options, clientPin.getPinUvAuth()); + if (processor != null) { + registrationProcessors.add(processor); + permissions |= processor.getPermissions(); + } + } final AuthParams authParams = getAuthParams( clientDataHash, @@ -472,10 +470,10 @@ protected WithExtensionResults ctapMakeCredential( permissions, rpId); - Map authenticatorInputs = - registrationProcessors.stream() - .map(p -> p.getInput(authParams.pinToken)) - .collect(HashMap::new, HashMap::putAll, HashMap::putAll); + HashMap authenticatorInputs = new HashMap<>(); + for (Extension.RegistrationProcessor processor : registrationProcessors) { + authenticatorInputs.putAll(processor.getInput(authParams.pinToken)); + } final List excludeCredentials = removeUnsupportedCredentials( @@ -528,14 +526,11 @@ protected WithExtensionResults ctapMakeCredential( state ); - ClientExtensionResults results = - registrationProcessors.stream() - .map(p -> p.getOutput( - AttestationObject.fromCredential(credentialData), - authParams.pinToken)) - .collect(ClientExtensionResults::new, - ClientExtensionResults::add, - ClientExtensionResults::addAll); + ClientExtensionResults results = new ClientExtensionResults(); + for (Extension.RegistrationProcessor processor : registrationProcessors) { + AttestationObject attestationObject = AttestationObject.fromCredential(credentialData); + results.add(processor.getOutput(attestationObject, authParams.pinToken)); + } return new WithExtensionResults<>(credentialData, results); } @@ -577,17 +572,16 @@ protected List> ctapGetAssertio options.getAllowCredentials() ); - List authenticationProcessors = - extensions.stream() - .map(e -> e.getAssertion(ctap, options, clientPin.getPinUvAuth())) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - int permissions = - authenticationProcessors.stream() - .map(Extension.AuthenticationProcessor::getPermissions) - .reduce(ClientPin.PIN_PERMISSION_GA, - (perms, perm) -> perms | perm); + int permissions = ClientPin.PIN_PERMISSION_GA; + List authenticationProcessors = new ArrayList<>(); + for (Extension extension : extensions) { + Extension.AuthenticationProcessor processor = + extension.getAssertion(ctap, options, clientPin.getPinUvAuth()); + if (processor != null) { + authenticationProcessors.add(processor); + permissions |= processor.getPermissions(); + } + } final AuthParams authParams = getAuthParams( clientDataHash, @@ -606,12 +600,11 @@ protected List> ctapGetAssertio authParams.pinToken) : null; - final Map authenticatorInputs = - authenticationProcessors.stream() - .map(p -> p.getInput(selectedCred, authParams.pinToken)) - .collect(HashMap::new, - HashMap::putAll, - HashMap::putAll); + HashMap authenticatorInputs = new HashMap<>(); + for (Extension.AuthenticationProcessor processor : authenticationProcessors) { + authenticatorInputs.putAll(processor.getInput(selectedCred, authParams.pinToken)); + } + try { List assertions = ctap.getAssertions( rpId, @@ -630,15 +623,12 @@ protected List> ctapGetAssertio List> result = new ArrayList<>(); for(final Ctap2Session.AssertionData assertionData : assertions) { - ClientExtensionResults results = - authenticationProcessors.stream() - .map(p -> p.getOutput(assertionData, authParams.pinToken)) - .collect(ClientExtensionResults::new, - ClientExtensionResults::add, - ClientExtensionResults::addAll); + ClientExtensionResults results = new ClientExtensionResults(); + for (Extension.AuthenticationProcessor processor : authenticationProcessors) { + results.add(processor.getOutput(assertionData, authParams.pinToken)); + } result.add(new WithExtensionResults<>(assertionData, results)); } - return result; } catch (CtapException exc) { @@ -841,10 +831,12 @@ static PublicKeyCredentialDescriptor filterCreds( Ctap2Session.InfoData info = ctap.getCachedInfo(); Integer maxCredIdLength = info.getMaxCredentialIdLength(); if (maxCredIdLength != null) { - creds = descriptors - .stream() - .filter(desc -> desc.getId().length <= maxCredIdLength) - .collect(Collectors.toList()); + creds = new ArrayList<>(); + for (PublicKeyCredentialDescriptor desc : descriptors) { + if (desc.getId().length <= maxCredIdLength) { + creds.add(desc); + } + } } else { creds = descriptors; } @@ -912,9 +904,11 @@ static PublicKeyCredentialDescriptor filterCreds( if (descriptors == null || descriptors.isEmpty()) { return null; } - return descriptors.stream() - .map(d -> d.toMap(SerializationType.CBOR)) - .collect(Collectors.toList()); + List> creds = new ArrayList<>(); + for (PublicKeyCredentialDescriptor descriptor : descriptors) { + creds.add(descriptor.toMap(SerializationType.CBOR)); + } + return creds; } /** diff --git a/fido/src/main/java/com/yubico/yubikit/fido/client/extensions/HmacSecretExtension.java b/fido/src/main/java/com/yubico/yubikit/fido/client/extensions/HmacSecretExtension.java index b998a317..b519319e 100644 --- a/fido/src/main/java/com/yubico/yubikit/fido/client/extensions/HmacSecretExtension.java +++ b/fido/src/main/java/com/yubico/yubikit/fido/client/extensions/HmacSecretExtension.java @@ -43,6 +43,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -117,10 +118,10 @@ public AuthenticationProcessor getAssertion( throw new IllegalArgumentException("evalByCredential needs allow list"); } - Set ids = allowCredentials - .stream() - .map(desc -> toUrlSafeString(desc.getId())) - .collect(Collectors.toSet()); + Set ids = new HashSet<>(); + for (PublicKeyCredentialDescriptor descriptor : allowCredentials) { + ids.add(toUrlSafeString(descriptor.getId())); + } if (!ids.containsAll(evalByCredential.keySet())) { throw new IllegalArgumentException("evalByCredentials contains invalid key"); diff --git a/fido/src/test/java/com/yubico/yubikit/fido/client/BasicWebAuthnClientUtilsTest.java b/fido/src/test/java/com/yubico/yubikit/fido/client/BasicWebAuthnClientUtilsTest.java index e6a06f20..3377ba5f 100644 --- a/fido/src/test/java/com/yubico/yubikit/fido/client/BasicWebAuthnClientUtilsTest.java +++ b/fido/src/test/java/com/yubico/yubikit/fido/client/BasicWebAuthnClientUtilsTest.java @@ -43,12 +43,12 @@ import org.junit.Test; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -432,26 +432,33 @@ Ctap2Session build() throws Throwable { List> allowList = invocation.getArgument(2); - List ids = idsForRp != null - ? idsForRp.stream().filter(id -> - allowList - .stream() - .anyMatch(desc -> Arrays.equals( - id, - (byte[]) desc.get(PublicKeyCredentialDescriptor.ID - ))) - ).collect(Collectors.toList()) - : Collections.emptyList(); + List ids; + if (idsForRp != null) { + ids = new ArrayList<>(); + for (byte[] id : idsForRp) { + for (Map desc : allowList) { + byte[] descId = (byte[]) desc.get(PublicKeyCredentialDescriptor.ID); + if (Arrays.equals(id, descId)) { + ids.add(id); + break; + } + } + } + } else { + ids = Collections.emptyList(); + } if (ids.isEmpty()) { throw new CtapException(CtapException.ERR_NO_CREDENTIALS); } - return ids.stream().map(id -> { + List list = new ArrayList<>(); + for (byte[] bytes : ids) { Ctap2Session.AssertionData assertionData = mock(Ctap2Session.AssertionData.class); - when(assertionData.getCredentialId(isNull())).thenReturn(id); - return assertionData; - }).collect(Collectors.toList()); + when(assertionData.getCredentialId(isNull())).thenReturn(bytes); + list.add(assertionData); + } + return list; }); return ctapMock; diff --git a/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/ClientHelper.java b/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/ClientHelper.java index 08649eb5..154fd5e2 100644 --- a/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/ClientHelper.java +++ b/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/ClientHelper.java @@ -29,9 +29,8 @@ import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialType; import java.io.IOException; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; public class ClientHelper { final BasicWebAuthnClient client; @@ -87,17 +86,19 @@ public void deleteCredentialsByIds( public void deleteCredentials(List credentials) throws IOException, CommandException, ClientError { - deleteCredentialsByIds(credentials - .stream() - .map(PublicKeyCredential::getRawId) - .collect(Collectors.toList())); + List credIds = new ArrayList<>(); + for (PublicKeyCredential credential : credentials) { + credIds.add(credential.getRawId()); + } + deleteCredentialsByIds(credIds); } public void deleteCredentials(PublicKeyCredential... credentials) throws IOException, CommandException, ClientError { - deleteCredentialsByIds(Arrays - .stream(credentials) - .map(PublicKeyCredential::getRawId) - .collect(Collectors.toList())); + List credIds = new ArrayList<>(); + for (PublicKeyCredential credential : credentials) { + credIds.add(credential.getRawId()); + } + deleteCredentialsByIds(credIds); } } \ No newline at end of file diff --git a/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/CreationOptionsBuilder.java b/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/CreationOptionsBuilder.java index 4d428848..ca0acf3f 100644 --- a/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/CreationOptionsBuilder.java +++ b/testing/src/main/java/com/yubico/yubikit/testing/fido/utils/CreationOptionsBuilder.java @@ -28,11 +28,11 @@ import com.yubico.yubikit.fido.webauthn.ResidentKeyRequirement; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -81,10 +81,12 @@ public CreationOptionsBuilder excludeCredentialDescriptors( return this; } - public CreationOptionsBuilder excludeCredentials(List excludeCredentials) { - this.excludeCredentials = excludeCredentials.stream().map( - c -> new PublicKeyCredentialDescriptor(PUBLIC_KEY, c.getRawId()) - ).collect(Collectors.toList()); + public CreationOptionsBuilder excludeCredentials(List credentials) { + List list = new ArrayList<>(); + for (PublicKeyCredential credential : credentials) { + list.add(new PublicKeyCredentialDescriptor(PUBLIC_KEY, credential.getRawId())); + } + excludeCredentials = list; return this; } @@ -94,10 +96,11 @@ public CreationOptionsBuilder excludeCredentials( return this; } - public CreationOptionsBuilder excludeCredentials(PublicKeyCredential... excludeCredentials) { - this.excludeCredentials = Arrays.stream(excludeCredentials).map( - c -> new PublicKeyCredentialDescriptor(PUBLIC_KEY, c.getRawId()) - ).collect(Collectors.toList()); + public CreationOptionsBuilder excludeCredentials(PublicKeyCredential... credentials) { + excludeCredentials = new ArrayList<>(); + for (PublicKeyCredential cred : credentials) { + excludeCredentials.add(new PublicKeyCredentialDescriptor(PUBLIC_KEY, cred.getRawId())); + } return this; }