Files
rsa-file-encryption/crypt/file.go
2024-04-20 10:33:41 -04:00

166 lines
3.3 KiB
Go

package crypt
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/subtle"
"fmt"
"os"
)
type EncryptedFile struct {
ciphertext []byte
plainText []byte
nonce []byte
privatePem []byte
PublicPem []byte
privateKey *rsa.PrivateKey
PublicKey *rsa.PublicKey
symmetricKey []byte
symmetricKeyEnc []byte
}
func (f *EncryptedFile) packFile() []byte {
file := append(f.nonce, f.ciphertext...)
return append(file, f.symmetricKeyEnc...)
}
func (f *EncryptedFile) EncryptFile() error {
c, err := aes.NewCipher(f.symmetricKey)
if err != nil {
return err
}
f.nonce = make([]byte, aes.BlockSize)
_, err = rand.Read(f.nonce)
if err != nil {
return err
}
cbc := cipher.NewCBCEncrypter(c, f.nonce)
ciphertext := make([]byte, len(f.plainText))
ciphertext = pad(ciphertext, aes.BlockSize)
plaintextP := pad(f.plainText, aes.BlockSize)
cbc.CryptBlocks(ciphertext, plaintextP)
f.ciphertext = ciphertext
return nil
}
func (f *EncryptedFile) OsReadPlainTextFile(path string) error {
plaintext, err := os.ReadFile(path)
if err != nil {
return err
}
f.plainText = plaintext
return nil
}
func (f *EncryptedFile) WriteEncryptFileToDisk(filePath string) error {
packed := f.packFile()
err := os.WriteFile(filePath+".enc", packed, 0600)
if err != nil {
return err
}
return nil
}
func (f *EncryptedFile) WriteDecryptedFileToDisk(filePath string) error {
err := os.WriteFile(filePath, f.plainText, 0600)
if err != nil {
return err
}
return nil
}
func (f *EncryptedFile) unpackFileAndDecrypt(packedFile []byte) error {
keyLen := f.privateKey.Size()
lenWithoutKey := len(packedFile) - keyLen
packedFile, f.symmetricKeyEnc = packedFile[0:lenWithoutKey], packedFile[lenWithoutKey:]
err := f.decryptSymmetricKey()
if err != nil {
return err
}
a, err := aes.NewCipher(f.symmetricKey)
if err != nil {
return err
}
f.nonce, f.ciphertext = packedFile[0:aes.BlockSize], packedFile[aes.BlockSize:]
cbc := cipher.NewCBCDecrypter(a, f.nonce)
plainText := make([]byte, len(f.ciphertext))
cbc.CryptBlocks(plainText, f.ciphertext)
f.plainText, err = unPad(plainText)
if err != nil {
return err
}
return nil
}
func (f *EncryptedFile) OsReadCipherTextFile(path string) error {
packedFile, err := os.ReadFile(path)
if err != nil {
return err
}
err = f.unpackFileAndDecrypt(packedFile)
if err != nil {
return err
}
return nil
}
func pad(buf []byte, size int) []byte {
if size < 1 || size > 255 {
panic(fmt.Sprintf("pkcs7pad: inappropriate block size %d", size))
}
i := size - (len(buf) % size)
return append(buf, bytes.Repeat([]byte{byte(i)}, i)...)
}
func unPad(buf []byte) ([]byte, error) {
if len(buf) == 0 {
return nil, fmt.Errorf("pkcs7pad: bad padding")
}
padLen := buf[len(buf)-1]
toCheck := 255
good := 1
if toCheck > len(buf) {
toCheck = len(buf)
}
for i := 0; i < toCheck; i++ {
b := buf[len(buf)-1-i]
outOfRange := subtle.ConstantTimeLessOrEq(int(padLen), i)
equal := subtle.ConstantTimeByteEq(padLen, b)
good &= subtle.ConstantTimeSelect(outOfRange, 1, equal)
}
good &= subtle.ConstantTimeLessOrEq(1, int(padLen))
good &= subtle.ConstantTimeLessOrEq(int(padLen), len(buf))
if good != 1 {
return nil, fmt.Errorf("pkcs7pad: bad padding")
}
return buf[:len(buf)-int(padLen)], nil
}