mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 23:50:48 +02:00
- Added new routes and handlers for managing storage providers, including creation, editing, and deletion. - Introduced a new StorageProvider form component for user input. - Enhanced the database schema to support storage provider references in transfer configurations. - Implemented encryption for sensitive fields in storage provider data. - Added tests for storage provider API endpoints and integration with the database. - Updated frontend components to support storage provider selection and testing.
195 lines
5.5 KiB
Go
195 lines
5.5 KiB
Go
package encryption
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// Standard errors for encryption operations
|
|
var (
|
|
ErrEncryptionFailed = errors.New("encryption failed")
|
|
ErrDecryptionFailed = errors.New("decryption failed")
|
|
ErrInvalidBlockSize = errors.New("invalid block size")
|
|
ErrInvalidCiphertext = errors.New("invalid ciphertext")
|
|
ErrInvalidKeySize = errors.New("invalid key size")
|
|
ErrEmptyPlaintext = errors.New("plaintext is empty")
|
|
ErrEmptyCiphertext = errors.New("ciphertext is empty")
|
|
ErrMissingIV = errors.New("initialization vector missing")
|
|
)
|
|
|
|
// EncryptionService provides methods to encrypt and decrypt data
|
|
type EncryptionService struct {
|
|
keyManager KeyManager
|
|
}
|
|
|
|
// NewEncryptionService creates a new encryption service using the provided key manager
|
|
func NewEncryptionService(km KeyManager) (*EncryptionService, error) {
|
|
if km == nil {
|
|
return nil, errors.New("key manager is required")
|
|
}
|
|
return &EncryptionService{keyManager: km}, nil
|
|
}
|
|
|
|
// Encrypt encrypts the plaintext using AES-256-CBC with PKCS7 padding
|
|
// It returns a base64-encoded string of the IV + ciphertext
|
|
func (s *EncryptionService) Encrypt(plaintext []byte) (string, error) {
|
|
if len(plaintext) == 0 {
|
|
return "", ErrEmptyPlaintext
|
|
}
|
|
|
|
// Get the encryption key
|
|
key, err := s.keyManager.GetPrimaryKey()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get encryption key: %w", err)
|
|
}
|
|
|
|
// Create a new AES cipher block
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %v", ErrEncryptionFailed, err)
|
|
}
|
|
|
|
// Pad the plaintext to be a multiple of the block size
|
|
paddedPlaintext := pkcs7Pad(plaintext, block.BlockSize())
|
|
|
|
// Generate a random IV
|
|
iv := make([]byte, block.BlockSize())
|
|
if _, err := io.ReadFull(SecureRandomReader, iv); err != nil {
|
|
return "", fmt.Errorf("%w: failed to generate IV: %v", ErrEncryptionFailed, err)
|
|
}
|
|
|
|
// Create CBC encrypter
|
|
mode := cipher.NewCBCEncrypter(block, iv)
|
|
|
|
// Encrypt the data
|
|
ciphertext := make([]byte, len(paddedPlaintext))
|
|
mode.CryptBlocks(ciphertext, paddedPlaintext)
|
|
|
|
// Prepend IV to ciphertext
|
|
combined := append(iv, ciphertext...)
|
|
|
|
// Encode with base64
|
|
encoded := base64.StdEncoding.EncodeToString(combined)
|
|
|
|
return encoded, nil
|
|
}
|
|
|
|
// Decrypt decrypts the base64-encoded ciphertext using AES-256-CBC with PKCS7 padding
|
|
// It expects the ciphertext to be a base64-encoded string of the IV + actual ciphertext
|
|
func (s *EncryptionService) Decrypt(encodedCiphertext string) ([]byte, error) {
|
|
if encodedCiphertext == "" {
|
|
return nil, ErrEmptyCiphertext
|
|
}
|
|
|
|
// Decode the base64 encoded data
|
|
combined, err := base64.StdEncoding.DecodeString(encodedCiphertext)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid base64 encoding: %v", ErrDecryptionFailed, err)
|
|
}
|
|
|
|
// Get the encryption key
|
|
key, err := s.keyManager.GetPrimaryKey()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get encryption key: %w", err)
|
|
}
|
|
|
|
// Create a new AES cipher block
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrDecryptionFailed, err)
|
|
}
|
|
|
|
// Extract IV and ciphertext
|
|
blockSize := block.BlockSize()
|
|
if len(combined) < blockSize {
|
|
return nil, ErrMissingIV
|
|
}
|
|
iv := combined[:blockSize]
|
|
ciphertext := combined[blockSize:]
|
|
|
|
// Verify ciphertext length
|
|
if len(ciphertext) == 0 {
|
|
return nil, ErrEmptyCiphertext
|
|
}
|
|
if len(ciphertext)%blockSize != 0 {
|
|
return nil, ErrInvalidBlockSize
|
|
}
|
|
|
|
// Create CBC decrypter
|
|
mode := cipher.NewCBCDecrypter(block, iv)
|
|
|
|
// Decrypt the data
|
|
decrypted := make([]byte, len(ciphertext))
|
|
mode.CryptBlocks(decrypted, ciphertext)
|
|
|
|
// Remove padding
|
|
unpadded, err := pkcs7Unpad(decrypted, blockSize)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrDecryptionFailed, err)
|
|
}
|
|
|
|
return unpadded, nil
|
|
}
|
|
|
|
// EncryptString encrypts a string and returns a base64-encoded result
|
|
func (s *EncryptionService) EncryptString(plaintext string) (string, error) {
|
|
return s.Encrypt([]byte(plaintext))
|
|
}
|
|
|
|
// DecryptString decrypts a base64-encoded ciphertext and returns the plaintext string
|
|
func (s *EncryptionService) DecryptString(encodedCiphertext string) (string, error) {
|
|
plaintext, err := s.Decrypt(encodedCiphertext)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(plaintext), nil
|
|
}
|
|
|
|
// pkcs7Pad adds PKCS#7 padding to the data to make it a multiple of the block size
|
|
func pkcs7Pad(data []byte, blockSize int) []byte {
|
|
padding := blockSize - (len(data) % blockSize)
|
|
padText := make([]byte, padding)
|
|
for i := range padText {
|
|
padText[i] = byte(padding)
|
|
}
|
|
return append(data, padText...)
|
|
}
|
|
|
|
// pkcs7Unpad removes PKCS#7 padding from the data
|
|
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
|
|
if len(data) == 0 || len(data)%blockSize != 0 {
|
|
return nil, ErrInvalidBlockSize
|
|
}
|
|
|
|
padding := int(data[len(data)-1])
|
|
if padding <= 0 || padding > blockSize {
|
|
return nil, errors.New("invalid padding value")
|
|
}
|
|
|
|
// Validate that all padding bytes have the correct value
|
|
for i := len(data) - padding; i < len(data); i++ {
|
|
if data[i] != byte(padding) {
|
|
return nil, errors.New("invalid padding")
|
|
}
|
|
}
|
|
|
|
return data[:len(data)-padding], nil
|
|
}
|
|
|
|
// GetGlobalEncryptionService creates an EncryptionService using the global key manager
|
|
// It initializes the key manager if it hasn't been initialized yet
|
|
func GetGlobalEncryptionService() (*EncryptionService, error) {
|
|
// Make sure key manager is initialized
|
|
if GetKeyManager() == nil {
|
|
if err := InitializeKeyManager(""); err != nil {
|
|
return nil, fmt.Errorf("failed to initialize key manager: %w", err)
|
|
}
|
|
}
|
|
|
|
return NewEncryptionService(GetKeyManager())
|
|
}
|