1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 module hunt.shiro.crypto.hash.AbstractHash;
20 
21 import std.stdio;
22 import hunt.shiro.codec.Base64;
23 import hunt.shiro.codec.CodecSupport;
24 import hunt.shiro.codec.Hex;
25 import hunt.shiro.crypto.hash.Hash;
26 
27 //import hunt.util.Common;
28 //import java.security.MessageDigest;
29 //import java.security.NoSuchAlgorithmException;
30 import hunt.util.ArrayHelper;
31 
32 /**
33  * Provides a base for all Shiro Hash algorithms with support for salts and multiple hash iterations.
34  * <p/>
35  * Read
36  * <a href="http://www.owasp.org/index.php/Hashing_Java" target="blank">http://www.owasp.org/index.php/Hashing_Java</a>
37  * for a good article on the benefits of hashing, including what a 'salt' is as well as why it and multiple hash
38  * iterations can be useful.
39  * <p/>
40  * This class and its subclasses support hashing with additional capabilities of salting and multiple iterations via
41  * overloaded constructors.
42  *
43  * deprecated("") in Shiro 1.1 in favor of using the concrete {@link SimpleHash} implementation directly.
44  */
45 // deprecated("")
46 abstract class AbstractHash : CodecSupport, Hash {
47 
48     /**
49      * The hashed data
50      */
51    private byte[] bytes = null;
52 
53     /**
54      * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead.
55      */
56     private string hexEncoded = null;
57     /**
58      * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead.
59      */
60     private string base64Encoded = null;
61 
62     /**
63      * Creates an new instance without any of its properties set (no hashing is performed).
64      * <p/>
65      * Because all constructors in this class (except this one) hash the {@code source} constructor argument, this
66      * default, no-arg constructor is useful in scenarios when you have a byte array that you know is already hashed and
67      * just want to set the bytes in their raw form directly on an instance.  After instantiating the instance with
68      * this default, no-arg constructor, you can then immediately call {@link #setBytes setBytes} to have a
69      * fully-initialized instance.
70      */
71     this() {
72     }
73 
74 //     /**
75 //      * Creates a hash of the specified {@code source} with no {@code salt} using a single hash iteration.
76 //      * <p/>
77 //      * It is a convenience constructor that merely executes <code>this( source, null, 1);</code>.
78 //      * <p/>
79 //      * Please see the
80 //      * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)}
81 //      * constructor for the types of Objects that may be passed into this constructor, as well as how to support further
82 //      * types.
83 //      *
84 //      * @param source the object to be hashed.
85 //      * @throws CodecException if the specified {@code source} cannot be converted into a byte array (byte[]).
86 //      */
87 //     this(Object source) {
88 //         this(source, null, 1);
89 //     }
90 
91 //     /**
92 //      * Creates a hash of the specified {@code source} using the given {@code salt} using a single hash iteration.
93 //      * <p/>
94 //      * It is a convenience constructor that merely executes <code>this( source, salt, 1);</code>.
95 //      * <p/>
96 //      * Please see the
97 //      * {@link #AbstractHash(Object source, Object salt, int numIterations) AbstractHash(Object,Object,int)}
98 //      * constructor for the types of Objects that may be passed into this constructor, as well as how to support further
99 //      * types.
100 //      *
101 //      * @param source the source object to be hashed.
102 //      * @param salt   the salt to use for the hash
103 //      * @throws CodecException if either constructor argument cannot be converted into a byte array.
104 //      */
105 //     this(Object source, Object salt) {
106 //         this(source, salt, 1);
107 //     }
108 
109 //     /**
110 //      * Creates a hash of the specified {@code source} using the given {@code salt} a total of
111 //      * {@code hashIterations} times.
112 //      * <p/>
113 //      * By default, this class only supports Object method arguments of
114 //      * type {@code byte[]}, {@code char[]}, {@link string}, {@link java.io.File File}, or
115 //      * {@link java.io.InputStream InputStream}.  If either argument is anything other than these
116 //      * types a {@link hunt.shiro.codec.CodecException CodecException} will be thrown.
117 //      * <p/>
118 //      * If you want to be able to hash other object types, or use other salt types, you need to override the
119 //      * {@link #toBytes(Object) toBytes(Object)} method to support those specific types.  Your other option is to
120 //      * convert your arguments to one of the default three supported types first before passing them in to this
121 //      * constructor}.
122 //      *
123 //      * @param source         the source object to be hashed.
124 //      * @param salt           the salt to use for the hash
125 //      * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency.
126 //      * @throws CodecException if either Object constructor argument cannot be converted into a byte array.
127 //      */
128 //     this(Object source, Object salt, int hashIterations) {
129 //         byte[] sourceBytes = toBytes(source);
130 //         byte[] saltBytes = null;
131 //         if (salt !is null) {
132 //             saltBytes = toBytes(salt);
133 //         }
134 //         byte[] hashedBytes = hash(sourceBytes, saltBytes, hashIterations);
135 //         setBytes(hashedBytes);
136 //     }
137 
138 //     /**
139 //      * Implemented by subclasses, this specifies the {@link MessageDigest MessageDigest} algorithm name 
140 //      * to use when performing the hash.
141 //      *
142 //      * @return the {@link MessageDigest MessageDigest} algorithm name to use when performing the hash.
143 //      */
144 //     abstract string getAlgorithmName();
145 
146 //     byte[] getBytes() {
147 //         return this.bytes;
148 //     }
149 
150 //     /**
151 //      * Sets the raw bytes stored by this hash instance.
152 //      * <p/>
153 //      * The bytes are kept in raw form - they will not be hashed/changed.  This is primarily a utility method for
154 //      * constructing a Hash instance when the hashed value is already known.
155 //      *
156 //      * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance.
157 //      */
158 //     void setBytes(byte[] alreadyHashedBytes) {
159 //         this.bytes = alreadyHashedBytes;
160 //         this.hexEncoded = null;
161 //         this.base64Encoded = null;
162 //     }
163 
164 //     /**
165 //      * Returns the JDK MessageDigest instance to use for executing the hash.
166 //      *
167 //      * @param algorithmName the algorithm to use for the hash, provided by subclasses.
168 //      * @return the MessageDigest object for the specified {@code algorithm}.
169 //      * @throws UnknownAlgorithmException if the specified algorithm name is not available.
170 //      */
171 //     // protected MessageDigest getDigest(string algorithmName) {
172 //     //     try {
173 //     //         return MessageDigest.getInstance(algorithmName);
174 //     //     } catch (NoSuchAlgorithmException e) {
175 //     //         string msg = "No native '" ~ algorithmName ~ "' MessageDigest instance available on the current JVM.";
176 //     //         throw new UnknownAlgorithmException(msg, e);
177 //     //     }
178 //     // }
179 
180 //     /**
181 //      * Hashes the specified byte array without a salt for a single iteration.
182 //      *
183 //      * @param bytes the bytes to hash.
184 //      * @return the hashed bytes.
185 //      */
186 //    protected byte[] hash(byte[] bytes) {
187 //         return hash(bytes, null, 1);
188 //     }
189 
190 //     /**
191 //      * Hashes the specified byte array using the given {@code salt} for a single iteration.
192 //      *
193 //      * @param bytes the bytes to hash
194 //      * @param salt  the salt to use for the initial hash
195 //      * @return the hashed bytes
196 //      */
197 //    protected byte[] hash(byte[] bytes, byte[] salt) {
198 //         return hash(bytes, salt, 1);
199 //     }
200 
201 //     /**
202 //      * Hashes the specified byte array using the given {@code salt} for the specified number of iterations.
203 //      *
204 //      * @param bytes          the bytes to hash
205 //      * @param salt           the salt to use for the initial hash
206 //      * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency).
207 //      * @return the hashed bytes.
208 //      * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available.
209 //      */
210 //    protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) {
211 //         MessageDigest digest = getDigest(getAlgorithmName());
212 //         if (salt !is null) {
213 //             digest.reset();
214 //             digest.update(salt);
215 //         }
216 //         byte[] hashed = digest.digest(bytes);
217 //         int iterations = hashIterations - 1; //already hashed once above
218 //         //iterate remaining number:
219 //         for (int i = 0; i < iterations; i++) {
220 //             digest.reset();
221 //             hashed = digest.digest(hashed);
222 //         }
223 //         return hashed;
224 //     }
225 
226 //     /**
227 //      * Returns a hex-encoded string of the underlying {@link #getBytes byte array}.
228 //      * <p/>
229 //      * This implementation caches the resulting hex string so multiple calls to this method remain efficient.
230 //      * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the
231 //      * next time this method is called.
232 //      *
233 //      * @return a hex-encoded string of the underlying {@link #getBytes byte array}.
234 //      */
235 //     string toHex() {
236 //         if (this.hexEncoded  is null) {
237 //             this.hexEncoded = Hex.encodeToString(getBytes());
238 //         }
239 //         return this.hexEncoded;
240 //     }
241 
242 //     /**
243 //      * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}.
244 //      * <p/>
245 //      * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient.
246 //      * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the
247 //      * next time this method is called.
248 //      *
249 //      * @return a Base64-encoded string of the underlying {@link #getBytes byte array}.
250 //      */
251 //     string toBase64() {
252 //         if (this.base64Encoded  is null) {
253 //             //cache result in case this method is called multiple times.
254 //             this.base64Encoded = Base64.encodeToString(getBytes());
255 //         }
256 //         return this.base64Encoded;
257 //     }
258 
259 //     /**
260 //      * Simple implementation that merely returns {@link #toHex() toHex()}.
261 //      *
262 //      * @return the {@link #toHex() toHex()} value.
263 //      */
264 //     string toString() {
265 //         return toHex();
266 //     }
267 
268 //     /**
269 //      * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to
270 //      * this Hash's byte array, {@code false} otherwise.
271 //      *
272 //      * @param o the object (Hash) to check for equality.
273 //      * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to
274 //      *         this Hash's byte array, {@code false} otherwise.
275 //      */
276 //     boolean equals(Object o) {
277 //         auto oCast = cast(Hash)o;
278 //         if (oCast !is null) {
279 //             Hash other = oCast;
280 //             return MessageDigest.isEqual(getBytes(), other.getBytes());
281 //         }
282 //         return false;
283 //     }
284 
285 //     /**
286 //      * Simply returns toHex().hashCode();
287 //      *
288 //      * @return toHex().hashCode()
289 //      */
290 //     size_t toHash() @trusted nothrow {
291 //         if (this.bytes  is null || this.bytes.length == 0) {
292 //             return 0;
293 //         }
294 //         return ArrayHelper.hashCode(this.bytes);
295 //     }
296 
297 //     private static void printMainUsage(Class!AbstractHash clazz, string type) {
298 //         writeln("Prints an " ~ type ~ " hash value.");
299 //         writeln("Usage: java " ~ clazz.getName() ~ " [-base64] [-salt <saltValue>] [-times <N>] <valueToHash>");
300 //         writeln("Options:");
301 //         writeln("\t-base64\t\tPrints the hash value as a base64 string instead of the default hex.");
302 //         writeln("\t-salt\t\tSalts the hash with the specified <saltValue>");
303 //         writeln("\t-times\t\tHashes the input <N> number of times");
304 //     }
305 
306 //     private static boolean isReserved(string arg) {
307 //         return "-base64"  == arg || "-times"  == arg || "-salt"  == arg;
308 //     }
309 
310 //     static int doMain(Class!AbstractHash clazz, string[] args) {
311 //         string simple = clazz.getSimpleName();
312 //         int index = simple.indexOf("Hash");
313 //         string type = simple.substring(0, index).toUpperCase();
314 
315 //         if (args  is null || args.length < 1 || args.length > 7) {
316 //             printMainUsage(clazz, type);
317 //             return -1;
318 //         }
319 //         boolean hex = true;
320 //         string salt = null;
321 //         int times = 1;
322 //         string text = args[args.length - 1];
323 //         for (int i = 0; i < args.length; i++) {
324 //             string arg = args[i];
325 //             if (arg.equals("-base64")) {
326 //                 hex = false;
327 //             } else if (arg.equals("-salt")) {
328 //                 if ((i + 1) >= (args.length - 1)) {
329 //                     string msg = "Salt argument must be followed by a salt value.  The final argument is " ~
330 //                             "reserved for the value to hash.";
331 //                     writeln(msg);
332 //                     printMainUsage(clazz, type);
333 //                     return -1;
334 //                 }
335 //                 salt = args[i + 1];
336 //             } else if (arg.equals("-times")) {
337 //                 if ((i + 1) >= (args.length - 1)) {
338 //                     string msg = "Times argument must be followed by an integer value.  The final argument is " ~
339 //                             "reserved for the value to hash";
340 //                     writeln(msg);
341 //                     printMainUsage(clazz, type);
342 //                     return -1;
343 //                 }
344 //                 try {
345 //                     times = Integer.valueOf(args[i + 1]);
346 //                 } catch (NumberFormatException e) {
347 //                     string msg = "Times argument must be followed by an integer value.";
348 //                     writeln(msg);
349 //                     printMainUsage(clazz, type);
350 //                     return -1;
351 //                 }
352 //             }
353 //         }
354 
355 //         Hash hash = new Md2Hash(text, salt, times);
356 //         string hashed = hex ? hash.toHex() : hash.toBase64();
357 //         writeln(hex ? "Hex: " : "Base64: ");
358 //         writeln(hashed);
359 //         return 0;
360 //     }
361 }