all repos — safe @ main

easily encrypt and decrypt in go

safe.go (view raw)

 1package safe
 2
 3import (
 4	"bytes"
 5	"crypto/aes"
 6	"crypto/cipher"
 7	"crypto/rand"
 8	"encoding/hex"
 9	"errors"
10)
11
12const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
13
14var (
15	charsetLength = len(charset)
16)
17
18type Safe struct {
19	c cipher.Block
20}
21
22func get32ByteString() string {
23	randomBytes := make([]byte, 32)
24	_, err := rand.Read(randomBytes)
25	if err != nil {
26		panic(err)
27	}
28
29	passphrase := make([]byte, 32)
30	for i := 0; i < 32; i++ {
31		passphrase[i] = charset[int(randomBytes[i])%charsetLength]
32	}
33	return string(passphrase)
34
35}
36
37func NewSafe(password string) *Safe {
38	if len(password) != 32 {
39		println("WARNING: Using a random passphrase. Please use a fixed passphrase for production use.")
40		password = get32ByteString()
41	}
42
43	key := []byte(password)
44	c, err := aes.NewCipher(key)
45	if err != nil {
46		panic(err)
47	}
48	return &Safe{c}
49}
50
51func (s *Safe) Encrypt(plaintext string) string {
52	blockSize := s.c.BlockSize()
53	plaintextBytes := []byte(plaintext)
54
55	// PKCS#7 padding
56	padding := blockSize - len(plaintextBytes)%blockSize
57	padtext := append(plaintextBytes, bytes.Repeat([]byte{byte(padding)}, padding)...)
58
59	ciphertext := make([]byte, blockSize+len(padtext))
60	iv := ciphertext[:blockSize]
61	if _, err := rand.Read(iv); err != nil {
62		panic(err)
63	}
64
65	mode := cipher.NewCBCEncrypter(s.c, iv)
66	mode.CryptBlocks(ciphertext[blockSize:], padtext)
67
68	return hex.EncodeToString(ciphertext)
69}
70
71func (s *Safe) Decrypt(ciphertextHex string) (string, error) {
72	ciphertext, err := hex.DecodeString(ciphertextHex)
73	if err != nil {
74		return "", err
75	}
76
77	blockSize := s.c.BlockSize()
78	if len(ciphertext)%blockSize != 0 {
79		return "", errors.New("ciphertext is not a multiple of the block size")
80	}
81
82	iv := ciphertext[:blockSize]
83	ciphertext = ciphertext[blockSize:]
84
85	mode := cipher.NewCBCDecrypter(s.c, iv)
86	mode.CryptBlocks(ciphertext, ciphertext)
87
88	// Remove padding
89	padding := int(ciphertext[len(ciphertext)-1])
90	if padding > blockSize || padding <= 0 {
91		return "", errors.New("invalid padding")
92	}
93
94	return string(ciphertext[:len(ciphertext)-padding]), nil
95}