446 lines
11 KiB
Go
446 lines
11 KiB
Go
/*
|
|
|--------------------------------------------------------------------------
|
|
| Description
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Name:
|
|
| - Key
|
|
|
|
|
| Purpose:
|
|
| - Generate cryptographic key material through a type-driven dispatch
|
|
| model supporting symmetric and asymmetric key generation.
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Instruction
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Guideline:
|
|
| - Use the utility to generate cryptographically secure symmetric or
|
|
| asymmetric key material.
|
|
| - Use the "symmetric" type with the supported AES algorithm and
|
|
| 128, 192, or 256 bit key sizes.
|
|
| - Use the "asymmetric" type with the supported ECC algorithm using
|
|
| the EdDSA curve.
|
|
| - Provide the type-specific configuration required for the selected
|
|
| key generation workflow.
|
|
|
|
|
| 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"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
obrimKeyTypeSymmetric = "symmetric"
|
|
obrimKeyTypeAsymmetric = "asymmetric"
|
|
|
|
obrimKeyAlgorithmAES = "aes"
|
|
obrimKeyAlgorithmECC = "ecc"
|
|
obrimKeyCurveEdDSA = "eddsa"
|
|
|
|
obrimKeySize128 = 128
|
|
obrimKeySize192 = 192
|
|
obrimKeySize256 = 256
|
|
)
|
|
|
|
const (
|
|
obrimKeySuccessSymmetricGenerated = "SUCCESS_SYMMETRIC_KEY_GENERATED"
|
|
obrimKeySuccessAsymmetricGenerated = "SUCCESS_ASYMMETRIC_KEY_GENERATED"
|
|
|
|
obrimKeyFailureInvalidType = "FAILURE_INVALID_TYPE"
|
|
obrimKeyFailureInvalidConfig = "FAILURE_INVALID_CONFIG"
|
|
obrimKeyFailureMissingAlgorithm = "FAILURE_MISSING_ALGORITHM"
|
|
obrimKeyFailureInvalidAlgorithm = "FAILURE_INVALID_ALGORITHM"
|
|
obrimKeyFailureMissingKeySize = "FAILURE_MISSING_KEY_SIZE"
|
|
obrimKeyFailureInvalidKeySize = "FAILURE_INVALID_KEY_SIZE"
|
|
obrimKeyFailureKeyGeneration = "FAILURE_KEY_GENERATION"
|
|
obrimKeyFailureAsymmetricKeyGeneration = "FAILURE_ASYMMETRIC_KEY_GENERATION"
|
|
obrimKeyFailureUnsupportedOperation = "FAILURE_UNSUPPORTED_OPERATION"
|
|
)
|
|
|
|
const (
|
|
obrimKeyConfigAlgorithm = "algorithm"
|
|
obrimKeyConfigKeySize = "key_size"
|
|
)
|
|
|
|
type obrimKeySymmetricConfig struct {
|
|
algorithm string
|
|
keySize int
|
|
}
|
|
|
|
type obrimKeyAsymmetricConfig struct {
|
|
algorithm string
|
|
}
|
|
|
|
type obrimKeySymmetricPayload struct {
|
|
Type string `json:"type"`
|
|
Algorithm string `json:"algorithm"`
|
|
KeySize int `json:"key_size"`
|
|
KeyMaterial string `json:"key_material"`
|
|
GeneratedAt string `json:"generated_at"`
|
|
}
|
|
|
|
type obrimKeyAsymmetricPayload struct {
|
|
Type string `json:"type"`
|
|
Algorithm string `json:"algorithm"`
|
|
Curve string `json:"curve"`
|
|
PublicKey string `json:"public_key"`
|
|
PrivateKey string `json:"private_key"`
|
|
GeneratedAt string `json:"generated_at"`
|
|
}
|
|
|
|
type obrimKeyOutput struct {
|
|
Status bool `json:"status"`
|
|
Code string `json:"code"`
|
|
Payload any `json:"payload"`
|
|
}
|
|
|
|
func ObrimKey(keyType string, config map[string]any) map[string]any {
|
|
if code := obrimKeyValidateInput(keyType, config); code != "" {
|
|
return obrimKeyBuildOutput(false, code, nil)
|
|
}
|
|
|
|
return obrimKeyRouteRequest(keyType, config)
|
|
}
|
|
|
|
func obrimKeyValidateInput(keyType string, config map[string]any) string {
|
|
switch keyType {
|
|
case obrimKeyTypeSymmetric:
|
|
if config == nil {
|
|
return obrimKeyFailureInvalidConfig
|
|
}
|
|
|
|
algorithm, ok := config[obrimKeyConfigAlgorithm]
|
|
if !ok {
|
|
return obrimKeyFailureMissingAlgorithm
|
|
}
|
|
|
|
algorithmValue, ok := algorithm.(string)
|
|
if !ok || algorithmValue == "" {
|
|
return obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
|
|
switch algorithmValue {
|
|
case obrimKeyAlgorithmAES:
|
|
default:
|
|
return obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
|
|
keySize, ok := config[obrimKeyConfigKeySize]
|
|
if !ok {
|
|
return obrimKeyFailureMissingKeySize
|
|
}
|
|
|
|
switch value := keySize.(type) {
|
|
case int:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case int8:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case int16:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case int32:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case int64:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case uint:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case uint8:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case uint16:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case uint32:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
case uint64:
|
|
switch value {
|
|
case obrimKeySize128, obrimKeySize192, obrimKeySize256:
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
default:
|
|
return obrimKeyFailureInvalidKeySize
|
|
}
|
|
|
|
case obrimKeyTypeAsymmetric:
|
|
if config == nil {
|
|
return obrimKeyFailureInvalidConfig
|
|
}
|
|
|
|
algorithm, ok := config[obrimKeyConfigAlgorithm]
|
|
if !ok {
|
|
return obrimKeyFailureMissingAlgorithm
|
|
}
|
|
|
|
algorithmValue, ok := algorithm.(string)
|
|
if !ok || algorithmValue == "" {
|
|
return obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
|
|
switch algorithmValue {
|
|
case obrimKeyAlgorithmECC:
|
|
default:
|
|
return obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
|
|
default:
|
|
return obrimKeyFailureInvalidType
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func obrimKeyRouteRequest(keyType string, config map[string]any) map[string]any {
|
|
switch keyType {
|
|
case obrimKeyTypeSymmetric:
|
|
normalized, code := obrimKeyNormalizeSymmetricConfig(config)
|
|
if code != "" {
|
|
return obrimKeyBuildOutput(false, code, nil)
|
|
}
|
|
|
|
payload, code := obrimKeyGenerateSymmetric(normalized)
|
|
if code != "" {
|
|
return obrimKeyBuildOutput(false, code, nil)
|
|
}
|
|
|
|
return obrimKeyBuildOutput(
|
|
true,
|
|
obrimKeySuccessSymmetricGenerated,
|
|
payload,
|
|
)
|
|
|
|
case obrimKeyTypeAsymmetric:
|
|
normalized, code := obrimKeyNormalizeAsymmetricConfig(config)
|
|
if code != "" {
|
|
return obrimKeyBuildOutput(false, code, nil)
|
|
}
|
|
|
|
payload, code := obrimKeyGenerateAsymmetric(normalized)
|
|
if code != "" {
|
|
return obrimKeyBuildOutput(false, code, nil)
|
|
}
|
|
|
|
return obrimKeyBuildOutput(
|
|
true,
|
|
obrimKeySuccessAsymmetricGenerated,
|
|
payload,
|
|
)
|
|
|
|
default:
|
|
return obrimKeyBuildOutput(false, obrimKeyFailureUnsupportedOperation, nil)
|
|
}
|
|
}
|
|
|
|
func obrimKeyBuildOutput(status bool, code string, payload any) map[string]any {
|
|
output := obrimKeyOutput{
|
|
Status: status,
|
|
Code: code,
|
|
Payload: payload,
|
|
}
|
|
|
|
return map[string]any{
|
|
"status": output.Status,
|
|
"code": output.Code,
|
|
"payload": output.Payload,
|
|
}
|
|
}
|
|
|
|
func obrimKeyNormalizeSymmetricConfig(config map[string]any) (obrimKeySymmetricConfig, string) {
|
|
algorithm, ok := config[obrimKeyConfigAlgorithm].(string)
|
|
if !ok || algorithm == "" {
|
|
return obrimKeySymmetricConfig{}, obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
|
|
keySizeValue, ok := config[obrimKeyConfigKeySize]
|
|
if !ok {
|
|
return obrimKeySymmetricConfig{}, obrimKeyFailureMissingKeySize
|
|
}
|
|
|
|
keySize, ok := obrimKeyNormalizeKeySize(keySizeValue)
|
|
if !ok {
|
|
return obrimKeySymmetricConfig{}, obrimKeyFailureInvalidKeySize
|
|
}
|
|
|
|
return obrimKeySymmetricConfig{
|
|
algorithm: algorithm,
|
|
keySize: keySize,
|
|
}, ""
|
|
}
|
|
|
|
func obrimKeyNormalizeKeySize(value any) (int, bool) {
|
|
switch keySize := value.(type) {
|
|
case int:
|
|
return keySize, true
|
|
case int8:
|
|
return int(keySize), true
|
|
case int16:
|
|
return int(keySize), true
|
|
case int32:
|
|
return int(keySize), true
|
|
case int64:
|
|
return int(keySize), true
|
|
case uint:
|
|
return int(keySize), true
|
|
case uint8:
|
|
return int(keySize), true
|
|
case uint16:
|
|
return int(keySize), true
|
|
case uint32:
|
|
return int(keySize), true
|
|
case uint64:
|
|
return int(keySize), true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func obrimKeyGenerateSymmetric(config obrimKeySymmetricConfig) (map[string]any, string) {
|
|
switch config.algorithm {
|
|
case obrimKeyAlgorithmAES:
|
|
return obrimKeyGenerateAES(config.keySize)
|
|
default:
|
|
return nil, obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
}
|
|
|
|
func obrimKeyGenerateAES(keySize int) (map[string]any, string) {
|
|
keyLength := keySize / 8
|
|
|
|
keyMaterial := make([]byte, keyLength)
|
|
if _, err := rand.Read(keyMaterial); err != nil {
|
|
return nil, obrimKeyFailureKeyGeneration
|
|
}
|
|
|
|
if _, err := aes.NewCipher(keyMaterial); err != nil {
|
|
return nil, obrimKeyFailureKeyGeneration
|
|
}
|
|
|
|
payload := obrimKeySymmetricPayload{
|
|
Type: obrimKeyTypeSymmetric,
|
|
Algorithm: obrimKeyAlgorithmAES,
|
|
KeySize: keySize,
|
|
KeyMaterial: base64.StdEncoding.EncodeToString(keyMaterial),
|
|
GeneratedAt: time.Now().UTC().Format(time.RFC3339Nano),
|
|
}
|
|
|
|
return map[string]any{
|
|
"type": payload.Type,
|
|
"algorithm": payload.Algorithm,
|
|
"key_size": payload.KeySize,
|
|
"key_material": payload.KeyMaterial,
|
|
"generated_at": payload.GeneratedAt,
|
|
}, ""
|
|
}
|
|
|
|
func obrimKeyNormalizeAsymmetricConfig(config map[string]any) (obrimKeyAsymmetricConfig, string) {
|
|
algorithm, ok := config[obrimKeyConfigAlgorithm].(string)
|
|
if !ok || algorithm == "" {
|
|
return obrimKeyAsymmetricConfig{}, obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
|
|
return obrimKeyAsymmetricConfig{
|
|
algorithm: algorithm,
|
|
}, ""
|
|
}
|
|
|
|
func obrimKeyGenerateAsymmetric(config obrimKeyAsymmetricConfig) (map[string]any, string) {
|
|
switch config.algorithm {
|
|
case obrimKeyAlgorithmECC:
|
|
return obrimKeyGenerateECC()
|
|
default:
|
|
return nil, obrimKeyFailureInvalidAlgorithm
|
|
}
|
|
}
|
|
|
|
func obrimKeyGenerateECC() (map[string]any, string) {
|
|
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return nil, obrimKeyFailureAsymmetricKeyGeneration
|
|
}
|
|
|
|
payload := obrimKeyAsymmetricPayload{
|
|
Type: obrimKeyTypeAsymmetric,
|
|
Algorithm: obrimKeyAlgorithmECC,
|
|
Curve: obrimKeyCurveEdDSA,
|
|
PublicKey: base64.StdEncoding.EncodeToString(publicKey),
|
|
PrivateKey: base64.StdEncoding.EncodeToString(privateKey),
|
|
GeneratedAt: time.Now().UTC().Format(time.RFC3339Nano),
|
|
}
|
|
|
|
return map[string]any{
|
|
"type": payload.Type,
|
|
"algorithm": payload.Algorithm,
|
|
"curve": payload.Curve,
|
|
"public_key": payload.PublicKey,
|
|
"private_key": payload.PrivateKey,
|
|
"generated_at": payload.GeneratedAt,
|
|
}, ""
|
|
}
|