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.SimpleHash; 20 21 import hunt.shiro.crypto.hash.AbstractHash; 22 import hunt.shiro.crypto.hash.Hash; 23 24 import hunt.shiro.Exceptions; 25 import hunt.shiro.codec.Base64; 26 import hunt.shiro.codec.Hex; 27 import hunt.shiro.util.ByteSource; 28 import hunt.shiro.util.SimpleByteSource; 29 // import hunt.shiro.util.StringUtils; 30 31 // import hunt.security.MessageDigest; 32 // import java.security.NoSuchAlgorithmException; 33 34 import hunt.Exceptions; 35 import hunt.util.ArrayHelper; 36 37 import std.algorithm; 38 import std.array; 39 40 /** 41 * A {@code Hash} implementation that allows any {@link java.security.MessageDigest MessageDigest} algorithm name to 42 * be used. This class is a less type-safe variant than the other {@code AbstractHash} subclasses 43 * (e.g. {@link Sha512Hash}, etc), but it does allow for any algorithm name to be specified in case the other subclass 44 * implementations do not represent an algorithm that you may want to use. 45 * <p/> 46 * As of Shiro 1.1, this class effectively replaces the (now-deprecated) {@link AbstractHash} class. It subclasses 47 * {@code AbstractHash} only to retain backwards-compatibility. 48 * 49 */ 50 class SimpleHash : AbstractHash { 51 52 private enum int DEFAULT_ITERATIONS = 1; 53 54 /** 55 * The {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. 56 */ 57 private string algorithmName; 58 59 /** 60 * The hashed data 61 */ 62 private byte[] bytes; 63 64 /** 65 * Supplied salt, if any. 66 */ 67 private ByteSource salt; 68 69 /** 70 * Number of hash iterations to perform. Defaults to 1 in the constructor. 71 */ 72 private int iterations; 73 74 /** 75 * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. 76 */ 77 private string hexEncoded = null; 78 79 /** 80 * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. 81 */ 82 private string base64Encoded = null; 83 84 /** 85 * Creates an new instance with only its {@code algorithmName} set - no hashing is performed. 86 * <p/> 87 * Because all other constructors in this class hash the {@code source} constructor argument, this 88 * constructor is useful in scenarios when you have a byte array that you know is already hashed and 89 * just want to set the bytes in their raw form directly on an instance. After using this constructor, 90 * you can then immediately call {@link #setBytes setBytes} to have a fully-initialized instance. 91 * <p/> 92 * <b>N.B.</b>The algorithm identified by the {@code algorithmName} parameter must be available on the JVM. If it 93 * is not, a {@link UnknownAlgorithmException} will be thrown when the hash is performed (not at instantiation). 94 * 95 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 96 * performing the hash. 97 * @see UnknownAlgorithmException 98 */ 99 this(string algorithmName) { 100 this.algorithmName = algorithmName; 101 this.iterations = DEFAULT_ITERATIONS; 102 } 103 104 /** 105 * Creates an {@code algorithmName}-specific hash of the specified {@code source} with no {@code salt} using a 106 * single hash iteration. 107 * <p/> 108 * This is a convenience constructor that merely executes <code>this( algorithmName, source, null, 1);</code>. 109 * <p/> 110 * Please see the 111 * {@link #SimpleHash(string algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)} 112 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 113 * types. 114 * 115 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 116 * performing the hash. 117 * @param source the object to be hashed. 118 * @throws hunt.shiro.codec.CodecException 119 * if the specified {@code source} cannot be converted into a byte array (byte[]). 120 * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. 121 */ 122 this(string algorithmName, Object source) { 123 //noinspection NullableProblems 124 this(algorithmName, source, null, DEFAULT_ITERATIONS); 125 } 126 127 /** 128 * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} 129 * using a single hash iteration. 130 * <p/> 131 * It is a convenience constructor that merely executes <code>this( algorithmName, source, salt, 1);</code>. 132 * <p/> 133 * Please see the 134 * {@link #SimpleHash(string algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)} 135 * constructor for the types of Objects that may be passed into this constructor, as well as how to support further 136 * types. 137 * 138 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 139 * performing the hash. 140 * @param source the source object to be hashed. 141 * @param salt the salt to use for the hash 142 * @throws CodecException if either constructor argument cannot be converted into a byte array. 143 * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. 144 */ 145 this(string algorithmName, Object source, Object salt) { 146 this(algorithmName, source, salt, DEFAULT_ITERATIONS); 147 } 148 149 /** 150 * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given 151 * {@code salt} a total of {@code hashIterations} times. 152 * <p/> 153 * By default, this class only supports Object method arguments of 154 * type {@code byte[]}, {@code char[]}, {@link string}, {@link java.io.File File}, 155 * {@link java.io.InputStream InputStream} or {@link hunt.shiro.util.ByteSource ByteSource}. If either 156 * argument is anything other than these types a {@link hunt.shiro.codec.CodecException CodecException} 157 * will be thrown. 158 * <p/> 159 * If you want to be able to hash other object types, or use other salt types, you need to override the 160 * {@link #toBytes(Object) toBytes(Object)} method to support those specific types. Your other option is to 161 * convert your arguments to one of the default supported types first before passing them in to this 162 * constructor}. 163 * 164 * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when 165 * performing the hash. 166 * @param source the source object to be hashed. 167 * @param salt the salt to use for the hash 168 * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. 169 * @throws CodecException if either Object constructor argument cannot be converted into a byte array. 170 * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. 171 */ 172 this(string algorithmName, Object source, Object salt, int hashIterations) { 173 if (!algorithmName.empty()) { 174 throw new NullPointerException("algorithmName argument cannot be null or empty."); 175 } 176 this.algorithmName = algorithmName; 177 this.iterations = max(DEFAULT_ITERATIONS, hashIterations); 178 ByteSource saltBytes = null; 179 if (salt !is null) { 180 saltBytes = convertSaltToBytes(salt); 181 this.salt = saltBytes; 182 } 183 ByteSource sourceBytes = convertSourceToBytes(source); 184 hash(sourceBytes, saltBytes, hashIterations); 185 } 186 187 /** 188 * Acquires the specified {@code source} argument's bytes and returns them in the form of a {@code ByteSource} instance. 189 * <p/> 190 * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic 191 * conversion. Can be overridden by subclasses for source-specific conversion. 192 * 193 * @param source the source object to be hashed. 194 * @return the source's bytes in the form of a {@code ByteSource} instance. 195 */ 196 protected ByteSource convertSourceToBytes(Object source) { 197 return toByteSource(source); 198 } 199 200 /** 201 * Acquires the specified {@code salt} argument's bytes and returns them in the form of a {@code ByteSource} instance. 202 * <p/> 203 * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic 204 * conversion. Can be overridden by subclasses for salt-specific conversion. 205 * 206 * @param salt the salt to be use for the hash. 207 * @return the salt's bytes in the form of a {@code ByteSource} instance. 208 */ 209 protected ByteSource convertSaltToBytes(Object salt) { 210 return toByteSource(salt); 211 } 212 213 /** 214 * Converts a given object into a {@code ByteSource} instance. Assumes the object can be converted to bytes. 215 * 216 * @param o the Object to convert into a {@code ByteSource} instance. 217 * @return the {@code ByteSource} representation of the specified object's bytes. 218 */ 219 protected ByteSource toByteSource(Object o) { 220 if (o is null) { 221 return null; 222 } 223 auto oCast = cast(ByteSource)o; 224 if (oCast !is null) { 225 return oCast; 226 } 227 byte[] bytes = toBytes(o); 228 return ByteSourceUtil.bytes(bytes); 229 } 230 231 private void hash(ByteSource source, ByteSource salt, int hashIterations) { 232 byte[] saltBytes = salt !is null ? salt.getBytes() : null; 233 byte[] hashedBytes = hash(source.getBytes(), saltBytes, hashIterations); 234 setBytes(hashedBytes); 235 } 236 237 /** 238 * Returns the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. 239 * 240 * @return the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. 241 */ 242 string getAlgorithmName() { 243 return this.algorithmName; 244 } 245 246 ByteSource getSalt() { 247 return this.salt; 248 } 249 250 int getIterations() { 251 return this.iterations; 252 } 253 254 byte[] getBytes() { 255 return this.bytes; 256 } 257 258 /** 259 * Sets the raw bytes stored by this hash instance. 260 * <p/> 261 * The bytes are kept in raw form - they will not be hashed/changed. This is primarily a utility method for 262 * constructing a Hash instance when the hashed value is already known. 263 * 264 * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance. 265 */ 266 void setBytes(byte[] alreadyHashedBytes) { 267 this.bytes = alreadyHashedBytes; 268 this.hexEncoded = null; 269 this.base64Encoded = null; 270 } 271 272 /** 273 * Sets the iterations used to previously compute AN ALREADY GENERATED HASH. 274 * <p/> 275 * This is provided <em>ONLY</em> to reconstitute an already-created Hash instance. It should ONLY ever be 276 * invoked when re-constructing a hash instance from an already-hashed value. 277 * 278 * @param iterations the number of hash iterations used to previously create the hash/digest. 279 */ 280 void setIterations(int iterations) { 281 this.iterations = max(DEFAULT_ITERATIONS, iterations); 282 } 283 284 /** 285 * Sets the salt used to previously compute AN ALREADY GENERATED HASH. 286 * <p/> 287 * This is provided <em>ONLY</em> to reconstitute a Hash instance that has already been computed. It should ONLY 288 * ever be invoked when re-constructing a hash instance from an already-hashed value. 289 * 290 * @param salt the salt used to previously create the hash/digest. 291 */ 292 void setSalt(ByteSource salt) { 293 this.salt = salt; 294 } 295 296 /** 297 * Returns the JDK MessageDigest instance to use for executing the hash. 298 * 299 * @param algorithmName the algorithm to use for the hash, provided by subclasses. 300 * @return the MessageDigest object for the specified {@code algorithm}. 301 * @throws UnknownAlgorithmException if the specified algorithm name is not available. 302 */ 303 // protected MessageDigest getDigest(string algorithmName) { 304 // try { 305 // return MessageDigest.getInstance(algorithmName); 306 // } catch (NoSuchAlgorithmException e) { 307 // string msg = "No native '" ~ algorithmName ~ "' MessageDigest instance available on the current JVM."; 308 // throw new UnknownAlgorithmException(msg, e); 309 // } 310 // } 311 312 /** 313 * Hashes the specified byte array without a salt for a single iteration. 314 * 315 * @param bytes the bytes to hash. 316 * @return the hashed bytes. 317 * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. 318 */ 319 protected byte[] hash(byte[] bytes) { 320 return hash(bytes, null, DEFAULT_ITERATIONS); 321 } 322 323 /** 324 * Hashes the specified byte array using the given {@code salt} for a single iteration. 325 * 326 * @param bytes the bytes to hash 327 * @param salt the salt to use for the initial hash 328 * @return the hashed bytes 329 * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. 330 */ 331 protected byte[] hash(byte[] bytes, byte[] salt) { 332 return hash(bytes, salt, DEFAULT_ITERATIONS); 333 } 334 335 /** 336 * Hashes the specified byte array using the given {@code salt} for the specified number of iterations. 337 * 338 * @param bytes the bytes to hash 339 * @param salt the salt to use for the initial hash 340 * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency). 341 * @return the hashed bytes. 342 * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available. 343 */ 344 protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) { 345 // MessageDigest digest = getDigest(getAlgorithmName()); 346 // if (salt !is null) { 347 // digest.reset(); 348 // digest.update(salt); 349 // } 350 // byte[] hashed = digest.digest(bytes); 351 // int iterations = hashIterations - 1; //already hashed once above 352 // //iterate remaining number: 353 // for (int i = 0; i < iterations; i++) { 354 // digest.reset(); 355 // hashed = digest.digest(hashed); 356 // } 357 // return hashed; 358 implementationMissing(false); 359 return null; 360 } 361 362 bool isEmpty() { 363 return this.bytes is null || this.bytes.length == 0; 364 } 365 366 /** 367 * Returns a hex-encoded string of the underlying {@link #getBytes byte array}. 368 * <p/> 369 * This implementation caches the resulting hex string so multiple calls to this method remain efficient. 370 * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the 371 * next time this method is called. 372 * 373 * @return a hex-encoded string of the underlying {@link #getBytes byte array}. 374 */ 375 string toHex() { 376 if (this.hexEncoded is null) { 377 this.hexEncoded = Hex.encodeToString(getBytes()); 378 } 379 return this.hexEncoded; 380 } 381 382 /** 383 * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}. 384 * <p/> 385 * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient. 386 * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the 387 * next time this method is called. 388 * 389 * @return a Base64-encoded string of the underlying {@link #getBytes byte array}. 390 */ 391 string toBase64() { 392 if (this.base64Encoded is null) { 393 //cache result in case this method is called multiple times. 394 this.base64Encoded = Base64.encodeToString(getBytes()); 395 } 396 return this.base64Encoded; 397 } 398 399 /** 400 * Simple implementation that merely returns {@link #toHex() toHex()}. 401 * 402 * @return the {@link #toHex() toHex()} value. 403 */ 404 override string toString() { 405 return toHex(); 406 } 407 408 /** 409 * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to 410 * this Hash's byte array, {@code false} otherwise. 411 * 412 * @param o the object (Hash) to check for equality. 413 * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to 414 * this Hash's byte array, {@code false} otherwise. 415 */ 416 override bool opEquals(Object o) { 417 auto oCast = cast(Hash) o; 418 if (oCast !is null) { 419 Hash other = oCast; 420 // return MessageDigest.isEqual(getBytes(), other.getBytes()); 421 implementationMissing(false); 422 return false; 423 } 424 return false; 425 } 426 427 /** 428 * Simply returns toHex().hashCode(); 429 * 430 * @return toHex().hashCode() 431 */ 432 override size_t toHash() @trusted nothrow { 433 if (this.bytes is null || this.bytes.length == 0) { 434 return 0; 435 } 436 return hashOf(this.bytes); 437 } 438 }