aesgcm.go raw

   1  package config
   2  
   3  import (
   4  	"crypto/aes"
   5  	"crypto/cipher"
   6  	"crypto/rand"
   7  	"encoding/hex"
   8  	"fmt"
   9  	"strings"
  10  
  11  	"golang.org/x/crypto/argon2"
  12  )
  13  
  14  func DeriveKey(password string, salt []byte) ([]byte, []byte, error) {
  15  	if salt == nil {
  16  		salt = make([]byte, 32)
  17  		if _, err := rand.Read(salt); err != nil {
  18  			return nil, nil, err
  19  		}
  20  	}
  21  
  22  	key := argon2.Key([]byte(password), salt, 3, 32*1024, 1, 32)
  23  
  24  	return key, salt, nil
  25  }
  26  
  27  func AesGcmEncryptWithPassword(plaintext string, password string) (string, error) {
  28  	secretKey, salt, err := DeriveKey(password, nil)
  29  	if err != nil {
  30  		return "", err
  31  	}
  32  
  33  	ciphertext, err := AesGcmEncryptWithKey(plaintext, secretKey)
  34  	if err != nil {
  35  		return "", err
  36  	}
  37  
  38  	return hex.EncodeToString(salt) + "-" + ciphertext, nil
  39  }
  40  
  41  func AesGcmDecryptWithPassword(ciphertext string, password string) (string, error) {
  42  	arr := strings.Split(ciphertext, "-")
  43  	salt, _ := hex.DecodeString(arr[0])
  44  	secretKey, _, err := DeriveKey(password, salt)
  45  	if err != nil {
  46  		return "", err
  47  	}
  48  
  49  	return AesGcmDecryptWithKey(arr[1]+"-"+arr[2], secretKey)
  50  }
  51  
  52  func AesGcmEncryptWithKey(plaintext string, key []byte) (string, error) {
  53  	// require a 32 bytes key (256 bits)
  54  	if len(key) != 32 {
  55  		return "", fmt.Errorf("key must be at least 32 bytes, got %d", len(key))
  56  	}
  57  
  58  	plaintextBytes := []byte(plaintext)
  59  
  60  	aes, err := aes.NewCipher(key)
  61  	if err != nil {
  62  		return "", err
  63  	}
  64  
  65  	aesgcm, err := cipher.NewGCM(aes)
  66  	if err != nil {
  67  		return "", err
  68  	}
  69  
  70  	nonce := make([]byte, aesgcm.NonceSize())
  71  	_, err = rand.Read(nonce)
  72  	if err != nil {
  73  		return "", err
  74  	}
  75  
  76  	ciphertext := aesgcm.Seal(nil, nonce, plaintextBytes, nil)
  77  
  78  	return hex.EncodeToString(nonce) + "-" + hex.EncodeToString(ciphertext), nil
  79  }
  80  
  81  func AesGcmDecryptWithKey(ciphertext string, key []byte) (string, error) {
  82  	// require a 32 bytes key (256 bits)
  83  	if len(key) != 32 {
  84  		return "", fmt.Errorf("key must be at least 32 bytes, got %d", len(key))
  85  	}
  86  
  87  	arr := strings.Split(ciphertext, "-")
  88  	nonce, _ := hex.DecodeString(arr[0])
  89  	data, _ := hex.DecodeString(arr[1])
  90  
  91  	aes, err := aes.NewCipher([]byte(key))
  92  	if err != nil {
  93  		return "", err
  94  	}
  95  
  96  	aesgcm, err := cipher.NewGCM(aes)
  97  	if err != nil {
  98  		return "", err
  99  	}
 100  
 101  	plaintext, err := aesgcm.Open(nil, nonce, data, nil)
 102  	if err != nil {
 103  		return "", err
 104  	}
 105  
 106  	return string(plaintext), nil
 107  }
 108