obrimbaseapi/essential/visible/service/helper/hash/hash.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,
}
}