Skip to content

Commit

Permalink
feat: extend ChaCha20 and XChaCha20 to encrypt any file
Browse files Browse the repository at this point in the history
  • Loading branch information
pilinux committed Oct 10, 2024
1 parent 46606a7 commit 2bb181e
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 40 deletions.
Binary file added _example/xchacha20poly1305/dummy.pdf
Binary file not shown.
65 changes: 65 additions & 0 deletions _example/xchacha20poly1305/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package main
import (
"crypto/rand"
"fmt"
"os"

"golang.org/x/crypto/argon2"

Expand Down Expand Up @@ -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)
}
137 changes: 103 additions & 34 deletions chaCha20.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -120,38 +157,70 @@ 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
}
ciphertext = append(nonce, ciphertext...)
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
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=

0 comments on commit 2bb181e

Please sign in to comment.