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 }