You've already forked rsa-file-encryption
added hmac validation #1
@@ -1,7 +1,7 @@
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "*"
|
- "**"
|
||||||
|
|
||||||
name: 🧪 ✨ Unit Tests Workflow
|
name: 🧪 ✨ Unit Tests Workflow
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,20 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/hmac"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const hmacKey = "::HMAC::"
|
||||||
|
|
||||||
type EncryptedFile struct {
|
type EncryptedFile struct {
|
||||||
ciphertext []byte
|
ciphertext []byte
|
||||||
|
hmac []byte
|
||||||
plainText []byte
|
plainText []byte
|
||||||
nonce []byte
|
nonce []byte
|
||||||
privatePem []byte
|
privatePem []byte
|
||||||
@@ -25,7 +30,13 @@ type EncryptedFile struct {
|
|||||||
|
|
||||||
func (f *EncryptedFile) packFile() []byte {
|
func (f *EncryptedFile) packFile() []byte {
|
||||||
file := append(f.nonce, f.ciphertext...)
|
file := append(f.nonce, f.ciphertext...)
|
||||||
return append(file, f.symmetricKeyEnc...)
|
file = append(file, f.symmetricKeyEnc...)
|
||||||
|
if len(f.hmac) > 0 {
|
||||||
|
file = append(file, []byte(hmacKey)...)
|
||||||
|
file = append(file, f.hmac...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *EncryptedFile) EncryptFile() error {
|
func (f *EncryptedFile) EncryptFile() error {
|
||||||
@@ -48,9 +59,29 @@ func (f *EncryptedFile) EncryptFile() error {
|
|||||||
cbc.CryptBlocks(ciphertext, plaintextP)
|
cbc.CryptBlocks(ciphertext, plaintextP)
|
||||||
f.ciphertext = ciphertext
|
f.ciphertext = ciphertext
|
||||||
|
|
||||||
|
mac, err := f.generateHmac()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.hmac = mac
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *EncryptedFile) generateHmac() ([]byte, error) {
|
||||||
|
if len(f.symmetricKey) == 0 {
|
||||||
|
return nil, fmt.Errorf("symmetric key is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
mac := hmac.New(sha256.New, f.symmetricKey)
|
||||||
|
mac.Write(f.nonce)
|
||||||
|
mac.Write(f.ciphertext)
|
||||||
|
|
||||||
|
f.hmac = mac.Sum(nil)
|
||||||
|
return f.hmac, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *EncryptedFile) OsReadPlainTextFile(path string) error {
|
func (f *EncryptedFile) OsReadPlainTextFile(path string) error {
|
||||||
plaintext, err := os.ReadFile(path)
|
plaintext, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,7 +115,21 @@ func (f *EncryptedFile) WriteDecryptedFileToDisk(filePath string) error {
|
|||||||
func (f *EncryptedFile) unpackFileAndDecrypt(packedFile []byte) error {
|
func (f *EncryptedFile) unpackFileAndDecrypt(packedFile []byte) error {
|
||||||
keyLen := f.privateKey.Size()
|
keyLen := f.privateKey.Size()
|
||||||
|
|
||||||
|
minReqLen := aes.BlockSize + keyLen + len(hmacKey)
|
||||||
|
|
||||||
|
if len(packedFile) < minReqLen {
|
||||||
|
return fmt.Errorf("packed file is too short to be valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Contains(packedFile, []byte(hmacKey)) {
|
||||||
|
parts := bytes.SplitN(packedFile, []byte(hmacKey), 2)
|
||||||
|
packedFile, f.hmac = parts[0], parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
lenWithoutKey := len(packedFile) - keyLen
|
lenWithoutKey := len(packedFile) - keyLen
|
||||||
|
if lenWithoutKey < aes.BlockSize {
|
||||||
|
return fmt.Errorf("packed file is too short to contain valid nonce and ciphertext")
|
||||||
|
}
|
||||||
|
|
||||||
packedFile, f.symmetricKeyEnc = packedFile[0:lenWithoutKey], packedFile[lenWithoutKey:]
|
packedFile, f.symmetricKeyEnc = packedFile[0:lenWithoutKey], packedFile[lenWithoutKey:]
|
||||||
|
|
||||||
@@ -93,10 +138,22 @@ func (f *EncryptedFile) unpackFileAndDecrypt(packedFile []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(f.hmac) > 0 {
|
||||||
|
mac, err := f.generateHmac()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hmac.Equal(mac, f.hmac) {
|
||||||
|
return fmt.Errorf("hmac verification failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a, err := aes.NewCipher(f.symmetricKey)
|
a, err := aes.NewCipher(f.symmetricKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.nonce, f.ciphertext = packedFile[0:aes.BlockSize], packedFile[aes.BlockSize:]
|
f.nonce, f.ciphertext = packedFile[0:aes.BlockSize], packedFile[aes.BlockSize:]
|
||||||
|
|
||||||
cbc := cipher.NewCBCDecrypter(a, f.nonce)
|
cbc := cipher.NewCBCDecrypter(a, f.nonce)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,6 +86,10 @@ func (f *EncryptedFile) GenerateSymmetricKey() error {
|
|||||||
func (f *EncryptedFile) ParsePublicPem() error {
|
func (f *EncryptedFile) ParsePublicPem() error {
|
||||||
pemKeyBin, _ := pem.Decode(f.PublicPem)
|
pemKeyBin, _ := pem.Decode(f.PublicPem)
|
||||||
|
|
||||||
|
if pemKeyBin == nil {
|
||||||
|
return fmt.Errorf("failed to parse PEM block containing the public key")
|
||||||
|
}
|
||||||
|
|
||||||
if bytes.Contains(f.PublicPem, []byte("-----BEGIN PUBLIC KEY-----")) {
|
if bytes.Contains(f.PublicPem, []byte("-----BEGIN PUBLIC KEY-----")) {
|
||||||
key, err := x509.ParsePKIXPublicKey(pemKeyBin.Bytes)
|
key, err := x509.ParsePKIXPublicKey(pemKeyBin.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,6 +114,10 @@ func (f *EncryptedFile) ParsePublicPem() error {
|
|||||||
func (f *EncryptedFile) ParsePrivatePem() error {
|
func (f *EncryptedFile) ParsePrivatePem() error {
|
||||||
pemKeyBin, _ := pem.Decode(f.privatePem)
|
pemKeyBin, _ := pem.Decode(f.privatePem)
|
||||||
|
|
||||||
|
if pemKeyBin == nil {
|
||||||
|
return fmt.Errorf("failed to parse PEM block containing the private key")
|
||||||
|
}
|
||||||
|
|
||||||
if bytes.Contains(f.privatePem, []byte("-----BEGIN PRIVATE KEY-----")) {
|
if bytes.Contains(f.privatePem, []byte("-----BEGIN PRIVATE KEY-----")) {
|
||||||
key, err := x509.ParsePKCS8PrivateKey(pemKeyBin.Bytes)
|
key, err := x509.ParsePKCS8PrivateKey(pemKeyBin.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user