Skip to content

Commit

Permalink
fix: #776 (#2503)
Browse files Browse the repository at this point in the history
* fix: #776

* fix: #776 file location corrected

* fix: #2503 moved timer task to jans-fido2 project + code formatting
  • Loading branch information
maduvena authored Oct 3, 2022
1 parent 4fa83fc commit a564431
Show file tree
Hide file tree
Showing 13 changed files with 1,023 additions and 332 deletions.
747 changes: 636 additions & 111 deletions docs/script-catalog/person_authentication/casa/Casa.py

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion jans-fido2/server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

<build>
<finalName>fido2-server</finalName>

<filters>
<filter>profiles/default/config-build.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public class AppInitializer {
@Inject
private LoggerService loggerService;

@Inject
private MDS3UpdateTimer mds3UpdateTimer;

@PostConstruct
public void createApplicationComponents() {
try {
Expand All @@ -113,10 +116,12 @@ public void applicationInitialized(@Observes @Initialized(ApplicationScoped.clas
configurationFactory.create();

PersistenceEntryManager localPersistenceEntryManager = persistenceEntryManagerInstance.get();
log.trace("Attempting to use {}: {}", ApplicationFactory.PERSISTENCE_ENTRY_MANAGER_NAME, localPersistenceEntryManager.getOperationService());
log.trace("Attempting to use {}: {}", ApplicationFactory.PERSISTENCE_ENTRY_MANAGER_NAME,
localPersistenceEntryManager.getOperationService());

// Initialize python interpreter
pythonService.initPythonInterpreter(configurationFactory.getBaseConfiguration().getString("pythonModulesDir", null));
pythonService
.initPythonInterpreter(configurationFactory.getBaseConfiguration().getString("pythonModulesDir", null));

// Initialize script manager
List<CustomScriptType> supportedCustomScriptTypes = Lists.newArrayList(CustomScriptType.values());
Expand All @@ -132,6 +137,7 @@ public void applicationInitialized(@Observes @Initialized(ApplicationScoped.clas
configurationFactory.initTimer();
loggerService.initTimer();
cleanerTimer.initTimer();
mds3UpdateTimer.initTimer();
customScriptManager.initTimer(supportedCustomScriptTypes);

// Notify plugins about finish application initialization
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.jans.fido2.service.app;

public interface MDS3UpdateEvent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
*
* Copyright (c) 2020, Janssen Project
*/

package io.jans.fido2.service.app;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.inject.Named;

import io.jans.fido2.service.mds.TocService;
import io.jans.service.cdi.async.Asynchronous;
import io.jans.service.cdi.event.Scheduled;
import io.jans.service.timer.event.TimerEvent;
import io.jans.service.timer.schedule.TimerSchedule;
import org.slf4j.Logger;

/**
* @author madhumitas
*
*/
@ApplicationScoped
@Named

public class MDS3UpdateTimer {

private static final int DEFAULT_INTERVAL = 60 * 60 * 24; // every 24 hours

@Inject
private Logger log;

@Inject
private Event<TimerEvent> timerEvent;

@Inject
private TocService tocService;

public void initTimer() {
log.info("Initializing MDS3 Update Timer");

timerEvent.fire(new TimerEvent(new TimerSchedule(DEFAULT_INTERVAL, DEFAULT_INTERVAL), new MDS3UpdateEvent() {
}, Scheduled.Literal.INSTANCE));

log.info("Initialized MDS3 Update Timer");
}

@Asynchronous
public void process(@Observes @Scheduled MDS3UpdateEvent mds3UpdateEvent) {
LocalDate nextUpdate = tocService.getNextUpdateDate();
if (nextUpdate.equals(LocalDate.now()) || nextUpdate.isBefore(LocalDate.now())) {
log.info("Downloading the latest TOC from https://mds.fidoalliance.org/");
try {
tocService.downloadMdsFromServer(new URL("https://mds.fidoalliance.org/"));

} catch (MalformedURLException e) {
log.error("Error while parsing the FIDO alliance URL :", e);
return;
}
tocService.refresh();
} else {
log.info("{} more days for MDS3 Update", LocalDate.now().until(nextUpdate, ChronoUnit.DAYS));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package io.jans.fido2.service.mds;

import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -80,13 +81,23 @@ public void init(@Observes @ApplicationInitialized(ApplicationScoped.class) Obje
this.rootCertificatesMap = certificateService.getCertificatesMap(authenticatorCertsFolder);
}

public List<X509Certificate> getAttestationRootCertificates(JsonNode metadataNode, List<X509Certificate> attestationCertificates) {
if (metadataNode == null || !metadataNode.has("attestationRootCertificates")) {
List<X509Certificate> selectedRootCertificate = certificateService.selectRootCertificates(rootCertificatesMap, attestationCertificates);
public List<X509Certificate> getAttestationRootCertificates(JsonNode metadataNode,
List<X509Certificate> attestationCertificates) {
JsonNode metaDataStatement = null;
try {
metaDataStatement = dataMapperService.readTree(metadataNode.get("metadataStatement").toPrettyString());
} catch (IOException e) {
log.error("Error parsing the metadata statement", e);
}

if (metaDataStatement == null || !metaDataStatement.has("attestationRootCertificates")) {
List<X509Certificate> selectedRootCertificate = certificateService
.selectRootCertificates(rootCertificatesMap, attestationCertificates);

return selectedRootCertificate;
}

ArrayNode node = (ArrayNode) metadataNode.get("attestationRootCertificates");
ArrayNode node = (ArrayNode) metaDataStatement.get("attestationRootCertificates");
Iterator<JsonNode> iter = node.elements();
List<String> x509certificates = new ArrayList<>();
while (iter.hasNext()) {
Expand All @@ -103,12 +114,11 @@ public List<X509Certificate> getAttestationRootCertificates(AuthData authData, L
JsonNode metadataForAuthenticator = localMdsService.getAuthenticatorsMetadata(aaguid);
if (metadataForAuthenticator == null) {
try {
log.info("No metadata for authenticator {}. Attempting to contact MDS", aaguid);
log.info("No Local metadata for authenticator {}. Checking for metadata MDS3 blob", aaguid);
JsonNode metadata = mdsService.fetchMetadata(authData.getAaguid());
commonVerifiers.verifyThatMetadataIsValid(metadata);
metadataForAuthenticator = metadata;

return getAttestationRootCertificates(metadataForAuthenticator, attestationCertificates);
return getAttestationRootCertificates(metadata, attestationCertificates);
} catch (Fido2RuntimeException ex) {
log.warn("Failed to get metadata from Fido2 meta-data server");

Expand Down Expand Up @@ -146,4 +156,4 @@ private KeyStore getCertificationKeyStore(String aaguid, List<X509Certificate> c
return keyStoreCreator.createKeyStore(aaguid, certificates);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,18 @@

package io.jans.fido2.service.mds;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.Response.StatusType;

import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;

import com.fasterxml.jackson.databind.JsonNode;

import io.jans.fido2.exception.Fido2RuntimeException;
import io.jans.fido2.model.conf.AppConfiguration;
import io.jans.fido2.model.conf.Fido2Configuration;
Expand All @@ -47,11 +39,10 @@
import io.jans.fido2.service.client.ResteasyClientFactory;
import io.jans.fido2.service.verifier.CommonVerifiers;
import io.jans.service.cdi.event.ApplicationInitialized;
import io.jans.util.StringHelper;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.slf4j.Logger;

import com.fasterxml.jackson.databind.JsonNode;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;

@ApplicationScoped
public class MdsService {
Expand Down Expand Up @@ -83,99 +74,51 @@ public void init(@Observes @ApplicationInitialized(ApplicationScoped.class) Obje
this.mdsEntries = Collections.synchronizedMap(new HashMap<String, JsonNode>());
}

public JsonNode fetchMetadata(byte[] aaguidBuffer) {
Fido2Configuration fido2Configuration = appConfiguration.getFido2Configuration();
if (fido2Configuration == null) {
throw new Fido2RuntimeException("Fido2 configuration not exists");
}

String mdsAccessToken = fido2Configuration.getMdsAccessToken();
if (StringHelper.isEmpty(mdsAccessToken)) {
throw new Fido2RuntimeException("Fido2 MDS access token should be set");
}

String aaguid = deconvert(aaguidBuffer);

JsonNode mdsEntry = mdsEntries.get(aaguid);
if (mdsEntry != null) {
log.debug("Get MDS by aaguid {} from cache", aaguid);
return mdsEntry;
}

JsonNode tocEntry = tocService.getAuthenticatorsMetadata(aaguid);
if (tocEntry == null) {
throw new Fido2RuntimeException("Authenticator not in TOC aaguid " + aaguid);
}

String tocEntryUrl = tocEntry.get("url").asText();
URI metadataUrl;
try {
metadataUrl = new URI(String.format("%s/?token=%s", tocEntryUrl, mdsAccessToken));
log.debug("Authenticator AAGUI {} url metadataUrl {} downloaded", aaguid, metadataUrl);
} catch (URISyntaxException e) {
throw new Fido2RuntimeException("Invalid URI in TOC aaguid " + aaguid);
}
public JsonNode fetchMetadata(byte[] aaguidBuffer) {
Fido2Configuration fido2Configuration = appConfiguration.getFido2Configuration();
if (fido2Configuration == null) {
throw new Fido2RuntimeException("Fido2 configuration not exists");
}

verifyTocEntryStatus(aaguid, tocEntry);
String metadataHash = commonVerifiers.verifyThatFieldString(tocEntry, "hash");
String aaguid = deconvert(aaguidBuffer);

log.debug("Reaching MDS at {}", tocEntryUrl);
JsonNode mdsEntry = mdsEntries.get(aaguid);
if (mdsEntry != null) {
log.debug("Get MDS by aaguid {} from cache", aaguid);
return mdsEntry;
}

mdsEntry = downloadMdsFromServer(aaguid, metadataUrl, metadataHash);
JsonNode tocEntry = tocService.getAuthenticatorsMetadata(aaguid);
if (tocEntry == null) {
throw new Fido2RuntimeException("Authenticator not in TOC aaguid " + aaguid);
}

mdsEntries.put(aaguid, mdsEntry);

return mdsEntry;
}
verifyTocEntryStatus(aaguid, tocEntry);

private JsonNode downloadMdsFromServer(String aaguid, URI metadataUrl, String metadataHash) {
ResteasyClient resteasyClient = resteasyClientFactory.buildResteasyClient();
Response response = resteasyClient.target(metadataUrl).request().header("Content-Type", MediaType.APPLICATION_JSON).get();
String body = response.readEntity(String.class);

StatusType status = response.getStatusInfo();
log.debug("Response from resource server {}", status);
if (status.getFamily() == Status.Family.SUCCESSFUL) {
byte[] bodyBuffer;
try {
bodyBuffer = body.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new Fido2RuntimeException("Unable to verify metadata hash for aaguid " + aaguid);
}

byte[] digest = tocService.getDigester().digest(bodyBuffer);
if (!Arrays.equals(digest, base64Service.urlDecode(metadataHash))) {
throw new Fido2RuntimeException("Unable to verify metadata hash for aaguid " + aaguid);
}

try {
return dataMapperService.readTree(base64Service.urlDecode(body));
} catch (IOException e) {
log.error("Can't parse payload from the server");
throw new Fido2RuntimeException("Unable to parse payload from server for aaguid " + aaguid);
}
} else {
throw new Fido2RuntimeException("Unable to retrieve metadata for aaguid " + aaguid + " status " + status);
}
return tocEntry;
}

private void verifyTocEntryStatus(String aaguid, JsonNode tocEntry) {
JsonNode statusReports = tocEntry.get("statusReports");

Iterator<JsonNode> iter = statusReports.elements();
while (iter.hasNext()) {
JsonNode statusReport = iter.next();
AuthenticatorCertificationStatus authenticatorStatus = AuthenticatorCertificationStatus.valueOf(statusReport.get("status").asText());
String authenticatorEffectiveDate = statusReport.get("effectiveDate").asText();
log.debug("Authenticator AAGUI {} status {} effective date {}", aaguid, authenticatorStatus, authenticatorEffectiveDate);
verifyStatusAcceptable(aaguid, authenticatorStatus);
}
}


private void verifyTocEntryStatus(String aaguid, JsonNode tocEntry) {
JsonNode statusReports = tocEntry.get("statusReports");

Iterator<JsonNode> iter = statusReports.elements();
while (iter.hasNext()) {
JsonNode statusReport = iter.next();
AuthenticatorCertificationStatus authenticatorStatus = AuthenticatorCertificationStatus
.valueOf(statusReport.get("status").asText());
String authenticatorEffectiveDate = statusReport.get("effectiveDate").asText();
log.debug("Authenticator AAGUID {} status {} effective date {}", aaguid, authenticatorStatus,
authenticatorEffectiveDate);
verifyStatusAcceptable(aaguid, authenticatorStatus);
}
}

private String deconvert(byte[] aaguidBuffer) {
return Hex.encodeHexString(aaguidBuffer).replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)",
"$1-$2-$3-$4-$5");
}
private String deconvert(byte[] aaguidBuffer) {
return Hex.encodeHexString(aaguidBuffer).replaceFirst(
"([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5");
}

private void verifyStatusAcceptable(String aaguid, AuthenticatorCertificationStatus status) {
final List<AuthenticatorCertificationStatus> undesiredAuthenticatorStatus = Arrays
Expand All @@ -192,4 +135,4 @@ public void clear() {
this.mdsEntries.clear();
}

}
}
Loading

0 comments on commit a564431

Please sign in to comment.