|
@@ -0,0 +1,59 @@
|
|
|
|
|
+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
|
|
|
|
|
+}
|