2018-04-25 06:17:20 +00:00
|
|
|
package scha
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"crypto/sha512"
|
2018-04-25 06:20:57 +00:00
|
|
|
"git.xdrm.io/schastsp/util/xor"
|
2018-04-25 06:17:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
/* (0) Static
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
/* (1) Constants */
|
|
|
|
const HSIZE uint16 = sha512.Size;
|
|
|
|
const HBITSIZE uint32 = uint32(HSIZE) * 8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* (1) Basic hash function
|
|
|
|
*
|
|
|
|
* @input<[]byte> Byte array input
|
|
|
|
*
|
|
|
|
* @return digest<[]byte]> Byte array digest
|
|
|
|
*
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
func hash(input []byte) []byte{
|
|
|
|
|
|
|
|
/* (1) Create sha512 hasher */
|
|
|
|
hasher := sha512.New();
|
|
|
|
|
|
|
|
/* (2) Defer memory cleanup */
|
|
|
|
defer hasher.Reset();
|
|
|
|
|
|
|
|
/* (3) Set input to be hashed */
|
|
|
|
hasher.Write(input);
|
|
|
|
|
|
|
|
/* (4) Extract digest */
|
|
|
|
return hasher.Sum(nil);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* (2) Public hashing interface
|
|
|
|
*
|
|
|
|
* @input<[]byte> Byte array input
|
|
|
|
* @depth<uint> Number of time to hash recursively (must be > 1)
|
|
|
|
* @salt<[]byte> [OPT] Optional salt
|
|
|
|
* @pepper<[]byte> [OPT] Optional pepper
|
|
|
|
*
|
|
|
|
* @return digest<[]byte]> Byte array digest
|
|
|
|
* @return err<error> If consistence error
|
|
|
|
*
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
func Hash(input []byte, depth uint16, options... []byte) ([]byte, error) {
|
|
|
|
|
|
|
|
|
|
|
|
/* (1) Manage errors errors
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
/* (1) Avoid no depth (no hash at all) */
|
|
|
|
if( depth < 1 ){
|
|
|
|
return nil, errors.New("Cannot use a 'depth' of zero. This is inconsistent and means that no hash will be processed");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (2) Avoir empty input */
|
|
|
|
if( len(input) < 1 ){
|
|
|
|
return nil, errors.New("Cannot use an empty 'input'. This is inconsistent");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* (2) Extract optional arguments
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
/* (1) Optional salt */
|
|
|
|
salt := make([]byte, 0)
|
|
|
|
if len(options) > 0 { salt = options[0] }
|
|
|
|
|
|
|
|
/* (2) Optional pepper */
|
|
|
|
pepper := make([]byte, 0)
|
|
|
|
if len(options) > 0 { pepper = options[0] }
|
|
|
|
|
|
|
|
|
|
|
|
/* (3) Process cyclic hash
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
/* (1) Initialise digest */
|
|
|
|
digest := make([]byte, 0, HSIZE)
|
|
|
|
|
|
|
|
/* (2) Process first hash + salt */
|
|
|
|
digest = hash( xor.ByteArray(input, salt) )
|
|
|
|
|
|
|
|
/* (3) Iterate @depth times */
|
|
|
|
for depth--; depth > 0; depth-- {
|
|
|
|
|
|
|
|
// Add Pepper only for last time
|
|
|
|
if( depth == 1 ){
|
|
|
|
digest = hash( xor.ByteArray(digest, pepper) )
|
|
|
|
} else {
|
|
|
|
digest = hash(digest)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return digest, nil
|
|
|
|
}
|