Skip to content

Commit

Permalink
[feature] Functions for listing hash providers and algorithms and spe…
Browse files Browse the repository at this point in the history
…cifying them when hashing
  • Loading branch information
adamretter committed Aug 22, 2019
1 parent ac9efb1 commit e20cbdd
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,15 @@ public class ExistExpathCryptoModule extends AbstractInternalModule {

public final static ErrorCode NO_FUNCTION = new ExpathCryptoErrorCode("NO_FUNCTION", "No function");

private final static FunctionDef[] functions = functionDefs(functionDefs(HashFunction.class, HashFunction.FS_HASH),
private final static FunctionDef[] functions = functionDefs(
functionDefs(HashFunction.class, HashFunction.FS_HASH),
functionDefs(HashFunction.class, HashFunction.FS_HASH_PROVIDERS),
functionDefs(HashFunction.class, HashFunction.FS_HASH_ALGORITHMS),
functionDefs(HmacFunction.class, HmacFunction.FS_HMAC),
functionDefs(GenerateSignatureFunction.class, GenerateSignatureFunction.FS_GENERATE_SIGNATURE),
functionDefs(ValidateSignatureFunction.class, ValidateSignatureFunction.FS_VALIDATE_SIGNATURE),
functionDefs(EncryptionFunctions.class, EncryptionFunctions.FS_ENCRYPT, EncryptionFunctions.FS_DECRYPT));
functionDefs(EncryptionFunctions.class, EncryptionFunctions.FS_ENCRYPT, EncryptionFunctions.FS_DECRYPT)
);

public ExistExpathCryptoModule(final Map<String, List<? extends Object>> parameters) throws Exception {
super(functions, parameters);
Expand Down
107 changes: 95 additions & 12 deletions src/main/java/org/expath/exist/crypto/digest/HashFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.exist.xquery.BasicFunction;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.BinaryValue;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;
import org.exist.xquery.functions.map.MapType;
import org.exist.xquery.value.*;
import org.expath.exist.crypto.EXpathCryptoException;

import ro.kuberam.libs.java.crypto.CryptoException;
import ro.kuberam.libs.java.crypto.digest.Hash;

import static org.exist.xquery.FunctionDSL.*;
import static org.expath.exist.crypto.ExistExpathCryptoModule.*;
import static org.expath.exist.crypto.ExistExpathCryptoModule.functionSignature;

public class HashFunction extends BasicFunction {

Expand All @@ -57,12 +57,53 @@ public class HashFunction extends BasicFunction {
"The data to be hashed.");
private static final FunctionParameterSequenceType FS_HASH_PARAM_ALGORITHM = param("algorithm", Type.STRING,
"The cryptographic hashing algorithm.");
private static final FunctionParameterSequenceType FS_HASH_PARAM_PROVIDER = param("provider", Type.STRING,
"The cryptographic hashing algorithm provider.");

public static final FunctionSignature FS_HASH[] = functionSignatures(FS_HASH_NAME,
"resulting hash value, as string.", returnsOptMany(Type.BYTE),
arities(arity(FS_HASH_PARAM_DATA, FS_HASH_PARAM_ALGORITHM),
arity(FS_HASH_PARAM_DATA, FS_HASH_PARAM_ALGORITHM, param("encoding", Type.STRING,
"The encoding of the output. The legal values are \"hex\" and \"base64\". The default value is \"base64\"."))));
public static final FunctionSignature FS_HASH[] = functionSignatures(
FS_HASH_NAME,
"resulting hash value, as string.",
returnsOptMany(Type.BYTE),
arities(
arity(
FS_HASH_PARAM_DATA,
FS_HASH_PARAM_ALGORITHM
),
arity(
FS_HASH_PARAM_DATA,
FS_HASH_PARAM_ALGORITHM,
param("encoding", Type.STRING, "The encoding of the output. The legal values are \"hex\" and \"base64\". The default value is \"base64\".")
),
arity(
FS_HASH_PARAM_DATA,
FS_HASH_PARAM_ALGORITHM,
param("encoding", Type.STRING, "The encoding of the output. The legal values are \"hex\" and \"base64\". The default value is \"base64\"."),
FS_HASH_PARAM_PROVIDER
)
)
);

private static final String FS_HASH_PROVIDERS_NAME = "hash-providers";
public static final FunctionSignature FS_HASH_PROVIDERS = functionSignature(
FS_HASH_PROVIDERS_NAME,
"Gets the names of all the hash providers",
returnsOptMany(Type.STRING)
);

private static final String FS_HASH_ALGORITHMS_NAME = "hash-algorithms";
public static final FunctionSignature FS_HASH_ALGORITHMS[] = {
functionSignature(
FS_HASH_ALGORITHMS_NAME,
"Gets the names of all the hash providers",
returnsOptMany(Type.STRING)
),
functionSignature(
FS_HASH_ALGORITHMS_NAME,
"Gets the names of all the hash providers",
returns(Type.MAP),
param("provider-name", Type.STRING, "The name of the hash provider.")
)
};

public HashFunction(final XQueryContext context, final FunctionSignature signature) {
super(context, signature);
Expand All @@ -71,24 +112,66 @@ public HashFunction(final XQueryContext context, final FunctionSignature signatu
@Override
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {

if (isCalledAs(FS_HASH_NAME)) {
return hash(args);

} else if (isCalledAs(FS_HASH_PROVIDERS_NAME)) {
final ValueSequence providers = new ValueSequence();
for (final String provider : Hash.listProviders()) {
providers.add(new StringValue(provider));
}
return providers;

} else if (isCalledAs(FS_HASH_ALGORITHMS_NAME)) {
if (args.length == 1) {
final String providerName = args[0].getStringValue();
final ValueSequence algorithmNames = new ValueSequence();
for (final String algorithmName : Hash.listAlgorithms(providerName)) {
algorithmNames.add(new StringValue(algorithmName));
}
return algorithmNames;

} else {
final MapType mapType = new MapType(this.context);
for(final Map.Entry<String, Set<String>> providerAlgorithms : Hash.listAlgorithms().entrySet()) {
final ValueSequence algorithmNames = new ValueSequence();
for (final String algorithmName : providerAlgorithms.getValue()) {
algorithmNames.add(new StringValue(algorithmName));
}
mapType.add(new StringValue(providerAlgorithms.getKey()), algorithmNames);
}
return mapType;
}
} else {
throw new XPathException(this, "Unknown function name");
}
}

private Sequence hash(final Sequence[] args) throws XPathException {
final int inputType = args[0].itemAt(0).getType();
final String hashAlgorithm = args[1].getStringValue();
final String encoding = Optional.ofNullable(args[2].getStringValue()).filter(str -> !str.isEmpty())
.orElse("base64");

String provider = null;
if (args.length == 4) {
provider = args[3].getStringValue();
}

LOG.debug("encoding = {}", () -> encoding);

final Sequence result;
if (inputType == Type.STRING || inputType == Type.ELEMENT || inputType == Type.DOCUMENT) {
try {
result = new StringValue(Hash.hashString(args[0].getStringValue(), hashAlgorithm, encoding));
result = new StringValue(Hash.hashString(args[0].getStringValue(), hashAlgorithm, provider, encoding));
} catch (CryptoException e) {
throw new EXpathCryptoException(this, e.getCryptoError());
}
} else if (inputType == Type.BASE64_BINARY || inputType == Type.HEX_BINARY) {
try {
final BinaryValue binaryValue = (BinaryValue) args[0].itemAt(0);
try (final InputStream is = binaryValue.getInputStream()) {
result = new StringValue(Hash.hashBinary(is, hashAlgorithm, encoding));
result = new StringValue(Hash.hashBinary(is, hashAlgorithm, provider, encoding));
}
} catch (CryptoException e) {
throw new EXpathCryptoException(this, e.getCryptoError());
Expand Down

0 comments on commit e20cbdd

Please sign in to comment.