400 lines
10 KiB
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
|
|
}
|
|
}
|