diff options
| author | Jacky Zhao <[email protected]> | 2020-05-15 18:53:37 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2020-05-15 18:53:37 -0700 |
| commit | 2e4a87393d6fdf0320696faedecdc7699289fffb (patch) | |
| tree | 7afe72a155fd9f6afd1bdded4a214b6fbba77fa0 /backend/security/encrypt.go | |
| parent | Merge pull request #24 from jackyzha0/update-readme (diff) | |
| parent | Add comments and clean up encryption (diff) | |
| download | ctrl-v-2e4a87393d6fdf0320696faedecdc7699289fffb.tar.xz ctrl-v-2e4a87393d6fdf0320696faedecdc7699289fffb.zip | |
Merge pull request #25 from jackyzha0/security
Add encryption to content when password is specified
Diffstat (limited to 'backend/security/encrypt.go')
| -rw-r--r-- | backend/security/encrypt.go | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/backend/security/encrypt.go b/backend/security/encrypt.go new file mode 100644 index 0000000..4af5e3c --- /dev/null +++ b/backend/security/encrypt.go @@ -0,0 +1,81 @@ +package security + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "errors" + "golang.org/x/crypto/scrypt" +) + +var EncryptionError = errors.New("could not encrypt the given content") + +func Encrypt(key, data string) (string, error) { + // initialize aes block cipher with given key + blockCipher, err := aes.NewCipher([]byte(key)) + if err != nil { + return "", err + } + + // wrap block cipher with Galois Counter Mode and standard nonce length + gcm, err := cipher.NewGCM(blockCipher) + if err != nil { + return "", err + } + + // generate nonce (number once used) unique to the given key + nonce := make([]byte, gcm.NonceSize()) + if _, err = rand.Read(nonce); err != nil { + return "", err + } + + // seal nonce with data to use during decryption + cipherText := gcm.Seal(nonce, nonce, []byte(data), nil) + + return string(cipherText), nil +} + +func Decrypt(key, data string) (string, error) { + // similar to encrypt, create cipher and wrap with GCM + blockCipher, err := aes.NewCipher([]byte(key)) + if err != nil { + return "", err + } + + gcm, err := cipher.NewGCM(blockCipher) + if err != nil { + return "", err + } + + // extract the nonce from the data + nonce, cipherText := data[:gcm.NonceSize()], data[gcm.NonceSize():] + + // use nonce to decrypt the data + plaintext, err := gcm.Open(nil, []byte(nonce), []byte(cipherText), nil) + if err != nil { + return "", err + } + + return string(plaintext), nil +} + +const keyBytes = 16 +const iterations = 16384 +const relativeMemoryCost = 8 +const relativeCPUCost = 1 + +func DeriveKey(password string, salt []byte) (string, []byte, error) { + if salt == nil { + salt = make([]byte, keyBytes) + if _, err := rand.Read(salt); err != nil { + return "", nil, err + } + } + + key, err := scrypt.Key([]byte(password), salt, iterations, relativeMemoryCost, relativeCPUCost, keyBytes) + if err != nil { + return "", nil, err + } + + return string(key), salt, nil +} |