595 lines
14 KiB
Go
595 lines
14 KiB
Go
/*
|
|
|--------------------------------------------------------------------------
|
|
| Metadata
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Name:
|
|
| - Hash
|
|
|
|
|
| Purpose:
|
|
| - Generate and verify cryptographic hash values using standard
|
|
| or salted hashing methods for integrity verification,
|
|
| canonical fingerprinting, resource identification,
|
|
| comparison workflows, and security-oriented hashing
|
|
| requirements.
|
|
|
|
|
| Guideline:
|
|
| - Call ObrimHash() as the single public entry point.
|
|
| - Provide exactly two primary parameters: type and config.
|
|
| - Use type "calculate" to generate hash values.
|
|
| - Use type "compare" to verify hash values.
|
|
| - Use mode "standard" for deterministic hashing.
|
|
| - Use mode "salted" for salt-assisted hashing.
|
|
| - Use only supported algorithms for the selected mode.
|
|
| - Preserve generated salt values when future verification
|
|
| is required.
|
|
| - Always evaluate the returned status and code values before
|
|
| consuming payload data.
|
|
|
|
|
| Example:
|
|
| - ObrimHash(
|
|
| "calculate",
|
|
| map[string]any{
|
|
| "mode": "standard",
|
|
| "algorithm": "sha256",
|
|
| "value": "example",
|
|
| },
|
|
| )
|
|
|
|
|
| - ObrimHash(
|
|
| "calculate",
|
|
| map[string]any{
|
|
| "mode": "salted",
|
|
| "algorithm": "salted_sha512",
|
|
| "value": "example",
|
|
| },
|
|
| )
|
|
|
|
|
| - ObrimHash(
|
|
| "compare",
|
|
| map[string]any{
|
|
| "mode": "standard",
|
|
| "algorithm": "sha256",
|
|
| "value": "example",
|
|
| "hash": "<hash>",
|
|
| },
|
|
| )
|
|
|
|
|
| - ObrimHash(
|
|
| "compare",
|
|
| map[string]any{
|
|
| "mode": "salted",
|
|
| "algorithm": "salted_sha512",
|
|
| "value": "example",
|
|
| "hash": "<hash>",
|
|
| "salt": "<salt>",
|
|
| },
|
|
| )
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Credit
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Contributor:
|
|
| - Rajon Ahmed
|
|
| - Blockonite
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
package hash
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"encoding/hex"
|
|
)
|
|
|
|
const (
|
|
obrimHashTypeCalculate = "calculate"
|
|
obrimHashTypeCompare = "compare"
|
|
|
|
obrimHashModeStandard = "standard"
|
|
obrimHashModeSalted = "salted"
|
|
|
|
obrimHashAlgorithmSHA256 = "sha256"
|
|
obrimHashAlgorithmSHA512 = "sha512"
|
|
obrimHashAlgorithmSaltedSHA256 = "salted_sha256"
|
|
obrimHashAlgorithmSaltedSHA512 = "salted_sha512"
|
|
|
|
ObrimHashCodeSuccessHashGenerated = "SUCCESS_HASH_GENERATED"
|
|
ObrimHashCodeSuccessHashCompared = "SUCCESS_HASH_COMPARED"
|
|
|
|
ObrimHashCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
|
ObrimHashCodeFailedUnsupportedMode = "FAILED_UNSUPPORTED_MODE"
|
|
ObrimHashCodeFailedUnsupportedAlgorithm = "FAILED_UNSUPPORTED_ALGORITHM"
|
|
ObrimHashCodeFailedUnsupportedConfigKey = "FAILED_UNSUPPORTED_CONFIG_KEY"
|
|
ObrimHashCodeFailedMissingMode = "FAILED_MISSING_MODE"
|
|
ObrimHashCodeFailedMissingAlgorithm = "FAILED_MISSING_ALGORITHM"
|
|
ObrimHashCodeFailedMissingValue = "FAILED_MISSING_VALUE"
|
|
ObrimHashCodeFailedMissingHash = "FAILED_MISSING_HASH"
|
|
ObrimHashCodeFailedMissingSalt = "FAILED_MISSING_SALT"
|
|
ObrimHashCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
|
ObrimHashCodeFailedSaltNotAllowed = "FAILED_SALT_NOT_ALLOWED"
|
|
ObrimHashCodeFailedHashGeneration = "FAILED_HASH_GENERATION"
|
|
ObrimHashCodeFailedSaltGeneration = "FAILED_SALT_GENERATION"
|
|
)
|
|
|
|
// ObrimHashResponse defines the standard utility response.
|
|
type ObrimHashResponse struct {
|
|
Status bool `json:"status"`
|
|
Code string `json:"code"`
|
|
Payload any `json:"payload"`
|
|
}
|
|
|
|
// ObrimHashConfig defines utility configuration.
|
|
type ObrimHashConfig struct {
|
|
Mode string
|
|
Algorithm string
|
|
Value string
|
|
Hash string
|
|
Salt string
|
|
}
|
|
|
|
// ObrimHashCalculateStandardPayload defines calculate standard payload.
|
|
type ObrimHashCalculateStandardPayload struct {
|
|
Algorithm string `json:"algorithm"`
|
|
Hash string `json:"hash"`
|
|
}
|
|
|
|
// ObrimHashCalculateSaltedPayload defines calculate salted payload.
|
|
type ObrimHashCalculateSaltedPayload struct {
|
|
Algorithm string `json:"algorithm"`
|
|
Hash string `json:"hash"`
|
|
Salt string `json:"salt"`
|
|
}
|
|
|
|
// ObrimHashCompareStandardPayload defines compare standard payload.
|
|
type ObrimHashCompareStandardPayload struct {
|
|
Algorithm string `json:"algorithm"`
|
|
Matched bool `json:"matched"`
|
|
Hash string `json:"hash"`
|
|
}
|
|
|
|
// ObrimHashCompareSaltedPayload defines compare salted payload.
|
|
type ObrimHashCompareSaltedPayload struct {
|
|
Algorithm string `json:"algorithm"`
|
|
Matched bool `json:"matched"`
|
|
Hash string `json:"hash"`
|
|
Salt string `json:"salt"`
|
|
}
|
|
|
|
// obrimHashAllowedConfigKeys stores supported configuration keys.
|
|
var obrimHashAllowedConfigKeys = map[string]struct{}{
|
|
"mode": {},
|
|
"algorithm": {},
|
|
"value": {},
|
|
"hash": {},
|
|
"salt": {},
|
|
}
|
|
|
|
// ObrimHash processes hash generation and verification requests.
|
|
func ObrimHash(
|
|
hashType string,
|
|
config map[string]any,
|
|
) map[string]any {
|
|
if !obrimHashValidateType(hashType) {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedUnsupportedType,
|
|
)
|
|
}
|
|
|
|
hashConfig, errorCode := obrimHashValidateConfig(
|
|
hashType,
|
|
config,
|
|
)
|
|
if errorCode != "" {
|
|
return obrimHashBuildErrorPayload(errorCode)
|
|
}
|
|
|
|
if !obrimHashValidateMode(hashConfig.Mode) {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedUnsupportedMode,
|
|
)
|
|
}
|
|
|
|
if !obrimHashValidateAlgorithm(
|
|
hashConfig.Mode,
|
|
hashConfig.Algorithm,
|
|
) {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedUnsupportedAlgorithm,
|
|
)
|
|
}
|
|
|
|
switch hashType {
|
|
case obrimHashTypeCalculate:
|
|
return obrimHashCalculate(hashConfig)
|
|
|
|
case obrimHashTypeCompare:
|
|
return obrimHashCompare(hashConfig)
|
|
|
|
default:
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedUnsupportedType,
|
|
)
|
|
}
|
|
}
|
|
|
|
// obrimHashValidateType validates operation type.
|
|
func obrimHashValidateType(hashType string) bool {
|
|
switch hashType {
|
|
case obrimHashTypeCalculate:
|
|
return true
|
|
|
|
case obrimHashTypeCompare:
|
|
return true
|
|
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// obrimHashValidateMode validates hashing mode.
|
|
func obrimHashValidateMode(mode string) bool {
|
|
switch mode {
|
|
case obrimHashModeStandard:
|
|
return true
|
|
|
|
case obrimHashModeSalted:
|
|
return true
|
|
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// obrimHashValidateAlgorithm validates hashing algorithm selection.
|
|
func obrimHashValidateAlgorithm(
|
|
mode string,
|
|
algorithm string,
|
|
) bool {
|
|
switch mode {
|
|
case obrimHashModeStandard:
|
|
switch algorithm {
|
|
case obrimHashAlgorithmSHA256:
|
|
return true
|
|
|
|
case obrimHashAlgorithmSHA512:
|
|
return true
|
|
}
|
|
|
|
case obrimHashModeSalted:
|
|
switch algorithm {
|
|
case obrimHashAlgorithmSaltedSHA256:
|
|
return true
|
|
|
|
case obrimHashAlgorithmSaltedSHA512:
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// obrimHashValidateConfig validates configuration keys and values.
|
|
func obrimHashValidateConfig(
|
|
hashType string,
|
|
config map[string]any,
|
|
) (ObrimHashConfig, string) {
|
|
var hashConfig ObrimHashConfig
|
|
|
|
if config == nil {
|
|
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
|
}
|
|
|
|
for key := range config {
|
|
if _, exists := obrimHashAllowedConfigKeys[key]; !exists {
|
|
return hashConfig, ObrimHashCodeFailedUnsupportedConfigKey
|
|
}
|
|
}
|
|
|
|
modeValue, ok := config["mode"].(string)
|
|
if !ok || modeValue == "" {
|
|
return hashConfig, ObrimHashCodeFailedMissingMode
|
|
}
|
|
|
|
algorithmValue, ok := config["algorithm"].(string)
|
|
if !ok || algorithmValue == "" {
|
|
return hashConfig, ObrimHashCodeFailedMissingAlgorithm
|
|
}
|
|
|
|
valueValue, ok := config["value"].(string)
|
|
if !ok || valueValue == "" {
|
|
return hashConfig, ObrimHashCodeFailedMissingValue
|
|
}
|
|
|
|
hashConfig.Mode = modeValue
|
|
hashConfig.Algorithm = algorithmValue
|
|
hashConfig.Value = valueValue
|
|
|
|
if hashValue, exists := config["hash"]; exists {
|
|
hashText, ok := hashValue.(string)
|
|
if !ok {
|
|
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
|
}
|
|
|
|
hashConfig.Hash = hashText
|
|
}
|
|
|
|
if saltValue, exists := config["salt"]; exists {
|
|
saltText, ok := saltValue.(string)
|
|
if !ok {
|
|
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
|
}
|
|
|
|
hashConfig.Salt = saltText
|
|
}
|
|
|
|
switch hashType {
|
|
case obrimHashTypeCalculate:
|
|
if hashConfig.Mode == obrimHashModeStandard &&
|
|
hashConfig.Salt != "" {
|
|
return hashConfig, ObrimHashCodeFailedSaltNotAllowed
|
|
}
|
|
|
|
case obrimHashTypeCompare:
|
|
if hashConfig.Hash == "" {
|
|
return hashConfig, ObrimHashCodeFailedMissingHash
|
|
}
|
|
|
|
if hashConfig.Mode == obrimHashModeStandard &&
|
|
hashConfig.Salt != "" {
|
|
return hashConfig, ObrimHashCodeFailedSaltNotAllowed
|
|
}
|
|
|
|
if hashConfig.Mode == obrimHashModeSalted &&
|
|
hashConfig.Salt == "" {
|
|
return hashConfig, ObrimHashCodeFailedMissingSalt
|
|
}
|
|
}
|
|
|
|
return hashConfig, ""
|
|
}
|
|
|
|
// obrimHashGenerateHash executes the selected hashing algorithm.
|
|
func obrimHashGenerateHash(
|
|
algorithm string,
|
|
value string,
|
|
salt string,
|
|
) (string, bool) {
|
|
switch algorithm {
|
|
case obrimHashAlgorithmSHA256:
|
|
sum := sha256.Sum256([]byte(value))
|
|
return hex.EncodeToString(sum[:]), true
|
|
|
|
case obrimHashAlgorithmSHA512:
|
|
sum := sha512.Sum512([]byte(value))
|
|
return hex.EncodeToString(sum[:]), true
|
|
|
|
case obrimHashAlgorithmSaltedSHA256:
|
|
sum := sha256.Sum256([]byte(value + salt))
|
|
return hex.EncodeToString(sum[:]), true
|
|
|
|
case obrimHashAlgorithmSaltedSHA512:
|
|
sum := sha512.Sum512([]byte(value + salt))
|
|
return hex.EncodeToString(sum[:]), true
|
|
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
|
|
// obrimHashCalculate processes hash generation requests.
|
|
func obrimHashCalculate(
|
|
config ObrimHashConfig,
|
|
) map[string]any {
|
|
switch config.Mode {
|
|
case obrimHashModeStandard:
|
|
return obrimHashCalculateStandard(config)
|
|
|
|
case obrimHashModeSalted:
|
|
return obrimHashCalculateSalted(config)
|
|
|
|
default:
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedUnsupportedMode,
|
|
)
|
|
}
|
|
}
|
|
|
|
// obrimHashCalculateStandard generates a deterministic hash.
|
|
func obrimHashCalculateStandard(
|
|
config ObrimHashConfig,
|
|
) map[string]any {
|
|
hashValue, success := obrimHashGenerateHash(
|
|
config.Algorithm,
|
|
config.Value,
|
|
"",
|
|
)
|
|
if !success {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedHashGeneration,
|
|
)
|
|
}
|
|
|
|
payload := ObrimHashCalculateStandardPayload{
|
|
Algorithm: config.Algorithm,
|
|
Hash: hashValue,
|
|
}
|
|
|
|
return obrimHashBuildSuccessPayload(
|
|
ObrimHashCodeSuccessHashGenerated,
|
|
payload,
|
|
)
|
|
}
|
|
|
|
// obrimHashCalculateSalted generates a salted hash and salt pair.
|
|
func obrimHashCalculateSalted(
|
|
config ObrimHashConfig,
|
|
) map[string]any {
|
|
var activeSalt string
|
|
|
|
activeSalt = config.Salt
|
|
|
|
if activeSalt == "" {
|
|
generatedSalt, success := obrimHashGenerateSalt()
|
|
if !success {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedSaltGeneration,
|
|
)
|
|
}
|
|
|
|
activeSalt = generatedSalt
|
|
}
|
|
|
|
hashValue, success := obrimHashGenerateHash(
|
|
config.Algorithm,
|
|
config.Value,
|
|
activeSalt,
|
|
)
|
|
if !success {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedHashGeneration,
|
|
)
|
|
}
|
|
|
|
payload := ObrimHashCalculateSaltedPayload{
|
|
Algorithm: config.Algorithm,
|
|
Hash: hashValue,
|
|
Salt: activeSalt,
|
|
}
|
|
|
|
return obrimHashBuildSuccessPayload(
|
|
ObrimHashCodeSuccessHashGenerated,
|
|
payload,
|
|
)
|
|
}
|
|
|
|
// obrimHashGenerateSalt generates a cryptographically secure salt.
|
|
func obrimHashGenerateSalt() (string, bool) {
|
|
var saltBytes = make([]byte, 32)
|
|
|
|
_, err := rand.Read(saltBytes)
|
|
if err != nil {
|
|
return "", false
|
|
}
|
|
|
|
return hex.EncodeToString(saltBytes), true
|
|
}
|
|
|
|
// obrimHashCompare processes hash verification requests.
|
|
func obrimHashCompare(
|
|
config ObrimHashConfig,
|
|
) map[string]any {
|
|
switch config.Mode {
|
|
case obrimHashModeStandard:
|
|
return obrimHashCompareStandard(config)
|
|
|
|
case obrimHashModeSalted:
|
|
return obrimHashCompareSalted(config)
|
|
|
|
default:
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedUnsupportedMode,
|
|
)
|
|
}
|
|
}
|
|
|
|
// obrimHashCompareStandard regenerates a standard hash for verification.
|
|
func obrimHashCompareStandard(
|
|
config ObrimHashConfig,
|
|
) map[string]any {
|
|
regeneratedHash, success := obrimHashGenerateHash(
|
|
config.Algorithm,
|
|
config.Value,
|
|
"",
|
|
)
|
|
if !success {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedHashGeneration,
|
|
)
|
|
}
|
|
|
|
payload := ObrimHashCompareStandardPayload{
|
|
Algorithm: config.Algorithm,
|
|
Matched: obrimHashVerify(
|
|
regeneratedHash,
|
|
config.Hash,
|
|
),
|
|
Hash: config.Hash,
|
|
}
|
|
|
|
return obrimHashBuildSuccessPayload(
|
|
ObrimHashCodeSuccessHashCompared,
|
|
payload,
|
|
)
|
|
}
|
|
|
|
// obrimHashCompareSalted regenerates a salted hash for verification.
|
|
func obrimHashCompareSalted(
|
|
config ObrimHashConfig,
|
|
) map[string]any {
|
|
regeneratedHash, success := obrimHashGenerateHash(
|
|
config.Algorithm,
|
|
config.Value,
|
|
config.Salt,
|
|
)
|
|
if !success {
|
|
return obrimHashBuildErrorPayload(
|
|
ObrimHashCodeFailedHashGeneration,
|
|
)
|
|
}
|
|
|
|
payload := ObrimHashCompareSaltedPayload{
|
|
Algorithm: config.Algorithm,
|
|
Matched: obrimHashVerify(
|
|
regeneratedHash,
|
|
config.Hash,
|
|
),
|
|
Hash: config.Hash,
|
|
Salt: config.Salt,
|
|
}
|
|
|
|
return obrimHashBuildSuccessPayload(
|
|
ObrimHashCodeSuccessHashCompared,
|
|
payload,
|
|
)
|
|
}
|
|
|
|
// obrimHashVerify compares regenerated and supplied hash values.
|
|
func obrimHashVerify(
|
|
regeneratedHash string,
|
|
suppliedHash string,
|
|
) bool {
|
|
return regeneratedHash == suppliedHash
|
|
}
|
|
|
|
// obrimHashBuildSuccessPayload constructs success response payloads.
|
|
func obrimHashBuildSuccessPayload(
|
|
code string,
|
|
payload any,
|
|
) map[string]any {
|
|
return map[string]any{
|
|
"status": true,
|
|
"code": code,
|
|
"payload": payload,
|
|
}
|
|
}
|
|
|
|
// obrimHashBuildErrorPayload constructs error response payloads.
|
|
func obrimHashBuildErrorPayload(
|
|
code string,
|
|
) map[string]any {
|
|
return map[string]any{
|
|
"status": false,
|
|
"code": code,
|
|
"payload": nil,
|
|
}
|
|
}
|