2727
2828import java .io .IOException ;
2929import java .io .InputStream ;
30+ import java .util .Map ;
3031import java .util .Optional ;
32+ import java .util .Set ;
3133
3234import org .apache .logging .log4j .LogManager ;
3335import org .apache .logging .log4j .Logger ;
3436import org .exist .xquery .BasicFunction ;
3537import org .exist .xquery .FunctionSignature ;
3638import org .exist .xquery .XPathException ;
3739import 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 .*;
4342import org .expath .exist .crypto .EXpathCryptoException ;
4443
4544import ro .kuberam .libs .java .crypto .CryptoException ;
4645import ro .kuberam .libs .java .crypto .digest .Hash ;
4746
4847import static org .exist .xquery .FunctionDSL .*;
4948import static org .expath .exist .crypto .ExistExpathCryptoModule .*;
49+ import static org .expath .exist .crypto .ExistExpathCryptoModule .functionSignature ;
5050
5151public 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