qcc/internal/crypto/e2ee.go
2026-06-30 12:52:09 +03:00

74 lines
1.7 KiB
Go

package crypto
import (
"crypto/rand"
"crypto/sha256"
"io"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/hkdf"
)
type E2EEKeypair struct {
PrivateKey [32]byte
PublicKey [32]byte
}
func GenerateE2EEKeypair() (*E2EEKeypair, error) {
priv := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, priv); err != nil {
return nil, err
}
// Clamp for X25519
priv[0] &= 248
priv[31] &= 127
priv[31] |= 64
pub, err := curve25519.X25519(priv, curve25519.Basepoint)
if err != nil {
return nil, err
}
var privArr [32]byte
var pubArr [32]byte
copy(privArr[:], priv)
copy(pubArr[:], pub)
return &E2EEKeypair{PrivateKey: privArr, PublicKey: pubArr}, nil
}
func DeriveE2EEKey(privateKey, publicKey [32]byte) []byte {
shared, _ := curve25519.X25519(privateKey[:], publicKey[:])
salt := make([]byte, 32)
info := []byte("qcc-e2ee-media-key")
hkdf := hkdf.New(sha256.New, shared, salt, info)
key := make([]byte, 32)
if _, err := io.ReadFull(hkdf, key); err != nil {
return nil
}
return key
}
func EncryptE2EE(key []byte, plaintext []byte) ([]byte, error) {
aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, err
}
nonce := make([]byte, chacha20poly1305.NonceSizeX)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return aead.Seal(nonce, nonce, plaintext, nil), nil
}
func DecryptE2EE(key []byte, ciphertext []byte) ([]byte, error) {
aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, err
}
if len(ciphertext) < chacha20poly1305.NonceSizeX {
return nil, io.ErrUnexpectedEOF
}
nonce := ciphertext[:chacha20poly1305.NonceSizeX]
return aead.Open(nil, nonce, ciphertext[chacha20poly1305.NonceSizeX:], nil)
}