diff --git a/pom.xml b/pom.xml index cad9494..d4fb278 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ EXPath Cryptographic Module Implementation - 1.8.0 + 1.8.1-SNAPSHOT 5.3.0 http://expath.org/ns/crypto org.expath.exist.crypto.ExistExpathCryptoModule diff --git a/src/main/java/org/expath/exist/crypto/ExistExpathCryptoModule.java b/src/main/java/org/expath/exist/crypto/ExistExpathCryptoModule.java index 05910e7..f22d31f 100644 --- a/src/main/java/org/expath/exist/crypto/ExistExpathCryptoModule.java +++ b/src/main/java/org/expath/exist/crypto/ExistExpathCryptoModule.java @@ -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> parameters) throws Exception { super(functions, parameters); diff --git a/src/main/java/org/expath/exist/crypto/digest/HashFunction.java b/src/main/java/org/expath/exist/crypto/digest/HashFunction.java index 0fa4bb5..76a3f34 100644 --- a/src/main/java/org/expath/exist/crypto/digest/HashFunction.java +++ b/src/main/java/org/expath/exist/crypto/digest/HashFunction.java @@ -27,16 +27,15 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Map; +import java.util.Set; 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 org.slf4j.Logger; @@ -46,6 +45,7 @@ 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 { @@ -56,12 +56,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); @@ -70,6 +111,42 @@ 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> 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; @@ -78,12 +155,17 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro } else { encoding = args[2].getStringValue().isEmpty() ? "base64" : args[2].getStringValue(); } + 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()); } @@ -91,7 +173,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro 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()); @@ -104,4 +186,4 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro return result; } -} \ No newline at end of file +}