/*
* DSS - Digital Signature Services
*
* Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
*
* Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* DSS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>.
*/
package eu.europa.ec.markt.dss.validation102853.policy;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.exception.DSSNotApplicableMethodException;
import eu.europa.ec.markt.dss.validation102853.RuleUtils;
import eu.europa.ec.markt.dss.validation102853.report.Conclusion;
import eu.europa.ec.markt.dss.validation102853.rules.MessageTag;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.ASCCM_ANS_1;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.ASCCM_ANS_2;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.ASCCM_ANS_3;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.ASCCM_ANS_4;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.ASCCM_ANS_5;
/**
* This class represents a signature cryptographic constraints and indicates their level: IGNORE, INFORM, WARN, FAIL.
* <p/>
* This constraint is composed of:
* - Encryption algorithm constraint;
* - Digest algorithm constraint;
* - Public key size constraint;
* - Algorithm Expiration date constraint.
* <p/>
* DISCLAIMER: Project owner DG-MARKT.
*
* @author <a href="mailto:dgmarkt.Project-DSS@arhs-developments.com">ARHS Developments</a>
* @version $Revision: 1016 $ - $Date: 2011-06-17 15:30:45 +0200 (Fri, 17 Jun 2011) $
*/
public class SignatureCryptographicConstraint extends Constraint {
private static final Logger LOG = LoggerFactory.getLogger(SignatureCryptographicConstraint.class);
/**
* This variable stores the context of the cryptographic constraints.
*/
protected String context;
/**
* This variable stores the sub-context of the cryptographic constraints.
*/
private final String subContext;
/**
* This is the container to store all authorised encryption algorithms.
*/
protected List<String> encryptionAlgorithms;
/**
* This is the container to store all authorised digest algorithms.
*/
protected List<String> digestAlgorithms;
/**
* This is the container to store minimum public key size per encryption algorithm.
*/
protected Map<String, String> minimumPublicKeySizes;
/**
* This is the container to store expiration dates of all encryption and digest algorithms.
*/
protected Map<String, Date> algorithmExpirationDates;
/**
* This is the {@code encryptionAlgorithm} to check
*/
private String encryptionAlgorithm;
/**
* This is the {@code digestAlgorithm} to check
*/
private String digestAlgorithm;
/**
* This is the {@code keyLength} to check
*/
private String keyLength;
/**
* This is the See {@link eu.europa.ec.markt.dss.validation102853.engine.rules.ProcessParameters#getCurrentTime()}
*/
protected Date currentTime;
public static class Pair {
public final String first;
public final String second;
/**
* Constructor for a Pair.
*
* @param first the first object in the Pair
* @param second the second object in the pair
*/
public Pair(String first, String second) {
this.first = first;
this.second = second;
}
}
/**
* This is the default constructor. It takes a level of the constraint as parameter. The string representing the level is trimmed and capitalized. If there is no corresponding
* {@code Level} then the {@code Level.IGNORE} is set and a warning is logged.
*
* @param level the constraint level string.
*/
public SignatureCryptographicConstraint(final String level, final String context, final String subContext) {
super(level);
this.context = context;
this.subContext = subContext;
}
/**
* This method is not applicable in the context of this class.
*
* @param value the simple value of the constraint to set.
*/
@Override
public void setValue(final String value) {
throw new DSSNotApplicableMethodException(getClass());
}
public String getEncryptionAlgorithm() {
return encryptionAlgorithm;
}
public void setEncryptionAlgorithm(final String encryptionAlgorithm) {
this.encryptionAlgorithm = encryptionAlgorithm;
}
public String getDigestAlgorithm() {
return digestAlgorithm;
}
public void setDigestAlgorithm(final String digestAlgorithm) {
this.digestAlgorithm = RuleUtils.canonicalizeDigestAlgo(digestAlgorithm);
}
public String getKeyLength() {
return keyLength;
}
public void setKeyLength(final String keyLength) {
this.keyLength = keyLength;
}
public void setCurrentTime(final Date currentTime) {
this.currentTime = currentTime;
}
public List<String> getEncryptionAlgorithms() {
return encryptionAlgorithms;
}
public void setEncryptionAlgorithms(final List<String> encryptionAlgorithms) {
this.encryptionAlgorithms = encryptionAlgorithms;
}
public List<String> getDigestAlgorithms() {
return digestAlgorithms;
}
public void setDigestAlgorithms(final List<String> digestAlgorithms) {
this.digestAlgorithms = digestAlgorithms;
}
public Map<String, String> getMinimumPublicKeySizes() {
return minimumPublicKeySizes;
}
public void setMinimumPublicKeySizes(final Map<String, String> minimumPublicKeySizes) {
this.minimumPublicKeySizes = minimumPublicKeySizes;
}
public Map<String, Date> getAlgorithmExpirationDates() {
return algorithmExpirationDates;
}
public void setAlgorithmExpirationDates(final Map<String, Date> algorithmExpirationDates) {
this.algorithmExpirationDates = algorithmExpirationDates;
}
/**
* This method carry out the validation of the constraint.
*
* @return true if the constraint is met, false otherwise.
*/
@Override
public boolean check() {
if (ignore()) {
node.addChild(STATUS, IGNORED);
return true;
}
if (inform()) {
node.addChild(STATUS, INFORMATION);
node.addChild(INFO).setAttribute(ENCRYPTION_ALGORITHM, encryptionAlgorithm);
node.addChild(INFO).setAttribute(DIGEST_ALGORITHM, digestAlgorithm);
node.addChild(INFO).setAttribute(PUBLIC_KEY_SIZE, keyLength);
return true;
}
// Encryption algorithm verification:
final boolean containsEncryptionAlgorithm = RuleUtils.contains1(encryptionAlgorithm, encryptionAlgorithms);
if (!containsEncryptionAlgorithm) {
final Pair[] pairs = getParametersAnswer1();
if (fail(ASCCM_ANS_1, pairs)) {
return false;
}
}
// Digest algorithm verification:
final boolean containsDigestAlgorithm = RuleUtils.contains1(digestAlgorithm, digestAlgorithms);
if (!containsDigestAlgorithm) {
final Pair[] pairs = getParametersAnswer2();
if (fail(ASCCM_ANS_2, pairs)) {
return false;
}
}
// Minimum public key size verification:
final String minimumPublicKeySize = minimumPublicKeySizes.get(encryptionAlgorithm);
int keyLengthInt = 0;
try {
keyLengthInt = DSSUtils.isBlank(keyLength) || "?".equals(keyLength) ? 0 : Integer.valueOf(keyLength);
} catch (NumberFormatException e) {
LOG.debug("Unknown key length: '" + keyLength + "'");
}
final boolean publicKeyBigEnough = minimumPublicKeySize != null && Integer.valueOf(keyLengthInt) >= Integer.valueOf(minimumPublicKeySize);
if (!publicKeyBigEnough) {
final Pair[] pairs = getParametersAnswer3(minimumPublicKeySize);
if (fail(ASCCM_ANS_3, pairs)) {
return false;
}
}
// Algorithm's expiration date verification:
if (!algorithmExpirationDates.isEmpty()) {
final String encryptionAlgorithmAndKey = encryptionAlgorithm + keyLength;
Date algorithmExpirationDate = algorithmExpirationDates.get(encryptionAlgorithmAndKey);
if (algorithmExpirationDate == null) {
final Pair[] pairs = getParametersAnswer4(encryptionAlgorithmAndKey);
if (fail(ASCCM_ANS_4, pairs)) {
return false;
}
}
boolean expiredAlgorithm = algorithmExpirationDate == null ? false : algorithmExpirationDate.before(currentTime);
if (expiredAlgorithm) {
final Pair[] pairs = getParametersAnswer5(encryptionAlgorithmAndKey, algorithmExpirationDate);
if (fail(ASCCM_ANS_5, pairs)) {
return false;
}
}
algorithmExpirationDate = algorithmExpirationDates.get(digestAlgorithm);
if (algorithmExpirationDate == null) {
final Pair[] pairs = getParametersAnswer4(digestAlgorithm);
if (fail(ASCCM_ANS_4, pairs)) {
return false;
}
}
expiredAlgorithm = algorithmExpirationDate == null ? false : algorithmExpirationDate.before(currentTime);
if (expiredAlgorithm) {
final Pair[] pairs = getParametersAnswer5(digestAlgorithm, algorithmExpirationDate);
if (fail(ASCCM_ANS_5, pairs)) {
return false;
}
}
}
node.addChild(STATUS, OK);
return true;
}
private Pair[] getParametersAnswer5(final String algorithm, final Date algorithmExpirationDate) {
boolean subContextPresent = DSSUtils.isNotBlank(subContext);
final Pair[] pairs = new Pair[subContextPresent ? 4 : 3];
pairs[0] = new Pair(ALGORITHM, algorithm);
pairs[1] = new Pair(CONTEXT, context);
pairs[2] = new Pair(ALGORITHM_EXPIRATION_DATE, algorithmExpirationDate == null ? "?" : DSSUtils.formatDate(algorithmExpirationDate));
if (subContextPresent) {
pairs[3] = new Pair(SUB_CONTEXT, subContext);
}
return pairs;
}
private Pair[] getParametersAnswer4(final String algorithm) {
boolean subContextPresent = DSSUtils.isNotBlank(subContext);
final Pair[] pairs = new Pair[subContextPresent ? 3 : 2];
pairs[0] = new Pair(ALGORITHM, algorithm);
pairs[1] = new Pair(CONTEXT, context);
if (subContextPresent) {
pairs[2] = new Pair(SUB_CONTEXT, subContext);
}
return pairs;
}
private Pair[] getParametersAnswer3(final String minimumPublicKeySize) {
boolean subContextPresent = DSSUtils.isNotBlank(subContext);
final Pair[] pairs = new Pair[subContextPresent ? 5 : 4];
pairs[0] = new Pair(ENCRYPTION_ALGORITHM, encryptionAlgorithm);
pairs[1] = new Pair(PUBLIC_KEY_SIZE, keyLength);
pairs[2] = new Pair(MINIMUM_PUBLIC_KEY_SIZE, minimumPublicKeySize);
pairs[3] = new Pair(CONTEXT, context);
if (subContextPresent) {
pairs[4] = new Pair(SUB_CONTEXT, subContext);
}
return pairs;
}
private Pair[] getParametersAnswer2() {
boolean subContextPresent = DSSUtils.isNotBlank(subContext);
final Pair[] pairs = new Pair[subContextPresent ? 3 : 2];
pairs[0] = new Pair(DIGEST_ALGORITHM, digestAlgorithm);
pairs[1] = new Pair(CONTEXT, context);
if (subContextPresent) {
pairs[2] = new Pair(SUB_CONTEXT, subContext);
}
return pairs;
}
private Pair[] getParametersAnswer1() {
boolean subContextPresent = DSSUtils.isNotBlank(subContext);
final Pair[] pairs = new Pair[subContextPresent ? 3 : 2];
pairs[0] = new Pair(ENCRYPTION_ALGORITHM, encryptionAlgorithm);
pairs[1] = new Pair(CONTEXT, context);
if (subContextPresent) {
pairs[2] = new Pair(SUB_CONTEXT, subContext);
}
return pairs;
}
private boolean fail(final MessageTag messageTag, final Pair[] pairs) {
if (warn()) {
addWarning(messageTag, pairs);
} else {
addError(messageTag, pairs);
return true;
}
return false;
}
private void addError(final MessageTag messageTag, final Pair[] pairs) {
node.addChild(STATUS, KO);
conclusion.setIndication(indication, subIndication);
final Conclusion.Error error = conclusion.addError(messageTag);
for (final Pair pair : pairs) {
error.setAttribute(pair.first, pair.second);
}
}
private void addWarning(final MessageTag messageTag, final Pair[] pairs) {
node.addChild(STATUS, WARN);
final Conclusion.Warning warning = conclusion.addWarning(messageTag);
for (final Pair pair : pairs) {
warning.setAttribute(pair.first, pair.second);
}
}
}