// Package encryption provides functions to encode/decode AES encrypted secrets. // // Encryption is used to store sensitive information in the database, such as 2FA TOTP secrets // for users who have 2FA authentication enabled. // // For new key generation, see pkg/config/variable.go#NewAESKey. package encryption import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "errors" "fmt" "io" "code.nonshy.com/nonshy/website/pkg/config" ) // Encrypt a byte stream using the site's AES passphrase. func Encrypt(input []byte) ([]byte, error) { if len(config.Current.Encryption.AESKey) == 0 { return nil, errors.New("AES key not configured") } // Generate a new AES cipher. c, err := aes.NewCipher(config.Current.Encryption.AESKey) if err != nil { return nil, err } // gcm or Galois/Counter Mode gcm, err := cipher.NewGCM(c) if err != nil { return nil, err } // Create a new byte array the size of the GCM nonce // which must be passed to Seal. nonce := make([]byte, gcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, fmt.Errorf("populating the nonce: %s", err) } // Encrypt the text using the Seal function. // Seal encrypts and authenticates plaintext, authenticates the // additional data and appends the result to dst, returning the // updated slice. The nonce must be NonceSize() bytes long and // unique for all time, for a given key. result := gcm.Seal(nonce, nonce, input, nil) return result, nil } // EncryptString encrypts a string value and returns the cipher text. func EncryptString(input string) ([]byte, error) { return Encrypt([]byte(input)) } // Decrypt a byte stream using the site's AES passphrase. func Decrypt(data []byte) ([]byte, error) { if len(config.Current.Encryption.AESKey) == 0 { return nil, errors.New("AES key not configured") } c, err := aes.NewCipher(config.Current.Encryption.AESKey) if err != nil { return nil, err } gcm, err := cipher.NewGCM(c) if err != nil { return nil, err } nonceSize := gcm.NonceSize() if len(data) < nonceSize { return nil, errors.New("ciphertext data less than nonceSize") } nonce, ciphertext := data[:nonceSize], data[nonceSize:] plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, err } return plaintext, nil } // DecryptString decrypts a string value from ciphertext. func DecryptString(data []byte) (string, error) { decoded, err := Decrypt(data) if err != nil { return "", err } return string(decoded), nil } // Hash a byte array as SHA256 and returns the hex string. func Hash(input []byte) string { h := sha256.New() h.Write(input) return fmt.Sprintf("%x", h.Sum(nil)) } // VerifyHash hashes a byte array and checks the result. func VerifyHash(input []byte, expect string) bool { return Hash(input) == expect }