From 5d037e8297a192996b7281af0ca761c160aaed30 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Fri, 15 May 2020 17:58:09 -0600 Subject: Add encryption to content when password is specified --- backend/security/encrypt.go | 65 +++++++++++++++++++++++++++++++++++++++++++++ backend/security/hash.go | 41 ++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 backend/security/encrypt.go create mode 100644 backend/security/hash.go (limited to 'backend/security') diff --git a/backend/security/encrypt.go b/backend/security/encrypt.go new file mode 100644 index 0000000..fff027c --- /dev/null +++ b/backend/security/encrypt.go @@ -0,0 +1,65 @@ +package security + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "golang.org/x/crypto/scrypt" +) + +func Encrypt(key, data []byte) ([]byte, error) { + blockCipher, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(blockCipher) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = rand.Read(nonce); err != nil { + return nil, err + } + + cipherText := gcm.Seal(nonce, nonce, data, nil) + + return cipherText, nil +} + +func Decrypt(key, data []byte) ([]byte, error) { + blockCipher, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(blockCipher) + if err != nil { + return nil, err + } + + nonce, cipherText := data[:gcm.NonceSize()], data[gcm.NonceSize():] + plaintext, err := gcm.Open(nil, nonce, cipherText, nil) + if err != nil { + return nil, err + } + + return plaintext, nil +} + +func DeriveKey(password, salt []byte) ([]byte, []byte, error) { + if salt == nil { + salt = make([]byte, 16) + if _, err := rand.Read(salt); err != nil { + return nil, nil, err + } + } + + key, err := scrypt.Key(password, salt, 16384, 8, 1, 16) + if err != nil { + return nil, nil, err + } + + return key, salt, nil +} diff --git a/backend/security/hash.go b/backend/security/hash.go new file mode 100644 index 0000000..b6ce167 --- /dev/null +++ b/backend/security/hash.go @@ -0,0 +1,41 @@ +package security + +import ( + "crypto/md5" + "encoding/hex" + "golang.org/x/crypto/bcrypt" + "math/big" + "time" +) + +const UrlLength = 7 + +// GenerateURI creates a unique identifier for a paste based on ip and timestamp +func GenerateURI(ip string) string { + timeStamp := time.Now().String() + return hashString(ip + timeStamp)[:UrlLength] +} + +// hashes using MD5 and then converts to base 62 +func hashString(text string) string { + hash := md5.Sum([]byte(text)) + hexStr := hex.EncodeToString(hash[:]) + + bi := big.NewInt(0) + bi.SetString(hexStr, 16) + return bi.Text(62) +} + +func HashPassword(password string) (string, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(hashedPassword), err +} + +func PasswordsEqual(dbPassword, parsedPassword string) bool { + dbPassBytes := []byte(dbPassword) + parsedPassBytes := []byte(parsedPassword) + compErr := bcrypt.CompareHashAndPassword(dbPassBytes, parsedPassBytes) + + // if comparison error, the given password is not valid + return compErr == nil +} \ No newline at end of file -- cgit v1.2.3 From 4e03758e92887fe4251a73ce8125b93e8624b6a2 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Fri, 15 May 2020 19:00:21 -0600 Subject: Add comments and clean up encryption --- backend/security/encrypt.go | 56 +++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 20 deletions(-) (limited to 'backend/security') diff --git a/backend/security/encrypt.go b/backend/security/encrypt.go index fff027c..4af5e3c 100644 --- a/backend/security/encrypt.go +++ b/backend/security/encrypt.go @@ -4,62 +4,78 @@ import ( "crypto/aes" "crypto/cipher" "crypto/rand" + "errors" "golang.org/x/crypto/scrypt" ) -func Encrypt(key, data []byte) ([]byte, error) { - blockCipher, err := aes.NewCipher(key) +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 nil, err + return "", err } + // wrap block cipher with Galois Counter Mode and standard nonce length gcm, err := cipher.NewGCM(blockCipher) if err != nil { - return nil, err + 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 nil, err + return "", err } - cipherText := gcm.Seal(nonce, nonce, data, nil) + // seal nonce with data to use during decryption + cipherText := gcm.Seal(nonce, nonce, []byte(data), nil) - return cipherText, nil + return string(cipherText), nil } -func Decrypt(key, data []byte) ([]byte, error) { - blockCipher, err := aes.NewCipher(key) +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 nil, err + return "", err } gcm, err := cipher.NewGCM(blockCipher) if err != nil { - return nil, err + return "", err } + // extract the nonce from the data nonce, cipherText := data[:gcm.NonceSize()], data[gcm.NonceSize():] - plaintext, err := gcm.Open(nil, nonce, cipherText, nil) + + // use nonce to decrypt the data + plaintext, err := gcm.Open(nil, []byte(nonce), []byte(cipherText), nil) if err != nil { - return nil, err + return "", err } - return plaintext, nil + return string(plaintext), nil } -func DeriveKey(password, salt []byte) ([]byte, []byte, error) { +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, 16) + salt = make([]byte, keyBytes) if _, err := rand.Read(salt); err != nil { - return nil, nil, err + return "", nil, err } } - key, err := scrypt.Key(password, salt, 16384, 8, 1, 16) + key, err := scrypt.Key([]byte(password), salt, iterations, relativeMemoryCost, relativeCPUCost, keyBytes) if err != nil { - return nil, nil, err + return "", nil, err } - return key, salt, nil + return string(key), salt, nil } -- cgit v1.2.3