Skip to content

Commit e20cbdd

Browse files
committed
[feature] Functions for listing hash providers and algorithms and specifying them when hashing
1 parent ac9efb1 commit e20cbdd

File tree

2 files changed

+101
-14
lines changed

2 files changed

+101
-14
lines changed

src/main/java/org/expath/exist/crypto/ExistExpathCryptoModule.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,15 @@ public class ExistExpathCryptoModule extends AbstractInternalModule {
5757

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

60-
private final static FunctionDef[] functions = functionDefs(functionDefs(HashFunction.class, HashFunction.FS_HASH),
60+
private final static FunctionDef[] functions = functionDefs(
61+
functionDefs(HashFunction.class, HashFunction.FS_HASH),
62+
functionDefs(HashFunction.class, HashFunction.FS_HASH_PROVIDERS),
63+
functionDefs(HashFunction.class, HashFunction.FS_HASH_ALGORITHMS),
6164
functionDefs(HmacFunction.class, HmacFunction.FS_HMAC),
6265
functionDefs(GenerateSignatureFunction.class, GenerateSignatureFunction.FS_GENERATE_SIGNATURE),
6366
functionDefs(ValidateSignatureFunction.class, ValidateSignatureFunction.FS_VALIDATE_SIGNATURE),
64-
functionDefs(EncryptionFunctions.class, EncryptionFunctions.FS_ENCRYPT, EncryptionFunctions.FS_DECRYPT));
67+
functionDefs(EncryptionFunctions.class, EncryptionFunctions.FS_ENCRYPT, EncryptionFunctions.FS_DECRYPT)
68+
);
6569

6670
public ExistExpathCryptoModule(final Map<String, List<? extends Object>> parameters) throws Exception {
6771
super(functions, parameters);

src/main/java/org/expath/exist/crypto/digest/HashFunction.java

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,26 @@
2727

2828
import java.io.IOException;
2929
import java.io.InputStream;
30+
import java.util.Map;
3031
import java.util.Optional;
32+
import java.util.Set;
3133

3234
import org.apache.logging.log4j.LogManager;
3335
import org.apache.logging.log4j.Logger;
3436
import org.exist.xquery.BasicFunction;
3537
import org.exist.xquery.FunctionSignature;
3638
import org.exist.xquery.XPathException;
3739
import org.exist.xquery.XQueryContext;
38-
import org.exist.xquery.value.BinaryValue;
39-
import org.exist.xquery.value.FunctionParameterSequenceType;
40-
import org.exist.xquery.value.Sequence;
41-
import org.exist.xquery.value.StringValue;
42-
import org.exist.xquery.value.Type;
40+
import org.exist.xquery.functions.map.MapType;
41+
import org.exist.xquery.value.*;
4342
import org.expath.exist.crypto.EXpathCryptoException;
4443

4544
import ro.kuberam.libs.java.crypto.CryptoException;
4645
import ro.kuberam.libs.java.crypto.digest.Hash;
4746

4847
import static org.exist.xquery.FunctionDSL.*;
4948
import static org.expath.exist.crypto.ExistExpathCryptoModule.*;
49+
import static org.expath.exist.crypto.ExistExpathCryptoModule.functionSignature;
5050

5151
public class HashFunction extends BasicFunction {
5252

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

61-
public static final FunctionSignature FS_HASH[] = functionSignatures(FS_HASH_NAME,
62-
"resulting hash value, as string.", returnsOptMany(Type.BYTE),
63-
arities(arity(FS_HASH_PARAM_DATA, FS_HASH_PARAM_ALGORITHM),
64-
arity(FS_HASH_PARAM_DATA, FS_HASH_PARAM_ALGORITHM, param("encoding", Type.STRING,
65-
"The encoding of the output. The legal values are \"hex\" and \"base64\". The default value is \"base64\"."))));
63+
public static final FunctionSignature FS_HASH[] = functionSignatures(
64+
FS_HASH_NAME,
65+
"resulting hash value, as string.",
66+
returnsOptMany(Type.BYTE),
67+
arities(
68+
arity(
69+
FS_HASH_PARAM_DATA,
70+
FS_HASH_PARAM_ALGORITHM
71+
),
72+
arity(
73+
FS_HASH_PARAM_DATA,
74+
FS_HASH_PARAM_ALGORITHM,
75+
param("encoding", Type.STRING, "The encoding of the output. The legal values are \"hex\" and \"base64\". The default value is \"base64\".")
76+
),
77+
arity(
78+
FS_HASH_PARAM_DATA,
79+
FS_HASH_PARAM_ALGORITHM,
80+
param("encoding", Type.STRING, "The encoding of the output. The legal values are \"hex\" and \"base64\". The default value is \"base64\"."),
81+
FS_HASH_PARAM_PROVIDER
82+
)
83+
)
84+
);
85+
86+
private static final String FS_HASH_PROVIDERS_NAME = "hash-providers";
87+
public static final FunctionSignature FS_HASH_PROVIDERS = functionSignature(
88+
FS_HASH_PROVIDERS_NAME,
89+
"Gets the names of all the hash providers",
90+
returnsOptMany(Type.STRING)
91+
);
92+
93+
private static final String FS_HASH_ALGORITHMS_NAME = "hash-algorithms";
94+
public static final FunctionSignature FS_HASH_ALGORITHMS[] = {
95+
functionSignature(
96+
FS_HASH_ALGORITHMS_NAME,
97+
"Gets the names of all the hash providers",
98+
returnsOptMany(Type.STRING)
99+
),
100+
functionSignature(
101+
FS_HASH_ALGORITHMS_NAME,
102+
"Gets the names of all the hash providers",
103+
returns(Type.MAP),
104+
param("provider-name", Type.STRING, "The name of the hash provider.")
105+
)
106+
};
66107

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

115+
if (isCalledAs(FS_HASH_NAME)) {
116+
return hash(args);
117+
118+
} else if (isCalledAs(FS_HASH_PROVIDERS_NAME)) {
119+
final ValueSequence providers = new ValueSequence();
120+
for (final String provider : Hash.listProviders()) {
121+
providers.add(new StringValue(provider));
122+
}
123+
return providers;
124+
125+
} else if (isCalledAs(FS_HASH_ALGORITHMS_NAME)) {
126+
if (args.length == 1) {
127+
final String providerName = args[0].getStringValue();
128+
final ValueSequence algorithmNames = new ValueSequence();
129+
for (final String algorithmName : Hash.listAlgorithms(providerName)) {
130+
algorithmNames.add(new StringValue(algorithmName));
131+
}
132+
return algorithmNames;
133+
134+
} else {
135+
final MapType mapType = new MapType(this.context);
136+
for(final Map.Entry<String, Set<String>> providerAlgorithms : Hash.listAlgorithms().entrySet()) {
137+
final ValueSequence algorithmNames = new ValueSequence();
138+
for (final String algorithmName : providerAlgorithms.getValue()) {
139+
algorithmNames.add(new StringValue(algorithmName));
140+
}
141+
mapType.add(new StringValue(providerAlgorithms.getKey()), algorithmNames);
142+
}
143+
return mapType;
144+
}
145+
} else {
146+
throw new XPathException(this, "Unknown function name");
147+
}
148+
}
149+
150+
private Sequence hash(final Sequence[] args) throws XPathException {
74151
final int inputType = args[0].itemAt(0).getType();
75152
final String hashAlgorithm = args[1].getStringValue();
76153
final String encoding = Optional.ofNullable(args[2].getStringValue()).filter(str -> !str.isEmpty())
77154
.orElse("base64");
155+
156+
String provider = null;
157+
if (args.length == 4) {
158+
provider = args[3].getStringValue();
159+
}
160+
78161
LOG.debug("encoding = {}", () -> encoding);
79162

80163
final Sequence result;
81164
if (inputType == Type.STRING || inputType == Type.ELEMENT || inputType == Type.DOCUMENT) {
82165
try {
83-
result = new StringValue(Hash.hashString(args[0].getStringValue(), hashAlgorithm, encoding));
166+
result = new StringValue(Hash.hashString(args[0].getStringValue(), hashAlgorithm, provider, encoding));
84167
} catch (CryptoException e) {
85168
throw new EXpathCryptoException(this, e.getCryptoError());
86169
}
87170
} else if (inputType == Type.BASE64_BINARY || inputType == Type.HEX_BINARY) {
88171
try {
89172
final BinaryValue binaryValue = (BinaryValue) args[0].itemAt(0);
90173
try (final InputStream is = binaryValue.getInputStream()) {
91-
result = new StringValue(Hash.hashBinary(is, hashAlgorithm, encoding));
174+
result = new StringValue(Hash.hashBinary(is, hashAlgorithm, provider, encoding));
92175
}
93176
} catch (CryptoException e) {
94177
throw new EXpathCryptoException(this, e.getCryptoError());

0 commit comments

Comments
 (0)