attachments.go raw
1 package bridge
2
3 import (
4 "crypto/rand"
5 "encoding/hex"
6 "fmt"
7
8 "golang.org/x/crypto/chacha20poly1305"
9 )
10
11 // EncryptAttachment encrypts data with a random ChaCha20-Poly1305 key.
12 // Returns (ciphertext, keyHex). The key is hex-encoded for use in fragment URLs.
13 func EncryptAttachment(plaintext []byte) (ciphertext []byte, keyHex string, err error) {
14 // Generate random 256-bit key
15 key := make([]byte, chacha20poly1305.KeySize)
16 if _, err := rand.Read(key); err != nil {
17 return nil, "", fmt.Errorf("generate key: %w", err)
18 }
19
20 aead, err := chacha20poly1305.New(key)
21 if err != nil {
22 return nil, "", fmt.Errorf("create AEAD: %w", err)
23 }
24
25 // Generate random nonce
26 nonce := make([]byte, aead.NonceSize())
27 if _, err := rand.Read(nonce); err != nil {
28 return nil, "", fmt.Errorf("generate nonce: %w", err)
29 }
30
31 // Encrypt: nonce || ciphertext (nonce prepended for decryption)
32 encrypted := aead.Seal(nonce, nonce, plaintext, nil)
33
34 return encrypted, hex.EncodeToString(key), nil
35 }
36
37 // DecryptAttachment decrypts data that was encrypted by EncryptAttachment.
38 // keyHex is the hex-encoded key from the fragment URL.
39 func DecryptAttachment(ciphertext []byte, keyHex string) ([]byte, error) {
40 key, err := hex.DecodeString(keyHex)
41 if err != nil {
42 return nil, fmt.Errorf("decode key: %w", err)
43 }
44
45 if len(key) != chacha20poly1305.KeySize {
46 return nil, fmt.Errorf("invalid key length: %d (expected %d)", len(key), chacha20poly1305.KeySize)
47 }
48
49 aead, err := chacha20poly1305.New(key)
50 if err != nil {
51 return nil, fmt.Errorf("create AEAD: %w", err)
52 }
53
54 if len(ciphertext) < aead.NonceSize() {
55 return nil, fmt.Errorf("ciphertext too short")
56 }
57
58 // Split nonce and ciphertext
59 nonce := ciphertext[:aead.NonceSize()]
60 ct := ciphertext[aead.NonceSize():]
61
62 plaintext, err := aead.Open(nil, nonce, ct, nil)
63 if err != nil {
64 return nil, fmt.Errorf("decrypt: %w", err)
65 }
66
67 return plaintext, nil
68 }
69