Files
quarkslab-proxyblob/pkg/protocol/crypto.go
T
2025-04-29 14:05:18 +02:00

99 lines
2.8 KiB
Go

package protocol
import (
"crypto/rand"
"io"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
)
// GenerateKeyPair creates a new X25519 key pair for key exchange.
// Returns a properly clamped private key and its corresponding public key.
func GenerateKeyPair() (privateKey, publicKey []byte) {
privateKey = make([]byte, curve25519.ScalarSize)
io.ReadFull(rand.Reader, privateKey)
// Clamp private key according to X25519 spec
privateKey[0] &= 248
privateKey[31] &= 127
privateKey[31] |= 64
publicKey, _ = curve25519.X25519(privateKey, curve25519.Basepoint)
return privateKey, publicKey
}
// GenerateNonce creates a random nonce for ChaCha20-Poly1305.
// Returns a cryptographically secure random nonce.
func GenerateNonce() []byte {
nonce := make([]byte, chacha20poly1305.NonceSizeX)
io.ReadFull(rand.Reader, nonce)
return nonce
}
// DeriveKey performs X25519 key exchange and HKDF key derivation.
// Returns a symmetric key and status code. Key is nil on error.
func DeriveKey(privateKey, peerPublicKey, nonce []byte) ([]byte, byte) {
// Derive shared secret using X25519
sharedSecret, err := curve25519.X25519(privateKey, peerPublicKey)
if err != nil {
return nil, ErrInvalidCrypto
}
// Derive symmetric key using HKDF-SHA3
kdf := hkdf.New(sha3.New256, sharedSecret, nonce, nil)
symmetricKey := make([]byte, chacha20poly1305.KeySize)
if _, err := io.ReadFull(kdf, symmetricKey); err != nil {
return nil, ErrInvalidCrypto
}
return symmetricKey, ErrNone
}
// Encrypt performs authenticated encryption using ChaCha20-Poly1305.
// Returns (nonce || ciphertext || tag) or nil on error.
func Encrypt(key, plaintext []byte) ([]byte, byte) {
aead, err := chacha20poly1305.NewX(key)
if err != nil {
return nil, ErrInvalidCrypto
}
nonce := GenerateNonce()
ciphertext := aead.Seal(nonce, nonce, plaintext, nil)
return ciphertext, ErrNone
}
// Decrypt performs authenticated decryption using ChaCha20-Poly1305.
// Returns decrypted plaintext or nil if authentication fails.
func Decrypt(key, ciphertext []byte) ([]byte, byte) {
aead, err := chacha20poly1305.NewX(key)
if err != nil {
return nil, ErrInvalidCrypto
}
if len(ciphertext) < chacha20poly1305.NonceSizeX {
return nil, ErrInvalidCrypto
}
nonce := ciphertext[:chacha20poly1305.NonceSizeX]
ciphertextBody := ciphertext[chacha20poly1305.NonceSizeX:]
plaintext, err := aead.Open(nil, nonce, ciphertextBody, nil)
if err != nil {
return nil, ErrInvalidCrypto
}
return plaintext, ErrNone
}
// Xor performs byte-wise XOR of data with a repeating key.
// Warning: This is NOT cryptographically secure, use only for basic obfuscation.
func Xor(data []byte, key []byte) []byte {
for i := range data {
data[i] ^= key[i%len(key)]
}
return data
}