upd: essential/file updated
helper cipher updated
This commit is contained in:
parent
964ddac31b
commit
c729c54c60
@ -0,0 +1,641 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Description
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Cipher
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide reusable AES-256 encryption and decryption for file content.
|
||||
| - Support standard and salted encryption formats through a unified interface.
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Instruction
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Guideline:
|
||||
| - Use encrypt-standard to encrypt file content using AES-256 without salt.
|
||||
| - Use encrypt-salted to encrypt file content using AES-256 with a cryptographically secure generated salt.
|
||||
| - Use decrypt-standard to decrypt AES-256 encrypted file content without salt metadata.
|
||||
| - Use decrypt-salted to decrypt self-contained encrypted files with embedded signature, version, salt, and ciphertext metadata.
|
||||
| - Supply the AES-256 compatible key, input file path, and output file path through the configuration.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimCipher("encrypt-standard", map[string]any{
|
||||
| "key": key,
|
||||
| "input": inputPath,
|
||||
| "output": outputPath,
|
||||
| })
|
||||
|
|
||||
| - ObrimCipher("encrypt-salted", map[string]any{
|
||||
| "key": key,
|
||||
| "input": inputPath,
|
||||
| "output": outputPath,
|
||||
| })
|
||||
|
|
||||
| - ObrimCipher("decrypt-standard", map[string]any{
|
||||
| "key": key,
|
||||
| "input": inputPath,
|
||||
| "output": outputPath,
|
||||
| })
|
||||
|
|
||||
| - ObrimCipher("decrypt-salted", map[string]any{
|
||||
| "key": key,
|
||||
| "input": inputPath,
|
||||
| "output": outputPath,
|
||||
| })
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Utility execution type identifiers.
|
||||
const (
|
||||
obrimCipherTypeEncryptStandard = "encrypt-standard"
|
||||
obrimCipherTypeEncryptSalted = "encrypt-salted"
|
||||
obrimCipherTypeDecryptStandard = "decrypt-standard"
|
||||
obrimCipherTypeDecryptSalted = "decrypt-salted"
|
||||
)
|
||||
|
||||
// Cipher format metadata values.
|
||||
const (
|
||||
obrimCipherAlgorithm = "AES-256-GCM"
|
||||
obrimCipherFormat = "OBRIM-CIPHER"
|
||||
obrimCipherVersion = "1"
|
||||
obrimCipherSaltSize = 32
|
||||
)
|
||||
|
||||
// Utility execution success result codes.
|
||||
const (
|
||||
obrimCipherSuccessEncryptStandard = "SUCCESS_ENCRYPT_STANDARD"
|
||||
obrimCipherSuccessEncryptSalted = "SUCCESS_ENCRYPT_SALTED"
|
||||
obrimCipherSuccessDecryptStandard = "SUCCESS_DECRYPT_STANDARD"
|
||||
obrimCipherSuccessDecryptSalted = "SUCCESS_DECRYPT_SALTED"
|
||||
)
|
||||
|
||||
// Utility execution failure result codes.
|
||||
const (
|
||||
obrimCipherFailureInvalidType = "FAILURE_INVALID_TYPE"
|
||||
obrimCipherFailureInvalidConfig = "FAILURE_INVALID_CONFIG"
|
||||
obrimCipherFailureMissingKey = "FAILURE_MISSING_KEY"
|
||||
obrimCipherFailureInvalidKey = "FAILURE_INVALID_KEY"
|
||||
obrimCipherFailureMissingInput = "FAILURE_MISSING_INPUT"
|
||||
obrimCipherFailureInvalidInput = "FAILURE_INVALID_INPUT"
|
||||
obrimCipherFailureMissingOutput = "FAILURE_MISSING_OUTPUT"
|
||||
obrimCipherFailureInvalidOutput = "FAILURE_INVALID_OUTPUT"
|
||||
obrimCipherFailureExternalSalt = "FAILURE_EXTERNAL_SALT"
|
||||
obrimCipherFailureReadInput = "FAILURE_READ_INPUT"
|
||||
obrimCipherFailureWriteOutput = "FAILURE_WRITE_OUTPUT"
|
||||
obrimCipherFailureCipherInitialization = "FAILURE_CIPHER_INITIALIZATION"
|
||||
obrimCipherFailureNonceGeneration = "FAILURE_NONCE_GENERATION"
|
||||
obrimCipherFailureEncryption = "FAILURE_ENCRYPTION"
|
||||
obrimCipherFailureDecryption = "FAILURE_DECRYPTION"
|
||||
obrimCipherFailureSaltGeneration = "FAILURE_SALT_GENERATION"
|
||||
obrimCipherFailureHeaderBuild = "FAILURE_HEADER_BUILD"
|
||||
obrimCipherFailureSignature = "FAILURE_INVALID_SIGNATURE"
|
||||
obrimCipherFailureVersion = "FAILURE_INVALID_VERSION"
|
||||
obrimCipherFailureSaltExtraction = "FAILURE_SALT_EXTRACTION"
|
||||
obrimCipherFailurePayloadExtraction = "FAILURE_PAYLOAD_EXTRACTION"
|
||||
)
|
||||
|
||||
// Standardized utility output.
|
||||
type obrimCipherOutput struct {
|
||||
Status bool
|
||||
Code string
|
||||
Payload any
|
||||
}
|
||||
|
||||
// Salted encrypted file header.
|
||||
type obrimCipherHeader struct {
|
||||
Signature []byte
|
||||
Version []byte
|
||||
Salt []byte
|
||||
}
|
||||
|
||||
// Standardized operation payload.
|
||||
type obrimCipherPayload struct {
|
||||
Operation string
|
||||
Algorithm string
|
||||
Format string
|
||||
Version string
|
||||
InputPath string
|
||||
OutputPath string
|
||||
}
|
||||
|
||||
// ObrimCipher executes encryption or decryption operations through a unified interface.
|
||||
func ObrimCipher(typeName string, config map[string]any) map[string]any {
|
||||
if code := obrimCipherValidateInput(typeName, config); code != "" {
|
||||
return obrimCipherBuildOutput(false, code, nil)
|
||||
}
|
||||
|
||||
payload, code := obrimCipherRouteRequest(typeName, config)
|
||||
if code != "" {
|
||||
return obrimCipherBuildOutput(false, code, nil)
|
||||
}
|
||||
|
||||
return obrimCipherBuildOutput(true, code, payload)
|
||||
}
|
||||
|
||||
// obrimCipherValidateInput validates the requested operation and its configuration.
|
||||
func obrimCipherValidateInput(typeName string, config map[string]any) string {
|
||||
switch typeName {
|
||||
case obrimCipherTypeEncryptStandard,
|
||||
obrimCipherTypeEncryptSalted,
|
||||
obrimCipherTypeDecryptStandard,
|
||||
obrimCipherTypeDecryptSalted:
|
||||
default:
|
||||
return obrimCipherFailureInvalidType
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
return obrimCipherFailureInvalidConfig
|
||||
}
|
||||
|
||||
key, ok := config["key"]
|
||||
if !ok || key == nil {
|
||||
return obrimCipherFailureMissingKey
|
||||
}
|
||||
|
||||
if !obrimCipherValidKey(key) {
|
||||
return obrimCipherFailureInvalidKey
|
||||
}
|
||||
|
||||
input, ok := config["input"]
|
||||
if !ok || input == nil {
|
||||
return obrimCipherFailureMissingInput
|
||||
}
|
||||
|
||||
inputPath, ok := input.(string)
|
||||
if !ok || inputPath == "" {
|
||||
return obrimCipherFailureInvalidInput
|
||||
}
|
||||
|
||||
output, ok := config["output"]
|
||||
if !ok || output == nil {
|
||||
return obrimCipherFailureMissingOutput
|
||||
}
|
||||
|
||||
outputPath, ok := output.(string)
|
||||
if !ok || outputPath == "" {
|
||||
return obrimCipherFailureInvalidOutput
|
||||
}
|
||||
|
||||
switch typeName {
|
||||
case obrimCipherTypeEncryptSalted, obrimCipherTypeDecryptSalted:
|
||||
if _, exists := config["salt"]; exists {
|
||||
return obrimCipherFailureExternalSalt
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimCipherRouteRequest routes a validated request to its operation-specific implementation.
|
||||
func obrimCipherRouteRequest(typeName string, config map[string]any) (map[string]any, string) {
|
||||
switch typeName {
|
||||
case obrimCipherTypeEncryptStandard:
|
||||
return obrimCipherEncryptStandard(config)
|
||||
case obrimCipherTypeEncryptSalted:
|
||||
return obrimCipherEncryptSalted(config)
|
||||
case obrimCipherTypeDecryptStandard:
|
||||
return obrimCipherDecryptStandard(config)
|
||||
case obrimCipherTypeDecryptSalted:
|
||||
return obrimCipherDecryptSalted(config)
|
||||
default:
|
||||
return nil, obrimCipherFailureInvalidType
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCipherBuildOutput builds the standardized utility output.
|
||||
func obrimCipherBuildOutput(status bool, code string, payload map[string]any) map[string]any {
|
||||
return map[string]any{
|
||||
"status": status,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCipherEncryptStandard encrypts file content without salt metadata.
|
||||
func obrimCipherEncryptStandard(config map[string]any) (map[string]any, string) {
|
||||
key, ok := obrimCipherKey(config["key"])
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidKey
|
||||
}
|
||||
|
||||
inputPath, outputPath, ok := obrimCipherPaths(config)
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidConfig
|
||||
}
|
||||
|
||||
plaintext, err := os.ReadFile(inputPath)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureReadInput
|
||||
}
|
||||
|
||||
ciphertext, code := obrimCipherEncrypt(key, plaintext, nil)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outputPath, ciphertext, 0600); err != nil {
|
||||
return nil, obrimCipherFailureWriteOutput
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"operation": obrimCipherTypeEncryptStandard,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
}, obrimCipherSuccessEncryptStandard
|
||||
}
|
||||
|
||||
// obrimCipherGenerateSalt generates a cryptographically secure salt.
|
||||
func obrimCipherGenerateSalt() ([]byte, string) {
|
||||
salt := make([]byte, obrimCipherSaltSize)
|
||||
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return nil, obrimCipherFailureSaltGeneration
|
||||
}
|
||||
|
||||
return salt, ""
|
||||
}
|
||||
|
||||
// obrimCipherBuildHeader builds the salted cipher signature, version, and salt header.
|
||||
func obrimCipherBuildHeader(salt []byte) ([]byte, string) {
|
||||
if len(salt) != obrimCipherSaltSize {
|
||||
return nil, obrimCipherFailureHeaderBuild
|
||||
}
|
||||
|
||||
signature := []byte(obrimCipherFormat)
|
||||
version := []byte(obrimCipherVersion)
|
||||
|
||||
header := make([]byte, 0, len(signature)+len(version)+len(salt))
|
||||
header = append(header, signature...)
|
||||
header = append(header, version...)
|
||||
header = append(header, salt...)
|
||||
|
||||
return header, ""
|
||||
}
|
||||
|
||||
// obrimCipherEncryptSalted encrypts file content with embedded salt metadata.
|
||||
func obrimCipherEncryptSalted(config map[string]any) (map[string]any, string) {
|
||||
key, ok := obrimCipherKey(config["key"])
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidKey
|
||||
}
|
||||
|
||||
inputPath, outputPath, ok := obrimCipherPaths(config)
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidConfig
|
||||
}
|
||||
|
||||
plaintext, err := os.ReadFile(inputPath)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureReadInput
|
||||
}
|
||||
|
||||
salt, code := obrimCipherGenerateSalt()
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
header, code := obrimCipherBuildHeader(salt)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
ciphertext, code := obrimCipherEncrypt(key, plaintext, salt)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
output := make([]byte, 0, len(header)+len(ciphertext))
|
||||
output = append(output, header...)
|
||||
output = append(output, ciphertext...)
|
||||
|
||||
if err := os.WriteFile(outputPath, output, 0600); err != nil {
|
||||
return nil, obrimCipherFailureWriteOutput
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"operation": obrimCipherTypeEncryptSalted,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"format": obrimCipherFormat,
|
||||
"version": obrimCipherVersion,
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
}, obrimCipherSuccessEncryptSalted
|
||||
}
|
||||
|
||||
// obrimCipherDecryptStandard decrypts file content without salt metadata.
|
||||
func obrimCipherDecryptStandard(config map[string]any) (map[string]any, string) {
|
||||
key, ok := obrimCipherKey(config["key"])
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidKey
|
||||
}
|
||||
|
||||
inputPath, outputPath, ok := obrimCipherPaths(config)
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidConfig
|
||||
}
|
||||
|
||||
ciphertext, err := os.ReadFile(inputPath)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureReadInput
|
||||
}
|
||||
|
||||
plaintext, code := obrimCipherDecrypt(key, ciphertext, nil)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outputPath, plaintext, 0600); err != nil {
|
||||
return nil, obrimCipherFailureWriteOutput
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"operation": obrimCipherTypeDecryptStandard,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
}, obrimCipherSuccessDecryptStandard
|
||||
}
|
||||
|
||||
// obrimCipherParseHeader parses the signature and version metadata.
|
||||
func obrimCipherParseHeader(data []byte) (obrimCipherHeader, string) {
|
||||
signatureLength := len(obrimCipherFormat)
|
||||
versionLength := len(obrimCipherVersion)
|
||||
|
||||
minimumLength := signatureLength + versionLength + obrimCipherSaltSize
|
||||
if len(data) < minimumLength {
|
||||
return obrimCipherHeader{}, obrimCipherFailureSignature
|
||||
}
|
||||
|
||||
signature := data[:signatureLength]
|
||||
if string(signature) != obrimCipherFormat {
|
||||
return obrimCipherHeader{}, obrimCipherFailureSignature
|
||||
}
|
||||
|
||||
versionStart := signatureLength
|
||||
versionEnd := versionStart + versionLength
|
||||
version := data[versionStart:versionEnd]
|
||||
|
||||
if string(version) != obrimCipherVersion {
|
||||
return obrimCipherHeader{}, obrimCipherFailureVersion
|
||||
}
|
||||
|
||||
saltStart := versionEnd
|
||||
saltEnd := saltStart + obrimCipherSaltSize
|
||||
|
||||
return obrimCipherHeader{
|
||||
Signature: append([]byte(nil), signature...),
|
||||
Version: append([]byte(nil), version...),
|
||||
Salt: append([]byte(nil), data[saltStart:saltEnd]...),
|
||||
}, ""
|
||||
}
|
||||
|
||||
// obrimCipherExtractSalt extracts the embedded salt from a parsed salted file.
|
||||
func obrimCipherExtractSalt(data []byte, header obrimCipherHeader) ([]byte, string) {
|
||||
if len(header.Salt) != obrimCipherSaltSize {
|
||||
return nil, obrimCipherFailureSaltExtraction
|
||||
}
|
||||
|
||||
signatureLength := len(header.Signature)
|
||||
versionLength := len(header.Version)
|
||||
saltStart := signatureLength + versionLength
|
||||
saltEnd := saltStart + obrimCipherSaltSize
|
||||
|
||||
if saltStart < 0 || saltEnd > len(data) {
|
||||
return nil, obrimCipherFailureSaltExtraction
|
||||
}
|
||||
|
||||
return append([]byte(nil), data[saltStart:saltEnd]...), ""
|
||||
}
|
||||
|
||||
// obrimCipherExtractPayload extracts the encrypted payload after the salted header.
|
||||
func obrimCipherExtractPayload(data []byte, header obrimCipherHeader) ([]byte, string) {
|
||||
payloadStart := len(header.Signature) + len(header.Version) + len(header.Salt)
|
||||
|
||||
if payloadStart >= len(data) {
|
||||
return nil, obrimCipherFailurePayloadExtraction
|
||||
}
|
||||
|
||||
payload := data[payloadStart:]
|
||||
if len(payload) <= 0 {
|
||||
return nil, obrimCipherFailurePayloadExtraction
|
||||
}
|
||||
|
||||
return append([]byte(nil), payload...), ""
|
||||
}
|
||||
|
||||
// obrimCipherDecryptSalted decrypts file content using its embedded salt metadata.
|
||||
func obrimCipherDecryptSalted(config map[string]any) (map[string]any, string) {
|
||||
key, ok := obrimCipherKey(config["key"])
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidKey
|
||||
}
|
||||
|
||||
inputPath, outputPath, ok := obrimCipherPaths(config)
|
||||
if !ok {
|
||||
return nil, obrimCipherFailureInvalidConfig
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(inputPath)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureReadInput
|
||||
}
|
||||
|
||||
header, code := obrimCipherParseHeader(data)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
salt, code := obrimCipherExtractSalt(data, header)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
payload, code := obrimCipherExtractPayload(data, header)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
plaintext, code := obrimCipherDecrypt(key, payload, salt)
|
||||
if code != "" {
|
||||
return nil, code
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outputPath, plaintext, 0600); err != nil {
|
||||
return nil, obrimCipherFailureWriteOutput
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"operation": obrimCipherTypeDecryptSalted,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"format": string(header.Signature),
|
||||
"version": string(header.Version),
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
}, obrimCipherSuccessDecryptSalted
|
||||
}
|
||||
|
||||
// obrimCipherEncrypt performs AES-256-GCM encryption and prefixes the nonce to the ciphertext.
|
||||
func obrimCipherEncrypt(key []byte, plaintext []byte, salt []byte) ([]byte, string) {
|
||||
derivedKey := obrimCipherDeriveKey(key, salt)
|
||||
|
||||
block, err := aes.NewCipher(derivedKey)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureCipherInitialization
|
||||
}
|
||||
|
||||
aead, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureCipherInitialization
|
||||
}
|
||||
|
||||
nonce := make([]byte, aead.NonceSize())
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, obrimCipherFailureNonceGeneration
|
||||
}
|
||||
|
||||
ciphertext := aead.Seal(nil, nonce, plaintext, nil)
|
||||
|
||||
output := make([]byte, 0, len(nonce)+len(ciphertext))
|
||||
output = append(output, nonce...)
|
||||
output = append(output, ciphertext...)
|
||||
|
||||
return output, ""
|
||||
}
|
||||
|
||||
// obrimCipherDecrypt performs AES-256-GCM decryption after extracting the nonce.
|
||||
func obrimCipherDecrypt(key []byte, ciphertext []byte, salt []byte) ([]byte, string) {
|
||||
derivedKey := obrimCipherDeriveKey(key, salt)
|
||||
|
||||
block, err := aes.NewCipher(derivedKey)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureCipherInitialization
|
||||
}
|
||||
|
||||
aead, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureCipherInitialization
|
||||
}
|
||||
|
||||
nonceSize := aead.NonceSize()
|
||||
if len(ciphertext) <= nonceSize {
|
||||
return nil, obrimCipherFailureDecryption
|
||||
}
|
||||
|
||||
nonce := ciphertext[:nonceSize]
|
||||
payload := ciphertext[nonceSize:]
|
||||
|
||||
plaintext, err := aead.Open(nil, nonce, payload, nil)
|
||||
if err != nil {
|
||||
return nil, obrimCipherFailureDecryption
|
||||
}
|
||||
|
||||
return plaintext, ""
|
||||
}
|
||||
|
||||
// obrimCipherDeriveKey derives the AES-256 key from the supplied key and optional salt.
|
||||
func obrimCipherDeriveKey(key []byte, salt []byte) []byte {
|
||||
if len(salt) == 0 {
|
||||
return append([]byte(nil), key...)
|
||||
}
|
||||
|
||||
digest := sha256.New()
|
||||
_, _ = digest.Write(key)
|
||||
_, _ = digest.Write(salt)
|
||||
|
||||
return digest.Sum(nil)
|
||||
}
|
||||
|
||||
// obrimCipherKey normalizes a supported AES-256 key value.
|
||||
func obrimCipherKey(value any) ([]byte, bool) {
|
||||
switch key := value.(type) {
|
||||
case string:
|
||||
if len([]byte(key)) != 32 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return []byte(key), true
|
||||
case []byte:
|
||||
if len(key) != 32 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return append([]byte(nil), key...), true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCipherValidKey validates that a supplied key is AES-256 compatible.
|
||||
func obrimCipherValidKey(value any) bool {
|
||||
_, ok := obrimCipherKey(value)
|
||||
return ok
|
||||
}
|
||||
|
||||
// obrimCipherPaths resolves and validates the configured input and output paths.
|
||||
func obrimCipherPaths(config map[string]any) (string, string, bool) {
|
||||
input, inputOK := config["input"].(string)
|
||||
output, outputOK := config["output"].(string)
|
||||
|
||||
if !inputOK || !outputOK || input == "" || output == "" {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
inputPath, err := filepath.Abs(input)
|
||||
if err != nil {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
outputPath, err := filepath.Abs(output)
|
||||
if err != nil {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
if inputPath == outputPath {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
if _, err := os.Stat(inputPath); err != nil {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
return inputPath, outputPath, true
|
||||
}
|
||||
|
||||
// obrimCipherError provides a deterministic error representation for internal callers.
|
||||
func obrimCipherError(code string) error {
|
||||
return errors.New(fmt.Sprintf("cipher: %s", code))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user