dethnote/src/vault/security.go

77 lines
1.8 KiB
Go

package vault
import (
"bytes"
"crypto/aes"
"crypto/sha256"
"encoding/hex"
"fmt"
"strings"
"golang.org/x/crypto/bcrypt"
)
// Tunable params for newly generated passwords.
var (
// bcrypt hash cost. The higher, the longer it takes to derive a password.
// Helps protect the hashes against brute force attacks.
HashCost = 14
)
// GenerateHash generates a bcrypt hash (slow) of a password, and then returns
// a SHA-256 sum of the hash.
func GenerateHash(password string) ([]byte, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), HashCost)
if err != nil {
return nil, err
}
// Make it a SHA-256 hash for easy use with AES-256.
hasher := sha256.New()
_, err = hasher.Write(hash)
bin := hasher.Sum(nil)
return bin, err
}
// HashToFilename converts a bcrypt hash into a safe filename on disk.
//
// It takes a SHA-256 sum of the hash and then splits it into a short tree
// for filesystem efficiency.
func HashToFilename(hash []byte) string {
sha := hex.EncodeToString(hash)
// Split it into a path tree.
var (
p1 = sha[:2]
p2 = sha[2:20]
p3 = sha[20:40]
p4 = sha[40:]
)
filename := strings.Join([]string{p1, p2, p3, p4}, "/")
return filename
}
// Encrypt data using AES-256.
func Encrypt(key []byte, data []byte) ([]byte, error) {
// Initialize an AES cipher.
encryptor, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("Encrypt: aes.NewCipher error: %s", err)
}
// Pad the plaintext until it's a multiple of the AES block size.
buf := bytes.NewBuffer(data)
for buf.Len()%aes.BlockSize != 0 {
buf.WriteByte(' ')
}
Log.Info("data: %+v", data)
Log.Info("buf: %+v", buf.Bytes())
// Encode the data to ciphertext.
ciphertext := make([]byte, aes.BlockSize+buf.Len())
encryptor.Encrypt(ciphertext, buf.Bytes())
fmt.Printf("cipher: %+v\n", ciphertext)
return ciphertext, nil
}