github.com/grafviktor/keep-my-secret@v0.9.10-0.20230908165355-19f35cce90e5/internal/api/utils/utils.go (about) 1 package utils 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/rand" 7 "io" 8 mathrand "math/rand" 9 "time" 10 ) 11 12 // IsUsernameConformsPolicy - checks if username conforms with security policy. Stub function. 13 // Normally we should have generic settings for username and password complexity. But I'm reluctant 14 // to complicate this logic for a pet project. 15 func IsUsernameConformsPolicy(username string) bool { 16 return len(username) > 0 17 } 18 19 // IsPasswordConformsPolicy - checks if password conforms with security policy. Stub function. 20 func IsPasswordConformsPolicy(password string) bool { 21 return len(password) > 0 22 } 23 24 var ( 25 aesKeyLength = 24 26 validRandomChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?~" 27 ) 28 29 // GenerateRandomPassword - generates password which is used to encrypt user data 30 // Returns string of the same length as aesKeyLength 31 func GenerateRandomPassword() string { 32 mathrand.Seed(time.Now().UnixNano()) 33 34 key := make([]byte, aesKeyLength) 35 for i := range key { 36 key[i] = validRandomChars[mathrand.Intn(len(validRandomChars))] 37 } 38 39 return string(key) 40 } 41 42 func normalizeAESKey(key string) string { 43 length := len(key) 44 45 if length < aesKeyLength { 46 for i := 0; i < aesKeyLength-length; i++ { 47 key += "0" 48 } 49 } else if length > aesKeyLength { 50 key = key[:aesKeyLength] 51 } 52 53 return key 54 } 55 56 // Encrypt - encrypts data with AES using key. If key is less that supported AES key length, 57 // then it is padded with zeros. 58 // plaindata - data to be encrypted 59 // key - key to be used for encryption 60 // Returns encrypted data or error 61 func Encrypt(plaindata []byte, key string) ([]byte, error) { 62 key = normalizeAESKey(key) 63 block, err := aes.NewCipher([]byte(key)) 64 if err != nil { 65 return nil, err 66 } 67 68 ciphertext := make([]byte, aes.BlockSize+len(plaindata)) 69 iv := ciphertext[:aes.BlockSize] 70 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 71 return nil, err 72 } 73 74 cfb := cipher.NewCFBEncrypter(block, iv) 75 cfb.XORKeyStream(ciphertext[aes.BlockSize:], plaindata) 76 77 return ciphertext, nil 78 } 79 80 // Decrypt - decrypts data with AES using key. If key is less that supported AES key length, 81 // then it is padded with zeros. 82 // cipherdata - data to be decrypted 83 // key - key to be used for decryption 84 // Returns decrypted data or error 85 func Decrypt(cipherdata []byte, key string) ([]byte, error) { 86 key = normalizeAESKey(key) 87 block, err := aes.NewCipher([]byte(key)) 88 if err != nil { 89 return nil, err 90 } 91 92 iv := cipherdata[:aes.BlockSize] 93 cipherdata = cipherdata[aes.BlockSize:] 94 95 cfb := cipher.NewCFBDecrypter(block, iv) 96 plaintext := make([]byte, len(cipherdata)) 97 cfb.XORKeyStream(plaintext, cipherdata) 98 99 return plaintext, nil 100 }