obrimbaseapi/essential/visible/service/helper/key/key.go

400 lines
10 KiB
Go

/*
|--------------------------------------------------------------------------
| Metadata
|--------------------------------------------------------------------------
|
| Name:
| - Key
|
| Purpose:
| - Generate cryptographic key material through a type-driven
| orchestration workflow with strict validation, schema isolation,
| and algorithm-specific generation routines.
|
| Guideline:
| - Call ObrimKey() as the single public entry point.
| - Provide an explicit generation type and configuration.
| - Use "symmetric" type for AES key generation.
| - Use "asymmetric" type for ECC EdDSA key pair generation.
| - Provide only supported configuration fields.
| - Complete configuration validation before generation.
| - Consume generated key material only from successful responses.
| - Check response status and code before accessing payload data.
|
| Example:
| - ObrimKey(
| "symmetric",
| map[string]any{
| "algorithm": "aes",
| "key_size": 256,
| },
| )
|
| - ObrimKey(
| "asymmetric",
| map[string]any{
| "algorithm": "ecc",
| },
| )
|
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Credit
|--------------------------------------------------------------------------
|
| Contributor:
| - Rajon Ahmed
| - Blockonite
|
|--------------------------------------------------------------------------
*/
package key
import (
"crypto/aes"
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"strings"
"time"
)
const (
obrimKeyTypeSymmetric = "symmetric"
obrimKeyTypeAsymmetric = "asymmetric"
obrimKeyAlgorithmAES = "aes"
obrimKeyAlgorithmECC = "ecc"
obrimKeyCurveEdDSA = "eddsa"
ObrimKeyCodeSuccessSymmetricGenerated = "SUCCESS_SYMMETRIC_KEY_GENERATED"
ObrimKeyCodeSuccessAsymmetricGenerated = "SUCCESS_ASYMMETRIC_KEY_GENERATED"
ObrimKeyCodeFailedMissingType = "FAILED_MISSING_TYPE"
ObrimKeyCodeFailedInvalidType = "FAILED_INVALID_TYPE"
ObrimKeyCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
ObrimKeyCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
ObrimKeyCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
ObrimKeyCodeFailedUnsupportedField = "FAILED_UNSUPPORTED_CONFIGURATION_FIELD"
ObrimKeyCodeFailedMissingAlgorithm = "FAILED_MISSING_ALGORITHM"
ObrimKeyCodeFailedInvalidAlgorithm = "FAILED_INVALID_ALGORITHM"
ObrimKeyCodeFailedUnsupportedAlgorithm = "FAILED_UNSUPPORTED_ALGORITHM"
ObrimKeyCodeFailedMissingKeySize = "FAILED_MISSING_KEY_SIZE"
ObrimKeyCodeFailedInvalidKeySize = "FAILED_INVALID_KEY_SIZE"
ObrimKeyCodeFailedUnsupportedKeySize = "FAILED_UNSUPPORTED_KEY_SIZE"
ObrimKeyCodeFailedKeyGeneration = "FAILED_KEY_GENERATION"
)
type ObrimKeyResponse struct {
Status bool `json:"status"`
Code string `json:"code"`
Payload map[string]any `json:"payload"`
}
// obrimKeyAllowedSymmetricFields stores allowed symmetric configuration fields.
var obrimKeyAllowedSymmetricFields = map[string]struct{}{
"algorithm": {},
"key_size": {},
}
// obrimKeyAllowedAsymmetricFields stores allowed asymmetric configuration fields.
var obrimKeyAllowedAsymmetricFields = map[string]struct{}{
"algorithm": {},
}
// ObrimKey dispatches key generation requests and orchestrates validation, generation, and response construction.
func ObrimKey(keyType string, config map[string]any) map[string]any {
normalizedType, errorResponse := obrimKeyValidate(keyType, config)
if errorResponse != nil {
return errorResponse
}
return obrimKeyDispatch(normalizedType, config)
}
// obrimKeyValidate validates common request structure and schema integrity.
func obrimKeyValidate(
keyType string,
config map[string]any,
) (string, map[string]any) {
if strings.TrimSpace(keyType) == "" {
return "", obrimKeyBuildError(ObrimKeyCodeFailedMissingType)
}
if config == nil {
return "", obrimKeyBuildError(ObrimKeyCodeFailedMissingConfig)
}
normalizedType := strings.ToLower(strings.TrimSpace(keyType))
switch normalizedType {
case obrimKeyTypeSymmetric:
if errorCode := obrimKeyValidateSymmetricConfig(config); errorCode != "" {
return "", obrimKeyBuildError(errorCode)
}
case obrimKeyTypeAsymmetric:
if errorCode := obrimKeyValidateAsymmetricConfig(config); errorCode != "" {
return "", obrimKeyBuildError(errorCode)
}
default:
return "", obrimKeyBuildError(ObrimKeyCodeFailedUnsupportedType)
}
return normalizedType, nil
}
// obrimKeyDispatch routes execution to the appropriate cryptographic workflow.
func obrimKeyDispatch(
keyType string,
config map[string]any,
) map[string]any {
switch keyType {
case obrimKeyTypeSymmetric:
return obrimKeyGenerateSymmetric(config)
case obrimKeyTypeAsymmetric:
return obrimKeyGenerateAsymmetric(config)
default:
return obrimKeyBuildError(ObrimKeyCodeFailedUnsupportedType)
}
}
// obrimKeyValidateSymmetricConfig validates symmetric configuration values.
func obrimKeyValidateSymmetricConfig(config map[string]any) string {
for field := range config {
if _, exists := obrimKeyAllowedSymmetricFields[field]; !exists {
return ObrimKeyCodeFailedUnsupportedField
}
}
algorithmValue, exists := config["algorithm"]
if !exists {
return ObrimKeyCodeFailedMissingAlgorithm
}
algorithm, ok := algorithmValue.(string)
if !ok {
return ObrimKeyCodeFailedInvalidAlgorithm
}
algorithm = strings.ToLower(strings.TrimSpace(algorithm))
if algorithm != obrimKeyAlgorithmAES {
return ObrimKeyCodeFailedUnsupportedAlgorithm
}
keySizeValue, exists := config["key_size"]
if !exists {
return ObrimKeyCodeFailedMissingKeySize
}
keySize, ok := obrimKeyNormalizeInteger(keySizeValue)
if !ok {
return ObrimKeyCodeFailedInvalidKeySize
}
switch keySize {
case 128, 192, 256:
return ""
default:
return ObrimKeyCodeFailedUnsupportedKeySize
}
}
// obrimKeyValidateAsymmetricConfig validates asymmetric configuration values.
func obrimKeyValidateAsymmetricConfig(config map[string]any) string {
for field := range config {
if _, exists := obrimKeyAllowedAsymmetricFields[field]; !exists {
return ObrimKeyCodeFailedUnsupportedField
}
}
algorithmValue, exists := config["algorithm"]
if !exists {
return ObrimKeyCodeFailedMissingAlgorithm
}
algorithm, ok := algorithmValue.(string)
if !ok {
return ObrimKeyCodeFailedInvalidAlgorithm
}
algorithm = strings.ToLower(strings.TrimSpace(algorithm))
if algorithm != obrimKeyAlgorithmECC {
return ObrimKeyCodeFailedUnsupportedAlgorithm
}
return ""
}
// obrimKeyGenerateSymmetric generates symmetric key material.
func obrimKeyGenerateSymmetric(config map[string]any) map[string]any {
normalizedConfig := obrimKeyNormalizeSymmetricConfig(config)
payload, errorCode := obrimKeyGenerateAES(normalizedConfig)
if errorCode != "" {
return obrimKeyBuildError(errorCode)
}
return obrimKeyBuildPayload(
ObrimKeyCodeSuccessSymmetricGenerated,
payload,
)
}
// obrimKeyGenerateAES generates AES key material.
func obrimKeyGenerateAES(
config map[string]any,
) (map[string]any, string) {
keySize := config["key_size"].(int)
// obrimKeyMaterial stores generated AES key bytes.
var obrimKeyMaterial = make([]byte, keySize/8)
if _, err := rand.Read(obrimKeyMaterial); err != nil {
return nil, ObrimKeyCodeFailedKeyGeneration
}
if _, err := aes.NewCipher(obrimKeyMaterial); err != nil {
return nil, ObrimKeyCodeFailedKeyGeneration
}
return map[string]any{
"type": obrimKeyTypeSymmetric,
"algorithm": obrimKeyAlgorithmAES,
"key_size": keySize,
"key_material": base64.StdEncoding.EncodeToString(obrimKeyMaterial),
"generated_at": time.Now().UTC().Format(time.RFC3339),
}, ""
}
// obrimKeyNormalizeSymmetricConfig normalizes symmetric configuration values.
func obrimKeyNormalizeSymmetricConfig(
config map[string]any,
) map[string]any {
keySize, _ := obrimKeyNormalizeInteger(config["key_size"])
return map[string]any{
"algorithm": strings.ToLower(
strings.TrimSpace(config["algorithm"].(string)),
),
"key_size": keySize,
}
}
// obrimKeyGenerateAsymmetric generates asymmetric key material.
func obrimKeyGenerateAsymmetric(config map[string]any) map[string]any {
normalizedConfig := obrimKeyNormalizeAsymmetricConfig(config)
payload, errorCode := obrimKeyGenerateECC(normalizedConfig)
if errorCode != "" {
return obrimKeyBuildError(errorCode)
}
return obrimKeyBuildPayload(
ObrimKeyCodeSuccessAsymmetricGenerated,
payload,
)
}
// obrimKeyGenerateECC generates ECC key pair material.
func obrimKeyGenerateECC(
config map[string]any,
) (map[string]any, string) {
_ = config
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, ObrimKeyCodeFailedKeyGeneration
}
return map[string]any{
"type": obrimKeyTypeAsymmetric,
"algorithm": obrimKeyAlgorithmECC,
"curve": obrimKeyCurveEdDSA,
"public_key": base64.StdEncoding.EncodeToString(publicKey),
"private_key": base64.StdEncoding.EncodeToString(privateKey),
"generated_at": time.Now().UTC().Format(time.RFC3339),
}, ""
}
// obrimKeyNormalizeAsymmetricConfig normalizes asymmetric configuration values.
func obrimKeyNormalizeAsymmetricConfig(
config map[string]any,
) map[string]any {
return map[string]any{
"algorithm": strings.ToLower(
strings.TrimSpace(config["algorithm"].(string)),
),
}
}
// obrimKeyBuildPayload constructs standardized success payloads.
func obrimKeyBuildPayload(
code string,
payload map[string]any,
) map[string]any {
return map[string]any{
"status": true,
"code": code,
"payload": payload,
}
}
// obrimKeyBuildError constructs standardized error responses.
func obrimKeyBuildError(code string) map[string]any {
return map[string]any{
"status": false,
"code": code,
"payload": nil,
}
}
// obrimKeyNormalizeInteger normalizes integer values.
func obrimKeyNormalizeInteger(value any) (int, bool) {
switch normalizedValue := value.(type) {
case int:
return normalizedValue, true
case int8:
return int(normalizedValue), true
case int16:
return int(normalizedValue), true
case int32:
return int(normalizedValue), true
case int64:
return int(normalizedValue), true
case uint:
return int(normalizedValue), true
case uint8:
return int(normalizedValue), true
case uint16:
return int(normalizedValue), true
case uint32:
return int(normalizedValue), true
case uint64:
return int(normalizedValue), true
default:
return 0, false
}
}