seekia/internal/cryptography/chaPolyShrink/chaPolyShrink.go

154 lines
3.8 KiB
Go
Raw Normal View History

// chaPolyShrink provides functions to encrypt or decrypt bytes using ChaPolyShrink
// ChaPolyShrink first compresses bytes using zlib, adds padding to obscure size of encrypted bytes, and ciphers data using chaPoly
package chaPolyShrink
import "encoding/binary"
import "golang.org/x/crypto/chacha20poly1305"
import "compress/zlib"
import "crypto/rand"
import "errors"
import "bytes"
import "io"
import "slices"
// A paddingSize is specified, which adds padding so that resulting encrypted result size is divisible by paddingSize
//Outputs:
// -[]byte: ChaPoly Encrypted bytes
// -error
func EncryptChaPolyShrink(input []byte, key [32]byte, nonce [24]byte, compressContent bool, paddingSize int, includeAdditionalData bool, additionalData [32]byte)([]byte, error){
if (paddingSize > 10000000 || paddingSize < 0) {
return nil, errors.New("EncryptChaPolyShrink called with invalid paddingSize.")
}
getCompressionStrength := func()int{
if (compressContent == false){
return 0
}
return 9
}
compressionStrength := getCompressionStrength()
var compressedBuffer bytes.Buffer
compressorWriter, err := zlib.NewWriterLevel(&compressedBuffer, compressionStrength)
if (err != nil) { return nil, err }
compressorWriter.Write(input)
compressorWriter.Close()
compressedBytes := compressedBuffer.Bytes()
compressedBytesLength := len(compressedBytes)
getNeededPaddingLength := func()int{
if (paddingSize == 0){
return 0
}
if (compressedBytesLength < paddingSize){
paddingLength := paddingSize - compressedBytesLength
return paddingLength
}
remainder := compressedBytesLength % paddingSize
if (remainder == 0){
return 0
}
paddingLengthInBytes := paddingSize - remainder
return paddingLengthInBytes
}
neededPaddingLength := getNeededPaddingLength()
paddingLengthUint32 := uint32(neededPaddingLength)
paddingLengthHeader := make([]byte, 4)
binary.LittleEndian.PutUint32(paddingLengthHeader, paddingLengthUint32)
paddingBytes := make([]byte, neededPaddingLength)
_, err = rand.Read(paddingBytes[:])
if (err != nil){ return nil, err }
compressedBytesWithPaddingAndHeader := slices.Concat(paddingLengthHeader, paddingBytes, compressedBytes)
getAdditionalDataParameter := func()[]byte{
if (includeAdditionalData == false){
return nil
}
result := additionalData[:]
return result
}
additionalDataParameter := getAdditionalDataParameter()
cipherObject, err := chacha20poly1305.NewX(key[:])
if (err != nil) { return nil, err }
encryptedMessage := cipherObject.Seal(nil, nonce[:], compressedBytesWithPaddingAndHeader, additionalDataParameter)
return encryptedMessage, nil
}
//Outputs:
// -bool: Able to decrypt
// -[]byte: Decrypted bytes
// -error (will return error if inputs are invalid)
func DecryptChaPolyShrink(inputBytes []byte, key [32]byte, nonce [24]byte, additionalDataExists bool, additionalData [32]byte)(bool, []byte, error){
getAdditionalDataParameter := func()[]byte{
if (additionalDataExists == false){
return nil
}
result := additionalData[:]
return result
}
additionalDataParameter := getAdditionalDataParameter()
cipherObject, err := chacha20poly1305.NewX(key[:])
if (err != nil) { return false, nil, err }
decryptedBytes, err := cipherObject.Open(nil, nonce[:], inputBytes, additionalDataParameter)
if (err != nil) {
return false, nil, nil
}
paddingHeader := decryptedBytes[:4]
paddingBytesUint32 := binary.LittleEndian.Uint32(paddingHeader[:])
endOfPaddingIndex := 4 + paddingBytesUint32
compressedBytes := decryptedBytes[endOfPaddingIndex:]
compressedBytesReader := bytes.NewReader(compressedBytes)
decompressedReader, err := zlib.NewReader(compressedBytesReader)
if (err != nil) {
return false, nil, nil
}
decompressedBytes, err := io.ReadAll(decompressedReader)
if (err != nil) {
return false, nil, nil
}
return true, decompressedBytes, nil
}