// Package data provides utilities for interacting with database data // throughout Write.as. package data import ( "crypto/aes" "crypto/cipher" "crypto/rand" "errors" "fmt" ) // Encryption parameters const ( keyLen = 32 delimiter = '%' ) // Encrypt AES-encrypts given text with the given key k. // This is used for encrypting sensitive information in the database, such as // oAuth tokens and email addresses. func Encrypt(k []byte, text string) ([]byte, error) { // Validate parameters if len(k) != keyLen { return nil, errors.New(fmt.Sprintf("Invalid key length (must be %d bytes).", keyLen)) } // Encrypt plaintext with AES-GCM block, err := aes.NewCipher(k) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } // Generate nonce ns := gcm.NonceSize() nonce := make([]byte, ns) if _, err := rand.Read(nonce); err != nil { return nil, err } ciphertext := gcm.Seal(nil, nonce, []byte(text), nil) // Build text output in the format: // NonceCiphertext outtext := append(nonce, ciphertext...) return outtext, nil } // Decrypt decrypts the given ciphertext with the given key k. func Decrypt(k, ciphertext []byte) ([]byte, error) { // Decrypt ciphertext block, err := aes.NewCipher(k) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } ns := gcm.NonceSize() // Validate data if len(ciphertext) < ns { return nil, errors.New("Ciphertext is too short") } nonce := ciphertext[:ns] ciphertext = ciphertext[ns:] plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, err } return plaintext, nil }