1package crypt 2 3import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/rand" 7 "crypto/sha256" 8 "fmt" 9 "log" 10 11 "golang.org/x/crypto/argon2" 12 "golang.org/x/crypto/chacha20poly1305" 13 "golang.org/x/crypto/pbkdf2" 14) 15 16// New generates a new key based on a passphrase and salt 17func New(passphrase []byte, usersalt []byte) (key []byte, salt []byte, err error) { 18 if len(passphrase) < 1 { 19 err = fmt.Errorf("need more than that for passphrase") 20 return 21 } 22 if usersalt == nil { 23 salt = make([]byte, 8) 24 // http://www.ietf.org/rfc/rfc2898.txt 25 // Salt. 26 if _, err := rand.Read(salt); err != nil { 27 log.Fatalf("can't get random salt: %v", err) 28 } 29 } else { 30 salt = usersalt 31 } 32 key = pbkdf2.Key(passphrase, salt, 100, 32, sha256.New) 33 return 34} 35 36// Encrypt will encrypt using the pre-generated key 37func Encrypt(plaintext []byte, key []byte) (encrypted []byte, err error) { 38 // generate a random iv each time 39 // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf 40 // Section 8.2 41 ivBytes := make([]byte, 12) 42 if _, err := rand.Read(ivBytes); err != nil { 43 log.Fatalf("can't initialize crypto: %v", err) 44 } 45 b, err := aes.NewCipher(key) 46 if err != nil { 47 return 48 } 49 aesgcm, err := cipher.NewGCM(b) 50 if err != nil { 51 return 52 } 53 encrypted = aesgcm.Seal(nil, ivBytes, plaintext, nil) 54 encrypted = append(ivBytes, encrypted...) 55 return 56} 57 58// Decrypt using the pre-generated key 59func Decrypt(encrypted []byte, key []byte) (plaintext []byte, err error) { 60 if len(encrypted) < 13 { 61 err = fmt.Errorf("incorrect passphrase") 62 return 63 } 64 b, err := aes.NewCipher(key) 65 if err != nil { 66 return 67 } 68 aesgcm, err := cipher.NewGCM(b) 69 if err != nil { 70 return 71 } 72 plaintext, err = aesgcm.Open(nil, encrypted[:12], encrypted[12:], nil) 73 return 74} 75 76// NewArgon2 generates a new key based on a passphrase and salt 77// using argon2 78// https://pkg.go.dev/golang.org/x/crypto/argon2 79func NewArgon2(passphrase []byte, usersalt []byte) (aead cipher.AEAD, salt []byte, err error) { 80 if len(passphrase) < 1 { 81 err = fmt.Errorf("need more than that for passphrase") 82 return 83 } 84 if usersalt == nil { 85 salt = make([]byte, 8) 86 // http://www.ietf.org/rfc/rfc2898.txt 87 // Salt. 88 if _, err := rand.Read(salt); err != nil { 89 log.Fatalf("can't get random salt: %v", err) 90 } 91 } else { 92 salt = usersalt 93 } 94 aead, err = chacha20poly1305.NewX(argon2.IDKey(passphrase, salt, 1, 64*1024, 4, 32)) 95 return 96} 97 98// EncryptChaCha will encrypt ChaCha20-Poly1305 using the pre-generated key 99// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305 100func EncryptChaCha(plaintext []byte, aead cipher.AEAD) (encrypted []byte, err error) { 101 nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(plaintext)+aead.Overhead()) 102 if _, err := rand.Read(nonce); err != nil { 103 panic(err) 104 } 105 106 // Encrypt the message and append the ciphertext to the nonce. 107 encrypted = aead.Seal(nonce, nonce, plaintext, nil) 108 return 109} 110 111// DecryptChaCha will encrypt ChaCha20-Poly1305 using the pre-generated key 112// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305 113func DecryptChaCha(encryptedMsg []byte, aead cipher.AEAD) (encrypted []byte, err error) { 114 if len(encryptedMsg) < aead.NonceSize() { 115 err = fmt.Errorf("ciphertext too short") 116 return 117 } 118 119 // Split nonce and ciphertext. 120 nonce, ciphertext := encryptedMsg[:aead.NonceSize()], encryptedMsg[aead.NonceSize():] 121 122 // Decrypt the message and check it wasn't tampered with. 123 encrypted, err = aead.Open(nil, nonce, ciphertext, nil) 124 return 125} 126