-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor crypto into its own package and add a helper to message stor…
…es to do enc/dec
- Loading branch information
1 parent
f8d4d36
commit 7b90af2
Showing
14 changed files
with
206 additions
and
203 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package crypto | ||
|
||
import ( | ||
"crypto/aes" | ||
"crypto/cipher" | ||
"crypto/rand" | ||
"encoding/hex" | ||
"fmt" | ||
"io" | ||
) | ||
|
||
type EntityEncryptHelper struct{} | ||
|
||
func (e EntityEncryptHelper) Encrypt(text, pass, salt string) (string, error) { | ||
// derive a key from the pass | ||
key, err := StrongKey(pass, salt) | ||
if err != nil { | ||
return "", err | ||
} | ||
ciphertext, err := EncryptAES(key, text) | ||
if err != nil { | ||
return "", err | ||
} | ||
return ciphertext, nil | ||
} | ||
|
||
// Decrypt cipher text with a given PIN which will be used to derive a key | ||
func (e EntityEncryptHelper) Decrypt(ciphertext, pass, salt string) (string, error) { | ||
// derive a key from the pass | ||
key, err := StrongKey(pass, salt) | ||
if err != nil { | ||
return "", err | ||
} | ||
plaintext, err := DecryptAES(key, ciphertext) | ||
if err != nil { | ||
return "", err | ||
} | ||
return plaintext, nil | ||
} | ||
|
||
// Encrypts the plain text with a given key | ||
// see StrongKey() to create one | ||
func EncryptAES(key []byte, plaintext string) (string, error) { | ||
cipherBlock, err := aes.NewCipher(key) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to create cipher: %w", err) | ||
} | ||
|
||
gcm, err := cipher.NewGCM(cipherBlock) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to wrap cipher: %w", err) | ||
} | ||
|
||
// Never use more than 2^32 random nonces with a given key because of the risk of a repeat. | ||
nonce := make([]byte, gcm.NonceSize()) | ||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil { | ||
return "", fmt.Errorf("failed create nonce: %w", err) | ||
} | ||
|
||
// ciphertext here is actually nonce+ciphertext | ||
// So that when we decrypt, just knowing the nonce size | ||
// is enough to separate it from the ciphertext. | ||
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil) | ||
|
||
return hex.EncodeToString(ciphertext), nil | ||
} | ||
|
||
// Decrypt the cipher text using the key provided | ||
func DecryptAES(key []byte, ct string) (string, error) { | ||
ciphertext, _ := hex.DecodeString(ct) | ||
cipherBlock, err := aes.NewCipher(key) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to create cipher: %w", err) | ||
} | ||
|
||
gcm, err := cipher.NewGCM(cipherBlock) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to wrap cipher: %w", err) | ||
} | ||
|
||
// Since we know the ciphertext is actually nonce+ciphertext | ||
// And len(nonce) == NonceSize(). We can separate the two. | ||
nonceSize := gcm.NonceSize() | ||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] | ||
|
||
plaintext, err := gcm.Open(nil, []byte(nonce), []byte(ciphertext), nil) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to decrypt: %w", err) | ||
} | ||
|
||
return string(plaintext), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package crypto | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"encoding/binary" | ||
"encoding/hex" | ||
"fmt" | ||
"io" | ||
|
||
"golang.org/x/crypto/hkdf" | ||
) | ||
|
||
// simple pin number generator | ||
// returns 4-5 digits | ||
func MakePin() (string, error) { | ||
b, err := generateRandomBytes(16) | ||
if err != nil { | ||
return "", err | ||
} | ||
pin := binary.BigEndian.Uint16(b) | ||
return fmt.Sprintf("%d", pin), nil | ||
} | ||
|
||
// simple random token generator (url encoded) | ||
func MakeToken() (string, error) { | ||
b, err := generateRandomBytes(32) | ||
if err != nil { | ||
return "", err | ||
} | ||
return base64.URLEncoding.EncodeToString(b), nil | ||
} | ||
|
||
// simple text hashing | ||
func HashText(text string) string { | ||
textHash := sha256.Sum256([]byte(text)) | ||
return hex.EncodeToString(textHash[:]) | ||
} | ||
|
||
const keySizeBytes = 16 // 128-bit key | ||
|
||
// Use HMAC Key Derivation Function (HKDF) to derive a strong key (RFC5869) | ||
// This function is useful to derive a key from some small password text the user knows | ||
func StrongKey(passText string, saltText string) ([]byte, error) { | ||
hashFn := sha256.New | ||
hashSize := hashFn().Size() | ||
passBytes := []byte(passText) | ||
if len(saltText) != hashSize { | ||
return nil, fmt.Errorf("invalid salt length, must be %d", hashSize) | ||
} | ||
saltBytes := []byte(saltText) | ||
hkdf := hkdf.New(hashFn, passBytes, saltBytes, nil) | ||
key := make([]byte, keySizeBytes) | ||
if _, err := io.ReadFull(hkdf, key); err != nil { | ||
return nil, err | ||
} | ||
return key, nil | ||
} | ||
|
||
// Generate cryptographically secure random of a given length | ||
func generateRandomBytes(size uint32) ([]byte, error) { | ||
bucket := make([]byte, size) | ||
if _, err := rand.Read(bucket); err != nil { | ||
return nil, err | ||
} | ||
return bucket, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.