From 2bb181e315b06cf7e7db816b5e300c20389b083c Mon Sep 17 00:00:00 2001 From: pilinux Date: Thu, 10 Oct 2024 17:16:52 +0200 Subject: [PATCH] feat: extend ChaCha20 and XChaCha20 to encrypt any file --- _example/xchacha20poly1305/dummy.pdf | Bin 0 -> 26125 bytes _example/xchacha20poly1305/main.go | 65 +++++++++++++ chaCha20.go | 137 ++++++++++++++++++++------- go.mod | 4 +- go.sum | 8 +- 5 files changed, 174 insertions(+), 40 deletions(-) create mode 100644 _example/xchacha20poly1305/dummy.pdf diff --git a/_example/xchacha20poly1305/dummy.pdf b/_example/xchacha20poly1305/dummy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a38ee8d79029a01630b8030c8cf40f6bcad3d69b GIT binary patch literal 26125 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ς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 G^nU=G?snb) literal 0 HcmV?d00001 diff --git a/_example/xchacha20poly1305/main.go b/_example/xchacha20poly1305/main.go index 315ede2..f861634 100644 --- a/_example/xchacha20poly1305/main.go +++ b/_example/xchacha20poly1305/main.go @@ -4,6 +4,7 @@ package main import ( "crypto/rand" "fmt" + "os" "golang.org/x/crypto/argon2" @@ -61,4 +62,68 @@ func main() { return } fmt.Println("plaintext:", plaintext) + + // ============================================================================ + // encrypt a file (dummy.pdf) using XChaCha20-Poly1305 + // the encrypted file will be saved as dummy.pdf.enc + // ============================================================================ + filename := "dummy.pdf" + encryptedFilename := filename + ".enc" + decryptedFilename := filename + + // read the file + pdfBytes, err := os.ReadFile(filename) + if err != nil { + fmt.Println(err) + return + } + + // encrypt the data + ciphertext, err = crypt.EncryptByteXChacha20poly1305WithNonceAppended(key, pdfBytes) + if err != nil { + fmt.Println(err) + return + } + + // save the encrypted data to a file + err = os.WriteFile(encryptedFilename, ciphertext, 0644) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("encrypted file:", encryptedFilename) + + // delete the original file + err = os.Remove(filename) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("original file deleted:", filename) + + // ============================================================================ + // decrypt the encrypted file (dummy.pdf.enc) using XChaCha20-Poly1305 + // the decrypted file will be saved as dummy.pdf + // ============================================================================ + // read the encrypted file + encryptedPdfBytes, err := os.ReadFile(encryptedFilename) + if err != nil { + fmt.Println(err) + return + } + + // decrypt the data + decryptedPdfBytes, err := crypt.DecryptByteXChacha20poly1305WithNonceAppended(key, encryptedPdfBytes) + if err != nil { + fmt.Println(err) + return + } + + // save the decrypted data to a file + err = os.WriteFile(decryptedFilename, decryptedPdfBytes, 0644) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("decrypted file:", decryptedFilename) } diff --git a/chaCha20.go b/chaCha20.go index 02d4206..1d3fec7 100644 --- a/chaCha20.go +++ b/chaCha20.go @@ -8,9 +8,9 @@ import ( "golang.org/x/crypto/chacha20poly1305" ) -// EncryptChacha20poly1305 encrypts and authenticates the given message with +// EncryptByteChacha20poly1305 encrypts and authenticates the given message (bytes) with // ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. -func EncryptChacha20poly1305(key []byte, text string) (ciphertext []byte, nonce []byte, err error) { +func EncryptByteChacha20poly1305(key []byte, input []byte) (ciphertext []byte, nonce []byte, err error) { // create a new ChaCha20-Poly1305 AEAD using the given 256-bit key aead, err := chacha20poly1305.New(key) if err != nil { @@ -26,18 +26,20 @@ func EncryptChacha20poly1305(key []byte, text string) (ciphertext []byte, nonce return } - // data to be encrypted - data := []byte(text) - // encrypt the data - ciphertext = aead.Seal(nil, nonce, data, nil) - + ciphertext = aead.Seal(nil, nonce, input, nil) return } -// DecryptChacha20poly1305 decrypts and authenticates the given message with +// EncryptChacha20poly1305 encrypts and authenticates the given message (string) with // ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. -func DecryptChacha20poly1305(key, nonce, ciphertext []byte) (text string, err error) { +func EncryptChacha20poly1305(key []byte, text string) (ciphertext []byte, nonce []byte, err error) { + return EncryptByteChacha20poly1305(key, []byte(text)) +} + +// DecryptByteChacha20poly1305 decrypts and authenticates the given ciphertext with +// ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. +func DecryptByteChacha20poly1305(key, nonce, ciphertext []byte) (plaintext []byte, err error) { // create a new ChaCha20-Poly1305 AEAD using the given 256-bit key aead, err := chacha20poly1305.New(key) if err != nil { @@ -46,45 +48,78 @@ func DecryptChacha20poly1305(key, nonce, ciphertext []byte) (text string, err er } // decrypt the data - plaintext, err := aead.Open(nil, nonce, ciphertext, nil) + plaintext, err = aead.Open(nil, nonce, ciphertext, nil) if err != nil { err = fmt.Errorf("error decrypting data: %v", err) return } - text = string(plaintext) return } -// EncryptChacha20poly1305WithNonceAppended encrypts and authenticates the given message with +// DecryptChacha20poly1305 decrypts and authenticates the given ciphertext with +// ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. +func DecryptChacha20poly1305(key, nonce, ciphertext []byte) (text string, err error) { + // decrypt the data + plaintext, err := DecryptByteChacha20poly1305(key, nonce, ciphertext) + if err != nil { + return + } + + text = string(plaintext) + return +} + +// EncryptByteChacha20poly1305WithNonceAppended encrypts and authenticates the given message (bytes) with // ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. // It appends the ciphertext to the nonce [ciphertext = nonce + ciphertext]. -func EncryptChacha20poly1305WithNonceAppended(key []byte, text string) (ciphertext []byte, err error) { - ciphertext, nonce, err := EncryptChacha20poly1305(key, text) +func EncryptByteChacha20poly1305WithNonceAppended(key []byte, input []byte) (ciphertext []byte, err error) { + ciphertext, nonce, err := EncryptByteChacha20poly1305(key, input) if err != nil { return } + ciphertext = append(nonce, ciphertext...) return } -// DecryptChacha20poly1305WithNonceAppended decrypts and authenticates the given message with +// EncryptChacha20poly1305WithNonceAppended encrypts and authenticates the given message (string) with +// ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. +// It appends the ciphertext to the nonce [ciphertext = nonce + ciphertext]. +func EncryptChacha20poly1305WithNonceAppended(key []byte, text string) (ciphertext []byte, err error) { + return EncryptByteChacha20poly1305WithNonceAppended(key, []byte(text)) +} + +// DecryptByteChacha20poly1305WithNonceAppended decrypts and authenticates the given ciphertext with // ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. // It expects the ciphertext along with the nonce [ciphertext = nonce + ciphertext]. -func DecryptChacha20poly1305WithNonceAppended(key, ciphertext []byte) (text string, err error) { +func DecryptByteChacha20poly1305WithNonceAppended(key, ciphertext []byte) (plaintext []byte, err error) { nonceSize := chacha20poly1305.NonceSize if len(ciphertext) < nonceSize { err = errors.New("ciphertext is too short") return } + nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] - text, err = DecryptChacha20poly1305(key, nonce, ciphertext) + return DecryptByteChacha20poly1305(key, nonce, ciphertext) +} + +// DecryptChacha20poly1305WithNonceAppended decrypts and authenticates the given ciphertext with +// ChaCha20-Poly1305 AEAD using the given 256-bit key and 96-bit nonce. +// It expects the ciphertext along with the nonce [ciphertext = nonce + ciphertext]. +func DecryptChacha20poly1305WithNonceAppended(key, ciphertext []byte) (text string, err error) { + plaintext, err := DecryptByteChacha20poly1305WithNonceAppended(key, ciphertext) + if err != nil { + return + } + + text = string(plaintext) return } -// EncryptXChacha20poly1305 encrypts and authenticates the given message with +// EncryptByteXChacha20poly1305 encrypts and authenticates the given message (bytes) with // XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. -func EncryptXChacha20poly1305(key []byte, text string) (ciphertext []byte, nonce []byte, err error) { +func EncryptByteXChacha20poly1305(key []byte, input []byte) (ciphertext []byte, nonce []byte, err error) { // create a new XChaCha20-Poly1305 AEAD using the given 256-bit key aead, err := chacha20poly1305.NewX(key) if err != nil { @@ -100,18 +135,20 @@ func EncryptXChacha20poly1305(key []byte, text string) (ciphertext []byte, nonce return } - // data to be encrypted - data := []byte(text) - // encrypt the data - ciphertext = aead.Seal(nil, nonce, data, nil) - + ciphertext = aead.Seal(nil, nonce, input, nil) return } -// DecryptXChacha20poly1305 decrypts and authenticates the given message with +// EncryptXChacha20poly1305 encrypts and authenticates the given message (string) with // XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. -func DecryptXChacha20poly1305(key, nonce, ciphertext []byte) (text string, err error) { +func EncryptXChacha20poly1305(key []byte, text string) (ciphertext []byte, nonce []byte, err error) { + return EncryptByteXChacha20poly1305(key, []byte(text)) +} + +// DecryptByteXChacha20poly1305 decrypts and authenticates the given ciphertext with +// XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. +func DecryptByteXChacha20poly1305(key, nonce, ciphertext []byte) (plaintext []byte, err error) { // create a new XChaCha20-Poly1305 AEAD using the given 256-bit key aead, err := chacha20poly1305.NewX(key) if err != nil { @@ -120,21 +157,33 @@ func DecryptXChacha20poly1305(key, nonce, ciphertext []byte) (text string, err e } // decrypt the data - plaintext, err := aead.Open(nil, nonce, ciphertext, nil) + plaintext, err = aead.Open(nil, nonce, ciphertext, nil) if err != nil { err = fmt.Errorf("error decrypting data: %v", err) return } - text = string(plaintext) return } -// EncryptXChacha20poly1305WithNonceAppended encrypts and authenticates the given message with +// DecryptXChacha20poly1305 decrypts and authenticates the given ciphertext with +// XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. +func DecryptXChacha20poly1305(key, nonce, ciphertext []byte) (text string, err error) { + // decrypt the data + plaintext, err := DecryptByteXChacha20poly1305(key, nonce, ciphertext) + if err != nil { + return + } + + text = string(plaintext) + return +} + +// EncryptByteXChacha20poly1305WithNonceAppended encrypts and authenticates the given message (bytes) with // XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. // It appends the ciphertext to the nonce [ciphertext = nonce + ciphertext]. -func EncryptXChacha20poly1305WithNonceAppended(key []byte, text string) (ciphertext []byte, err error) { - ciphertext, nonce, err := EncryptXChacha20poly1305(key, text) +func EncryptByteXChacha20poly1305WithNonceAppended(key []byte, input []byte) (ciphertext []byte, err error) { + ciphertext, nonce, err := EncryptByteXChacha20poly1305(key, input) if err != nil { return } @@ -142,16 +191,36 @@ func EncryptXChacha20poly1305WithNonceAppended(key []byte, text string) (ciphert return } -// DecryptXChacha20poly1305WithNonceAppended decrypts and authenticates the given message with +// EncryptXChacha20poly1305WithNonceAppended encrypts and authenticates the given message (string) with +// XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. +// It appends the ciphertext to the nonce [ciphertext = nonce + ciphertext]. +func EncryptXChacha20poly1305WithNonceAppended(key []byte, text string) (ciphertext []byte, err error) { + return EncryptByteXChacha20poly1305WithNonceAppended(key, []byte(text)) +} + +// DecryptByteXChacha20poly1305WithNonceAppended decrypts and authenticates the given ciphertext with // XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. // It expects the ciphertext along with the nonce [ciphertext = nonce + ciphertext]. -func DecryptXChacha20poly1305WithNonceAppended(key, ciphertext []byte) (text string, err error) { +func DecryptByteXChacha20poly1305WithNonceAppended(key, ciphertext []byte) (plaintext []byte, err error) { nonceSize := chacha20poly1305.NonceSizeX if len(ciphertext) < nonceSize { err = errors.New("ciphertext is too short") return } + nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] - text, err = DecryptXChacha20poly1305(key, nonce, ciphertext) + return DecryptByteXChacha20poly1305(key, nonce, ciphertext) +} + +// DecryptXChacha20poly1305WithNonceAppended decrypts and authenticates the given ciphertext with +// XChaCha20-Poly1305 AEAD using the given 256-bit key and 192-bit nonce. +// It expects the ciphertext along with the nonce [ciphertext = nonce + ciphertext]. +func DecryptXChacha20poly1305WithNonceAppended(key, ciphertext []byte) (text string, err error) { + plaintext, err := DecryptByteXChacha20poly1305WithNonceAppended(key, ciphertext) + if err != nil { + return + } + + text = string(plaintext) return } diff --git a/go.mod b/go.mod index 6294b28..006ae33 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,6 @@ module github.com/pilinux/crypt go 1.20 -require golang.org/x/crypto v0.27.0 +require golang.org/x/crypto v0.28.0 -require golang.org/x/sys v0.25.0 // indirect +require golang.org/x/sys v0.26.0 // indirect diff --git a/go.sum b/go.sum index 16773a1..25fba75 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=