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  }