| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
- package telegram
- import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "encoding/base64"
- "errors"
- "fmt"
- )
- // Crypto wraps AES-GCM with a fixed 32-byte key and random 12-byte nonces.
- // Used for encrypting 2FA passwords at rest.
- type Crypto struct {
- gcm cipher.AEAD
- }
- // NewCrypto builds a Crypto from a base64-encoded 32-byte key.
- func NewCrypto(b64Key string) (*Crypto, error) {
- key, err := base64.StdEncoding.DecodeString(b64Key)
- if err != nil {
- return nil, fmt.Errorf("decode secret key: %w", err)
- }
- if len(key) != 32 {
- return nil, fmt.Errorf("secret key must be 32 bytes, got %d", len(key))
- }
- block, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- gcm, err := cipher.NewGCM(block)
- if err != nil {
- return nil, err
- }
- return &Crypto{gcm: gcm}, nil
- }
- // Encrypt returns [nonce || ciphertext || tag].
- func (c *Crypto) Encrypt(plain string) ([]byte, error) {
- nonce := make([]byte, c.gcm.NonceSize())
- if _, err := rand.Read(nonce); err != nil {
- return nil, err
- }
- return c.gcm.Seal(nonce, nonce, []byte(plain), nil), nil
- }
- // Decrypt expects the [nonce || ciphertext || tag] layout produced by Encrypt.
- func (c *Crypto) Decrypt(blob []byte) (string, error) {
- ns := c.gcm.NonceSize()
- if len(blob) < ns {
- return "", errors.New("ciphertext too short")
- }
- nonce, ct := blob[:ns], blob[ns:]
- pt, err := c.gcm.Open(nil, nonce, ct, nil)
- if err != nil {
- return "", err
- }
- return string(pt), nil
- }
|