package crypto import ( "crypto/aes" "crypto/cipher" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "encoding/hex" "errors" "io" "math/big" "golang.org/x/crypto/hkdf" ) var ( ErrInvalidKeySize = errors.New("invalid key size") ErrInvalidCipherText = errors.New("invalid ciphertext") ErrEncryptionFailed = errors.New("encryption failed") ErrDecryptionFailed = errors.New("decryption failed") ErrInvalidPublicKey = errors.New("invalid public key") ErrInvalidSignature = errors.New("invalid signature") ) // CryptoService provides cryptographic operations type CryptoService struct { masterKey []byte } // NewCryptoService creates a new crypto service func NewCryptoService(masterKey []byte) (*CryptoService, error) { if len(masterKey) != 32 { return nil, ErrInvalidKeySize } return &CryptoService{masterKey: masterKey}, nil } // GenerateRandomBytes generates random bytes func GenerateRandomBytes(n int) ([]byte, error) { b := make([]byte, n) _, err := rand.Read(b) if err != nil { return nil, err } return b, nil } // GenerateRandomHex generates a random hex string func GenerateRandomHex(n int) (string, error) { bytes, err := GenerateRandomBytes(n) if err != nil { return "", err } return hex.EncodeToString(bytes), nil } // DeriveKey derives a key from the master key using HKDF func (c *CryptoService) DeriveKey(context string, length int) ([]byte, error) { hkdfReader := hkdf.New(sha256.New, c.masterKey, nil, []byte(context)) key := make([]byte, length) if _, err := io.ReadFull(hkdfReader, key); err != nil { return nil, err } return key, nil } // EncryptShare encrypts a key share using AES-256-GCM func (c *CryptoService) EncryptShare(shareData []byte, partyID string) ([]byte, error) { // Derive a unique key for this party key, err := c.DeriveKey("share_encryption:"+partyID, 32) if err != nil { return nil, err } block, err := aes.NewCipher(key) if err != nil { return nil, err } aesGCM, err := cipher.NewGCM(block) if err != nil { return nil, err } nonce := make([]byte, aesGCM.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, err } // Encrypt and prepend nonce ciphertext := aesGCM.Seal(nonce, nonce, shareData, []byte(partyID)) return ciphertext, nil } // DecryptShare decrypts a key share func (c *CryptoService) DecryptShare(encryptedData []byte, partyID string) ([]byte, error) { // Derive the same key used for encryption key, err := c.DeriveKey("share_encryption:"+partyID, 32) if err != nil { return nil, err } block, err := aes.NewCipher(key) if err != nil { return nil, err } aesGCM, err := cipher.NewGCM(block) if err != nil { return nil, err } nonceSize := aesGCM.NonceSize() if len(encryptedData) < nonceSize { return nil, ErrInvalidCipherText } nonce, ciphertext := encryptedData[:nonceSize], encryptedData[nonceSize:] plaintext, err := aesGCM.Open(nil, nonce, ciphertext, []byte(partyID)) if err != nil { return nil, ErrDecryptionFailed } return plaintext, nil } // EncryptMessage encrypts a message using AES-256-GCM func (c *CryptoService) EncryptMessage(plaintext []byte) ([]byte, error) { block, err := aes.NewCipher(c.masterKey) if err != nil { return nil, err } aesGCM, err := cipher.NewGCM(block) if err != nil { return nil, err } nonce := make([]byte, aesGCM.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, err } ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil) return ciphertext, nil } // DecryptMessage decrypts a message func (c *CryptoService) DecryptMessage(ciphertext []byte) ([]byte, error) { block, err := aes.NewCipher(c.masterKey) if err != nil { return nil, err } aesGCM, err := cipher.NewGCM(block) if err != nil { return nil, err } nonceSize := aesGCM.NonceSize() if len(ciphertext) < nonceSize { return nil, ErrInvalidCipherText } nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, ErrDecryptionFailed } return plaintext, nil } // Hash256 computes SHA-256 hash func Hash256(data []byte) []byte { hash := sha256.Sum256(data) return hash[:] } // VerifyECDSASignature verifies an ECDSA signature func VerifyECDSASignature(messageHash, signature, publicKey []byte) (bool, error) { // Parse public key (assuming secp256k1/P256 uncompressed format) curve := elliptic.P256() x, y := elliptic.Unmarshal(curve, publicKey) if x == nil { return false, ErrInvalidPublicKey } pubKey := &ecdsa.PublicKey{ Curve: curve, X: x, Y: y, } // Parse signature (R || S, each 32 bytes) if len(signature) != 64 { return false, ErrInvalidSignature } r := new(big.Int).SetBytes(signature[:32]) s := new(big.Int).SetBytes(signature[32:]) // Verify signature valid := ecdsa.Verify(pubKey, messageHash, r, s) return valid, nil } // GenerateNonce generates a cryptographic nonce func GenerateNonce() ([]byte, error) { return GenerateRandomBytes(32) } // SecureCompare performs constant-time comparison func SecureCompare(a, b []byte) bool { if len(a) != len(b) { return false } var result byte for i := 0; i < len(a); i++ { result |= a[i] ^ b[i] } return result == 0 } // ParsePublicKey parses a public key from bytes (P256 uncompressed format) func ParsePublicKey(publicKeyBytes []byte) (*ecdsa.PublicKey, error) { curve := elliptic.P256() x, y := elliptic.Unmarshal(curve, publicKeyBytes) if x == nil { return nil, ErrInvalidPublicKey } return &ecdsa.PublicKey{ Curve: curve, X: x, Y: y, }, nil } // VerifySignature verifies an ECDSA signature using a public key func VerifySignature(pubKey *ecdsa.PublicKey, messageHash, signature []byte) bool { // Parse signature (R || S, each 32 bytes) if len(signature) != 64 { return false } r := new(big.Int).SetBytes(signature[:32]) s := new(big.Int).SetBytes(signature[32:]) return ecdsa.Verify(pubKey, messageHash, r, s) } // HashMessage computes SHA-256 hash of a message (alias for Hash256) func HashMessage(message []byte) []byte { return Hash256(message) } // Encrypt encrypts data using AES-256-GCM with the provided key func Encrypt(key, plaintext []byte) ([]byte, error) { if len(key) != 32 { return nil, ErrInvalidKeySize } block, err := aes.NewCipher(key) if err != nil { return nil, err } aesGCM, err := cipher.NewGCM(block) if err != nil { return nil, err } nonce := make([]byte, aesGCM.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, err } ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil) return ciphertext, nil } // Decrypt decrypts data using AES-256-GCM with the provided key func Decrypt(key, ciphertext []byte) ([]byte, error) { if len(key) != 32 { return nil, ErrInvalidKeySize } block, err := aes.NewCipher(key) if err != nil { return nil, err } aesGCM, err := cipher.NewGCM(block) if err != nil { return nil, err } nonceSize := aesGCM.NonceSize() if len(ciphertext) < nonceSize { return nil, ErrInvalidCipherText } nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, ErrDecryptionFailed } return plaintext, nil } // DeriveKey derives a key from secret and salt using HKDF (standalone function) func DeriveKey(secret, salt []byte, length int) ([]byte, error) { hkdfReader := hkdf.New(sha256.New, secret, salt, nil) key := make([]byte, length) if _, err := io.ReadFull(hkdfReader, key); err != nil { return nil, err } return key, nil } // SignMessage signs a message using ECDSA private key func SignMessage(privateKey *ecdsa.PrivateKey, message []byte) ([]byte, error) { hash := Hash256(message) r, s, err := ecdsa.Sign(rand.Reader, privateKey, hash) if err != nil { return nil, err } // Encode R and S as 32 bytes each (total 64 bytes) signature := make([]byte, 64) rBytes := r.Bytes() sBytes := s.Bytes() // Pad with zeros if necessary copy(signature[32-len(rBytes):32], rBytes) copy(signature[64-len(sBytes):64], sBytes) return signature, nil } // EncodeToHex encodes bytes to hex string func EncodeToHex(data []byte) string { return hex.EncodeToString(data) } // DecodeFromHex decodes hex string to bytes func DecodeFromHex(s string) ([]byte, error) { return hex.DecodeString(s) } // EncodeToBase64 encodes bytes to base64 string func EncodeToBase64(data []byte) string { return hex.EncodeToString(data) // Using hex for simplicity, could use base64 } // DecodeFromBase64 decodes base64 string to bytes func DecodeFromBase64(s string) ([]byte, error) { return hex.DecodeString(s) } // MarshalPublicKey marshals an ECDSA public key to bytes func MarshalPublicKey(pubKey *ecdsa.PublicKey) []byte { return elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y) } // CompareBytes performs constant-time comparison of two byte slices func CompareBytes(a, b []byte) bool { return SecureCompare(a, b) }