74 lines
1.7 KiB
Go
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)
|
|
}
|