Add decryptChacha using native go

This commit is contained in:
Neeraj Gupta 2023-10-20 13:20:49 +05:30
parent 5f029fc12f
commit 2d8b3eb215
3 changed files with 424 additions and 22 deletions

View file

@ -84,3 +84,22 @@ func deriveSubKey(masterKey []byte, context string, subKeyID uint64, subKeyLengt
hasher.Write(nil) // No data, just using key, salt, and personalization
return hasher.Sum(nil), nil
}
func DecryptChaChaBase64(data string, key []byte, nonce string) (string, []byte, error) {
// Decode data from base64
dataBytes, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return "", nil, fmt.Errorf("invalid data: %v", err)
}
// Decode nonce from base64
nonceBytes, err := base64.StdEncoding.DecodeString(nonce)
if err != nil {
return "", nil, fmt.Errorf("invalid nonce: %v", err)
}
// Decrypt data
decryptedData, err := decryptChaCha20poly1305(dataBytes, key, nonceBytes)
if err != nil {
return "", nil, fmt.Errorf("failed to decrypt data: %v", err)
}
return base64.StdEncoding.EncodeToString(decryptedData), decryptedData, nil
}

View file

@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"cli-go/utils/encoding"
"encoding/base64"
"fmt"
"github.com/jamesruan/sodium"
"io"
@ -41,8 +40,7 @@ func EncryptChaCha20poly1305(data []byte, key []byte) ([]byte, []byte, error) {
// Returns:
// - A byte slice representing the decrypted data.
// - An error object, which is nil if no error occurs.
func decryptChaCha20poly1305(data []byte, key []byte, nonce []byte) ([]byte, error) {
func decryptChaCha20poly1305LibSodium(data []byte, key []byte, nonce []byte) ([]byte, error) {
reader := bytes.NewReader(data)
header := sodium.SecretStreamXCPHeader{Bytes: nonce}
decoder, err := sodium.MakeSecretStreamXCPDecoder(
@ -63,23 +61,16 @@ func decryptChaCha20poly1305(data []byte, key []byte, nonce []byte) ([]byte, err
return decryptedData[:n], nil
}
func DecryptChaChaBase64(data string, key []byte, nonce string) (string, []byte, error) {
// Decode data from base64
dataBytes, err := base64.StdEncoding.DecodeString(data)
func decryptChaCha20poly1305(data []byte, key []byte, nonce []byte) ([]byte, error) {
decryptor, err := NewDecryptor(key, nonce)
if err != nil {
return "", nil, fmt.Errorf("invalid data: %v", err)
return nil, err
}
// Decode nonce from base64
nonceBytes, err := base64.StdEncoding.DecodeString(nonce)
decoded, _, err := decryptor.Pull(data)
if err != nil {
return "", nil, fmt.Errorf("invalid nonce: %v", err)
return nil, err
}
// Decrypt data
decryptedData, err := decryptChaCha20poly1305(dataBytes, key, nonceBytes)
if err != nil {
return "", nil, fmt.Errorf("failed to decrypt data: %v", err)
}
return base64.StdEncoding.EncodeToString(decryptedData), decryptedData, nil
return decoded, nil
}
func SecretBoxOpen(c []byte, n []byte, k []byte) ([]byte, error) {
@ -89,12 +80,7 @@ func SecretBoxOpen(c []byte, n []byte, k []byte) ([]byte, error) {
}
func SecretBoxOpenBase64(cipher string, nonce string, k []byte) ([]byte, error) {
var cp sodium.Bytes = encoding.DecodeBase64(cipher)
out, err := cp.SecretBoxOpen(sodium.SecretBoxNonce{Bytes: encoding.DecodeBase64(nonce)}, sodium.SecretBoxKey{Bytes: k})
if err != nil {
return nil, err
}
return out, nil
return SecretBoxOpen(encoding.DecodeBase64(cipher), encoding.DecodeBase64(nonce), k)
}
func SealedBoxOpen(cipherText []byte, publicKey, masterSecret []byte) ([]byte, error) {

397
internal/crypto/stream.go Normal file
View file

@ -0,0 +1,397 @@
package crypto
import (
"bytes"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"golang.org/x/crypto/chacha20"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/poly1305"
)
// public constants
const (
TagMessage = 0
TagPush = 0x01
TagRekey = 0x02
TagFinal = TagPush | TagRekey
StreamKeyBytes = chacha20poly1305.KeySize
StreamHeaderBytes = chacha20poly1305.NonceSizeX
StreamABytes = 16 + 1
)
const crypto_core_hchacha20_INPUTBYTES = 16
/* const crypto_secretstream_xchacha20poly1305_INONCEBYTES = 8 */
const crypto_secretstream_xchacha20poly1305_COUNTERBYTES = 4
var pad0 [16]byte
var invalidKey = errors.New("invalid key")
var invalidInput = errors.New("invalid input")
var cryptoFailure = errors.New("crypto failed")
type streamState struct {
k [StreamKeyBytes]byte
nonce [chacha20poly1305.NonceSize]byte
pad [8]byte
}
func (s *streamState) reset() {
for i := range s.nonce {
s.nonce[i] = 0
}
s.nonce[0] = 1
}
type Encryptor interface {
Push(m []byte, tag byte) ([]byte, error)
}
type Decryptor interface {
Pull(m []byte) ([]byte, byte, error)
}
type encryptor struct {
streamState
}
type decryptor struct {
streamState
}
func NewStreamKey() []byte {
k := make([]byte, chacha20poly1305.KeySize)
_, _ = rand.Read(k)
return k
}
func NewEncryptor(key []byte) (Encryptor, []byte, error) {
if len(key) != StreamKeyBytes {
return nil, nil, invalidKey
}
header := make([]byte, StreamHeaderBytes)
_, _ = rand.Read(header)
stream := &encryptor{}
k, err := chacha20.HChaCha20(key[:], header[:16])
if err != nil {
//fmt.Printf("error: %v", err)
return nil, nil, err
}
copy(stream.k[:], k)
stream.reset()
for i := range stream.pad {
stream.pad[i] = 0
}
for i, b := range header[crypto_core_hchacha20_INPUTBYTES:] {
stream.nonce[i+crypto_secretstream_xchacha20poly1305_COUNTERBYTES] = b
}
// fmt.Printf("stream: %+v\n", stream.streamState)
return stream, header, nil
}
func (s *encryptor) Push(plain []byte, tag byte) ([]byte, error) {
var err error
//crypto_onetimeauth_poly1305_state poly1305_state;
var poly *poly1305.MAC
//unsigned char block[64U];
var block [64]byte
//unsigned char slen[8U];
var slen [8]byte
//unsigned char *c;
//unsigned char *mac;
//
//if (outlen_p != NULL) {
//*outlen_p = 0U;
//}
mlen := len(plain)
//if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
//sodium_misuse();
//}
out := make([]byte, mlen+StreamABytes)
chacha, err := chacha20.NewUnauthenticatedCipher(s.k[:], s.nonce[:])
if err != nil {
return nil, err
}
//crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
chacha.XORKeyStream(block[:], block[:])
//crypto_onetimeauth_poly1305_init(&poly1305_state, block);
var poly_init [32]byte
copy(poly_init[:], block[:])
poly = poly1305.New(&poly_init)
// TODO add support for add data
//sodium_memzero(block, sizeof block);
//crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
//crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
//(0x10 - adlen) & 0xf);
//memset(block, 0, sizeof block);
//block[0] = tag;
memzero(block[:])
block[0] = tag
//
//crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, state->nonce, 1U, state->k);
//crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
//out[0] = block[0];
chacha.XORKeyStream(block[:], block[:])
_, _ = poly.Write(block[:])
out[0] = block[0]
//
//c = out + (sizeof tag);
c := out[1:]
//crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
//crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
//crypto_onetimeauth_poly1305_update (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
chacha.XORKeyStream(c, plain)
_, _ = poly.Write(c[:mlen])
padlen := (0x10 - len(block) + mlen) & 0xf
_, _ = poly.Write(pad0[:padlen])
//
//STORE64_LE(slen, (uint64_t) adlen);
//crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
binary.LittleEndian.PutUint64(slen[:], uint64(0))
_, _ = poly.Write(slen[:])
//STORE64_LE(slen, (sizeof block) + mlen);
//crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
binary.LittleEndian.PutUint64(slen[:], uint64(len(block)+mlen))
_, _ = poly.Write(slen[:])
//
//mac = c + mlen;
//crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
mac := c[mlen:]
copy(mac, poly.Sum(nil))
//sodium_memzero(&poly1305_state, sizeof poly1305_state);
//
//XOR_BUF(STATE_INONCE(state), mac, crypto_secretstream_xchacha20poly1305_INONCEBYTES);
//sodium_increment(STATE_COUNTER(state), crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
xor_buf(s.nonce[crypto_secretstream_xchacha20poly1305_COUNTERBYTES:], mac)
buf_inc(s.nonce[:crypto_secretstream_xchacha20poly1305_COUNTERBYTES])
// TODO
//if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
//sodium_is_zero(STATE_COUNTER(state),
//crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
//crypto_secretstream_xchacha20poly1305_rekey(state);
//}
//if (outlen_p != NULL) {
//*outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
//}
//return 0;
return out, nil
}
func NewDecryptor(key, header []byte) (Decryptor, error) {
stream := &decryptor{}
//crypto_core_hchacha20(state->k, in, k, NULL);
k, err := chacha20.HChaCha20(key, header[:16])
if err != nil {
fmt.Printf("error: %v", err)
return nil, err
}
copy(stream.k[:], k)
//_crypto_secretstream_xchacha20poly1305_counter_reset(state);
stream.reset()
//memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
// crypto_secretstream_xchacha20poly1305_INONCEBYTES);
copy(stream.nonce[crypto_secretstream_xchacha20poly1305_COUNTERBYTES:],
header[crypto_core_hchacha20_INPUTBYTES:])
//memset(state->_pad, 0, sizeof state->_pad);
copy(stream.pad[:], pad0[:])
//fmt.Printf("decryptor: %+v\n", stream.streamState)
return stream, nil
}
func (s *decryptor) Pull(in []byte) ([]byte, byte, error) {
inlen := len(in)
//crypto_onetimeauth_poly1305_state poly1305_state;
//unsigned char block[64U];
var block [64]byte
//unsigned char slen[8U];
var slen [8]byte
//unsigned char mac[crypto_onetimeauth_poly1305_BYTES];
//const unsigned char *c;
//const unsigned char *stored_mac;
//unsigned long long mlen;
//unsigned char tag;
//
//if (mlen_p != NULL) {
//*mlen_p = 0U;
//}
//if (tag_p != NULL) {
//*tag_p = 0xff;
//}
//if (inlen < crypto_secretstream_xchacha20poly1305_ABYTES) {
//return -1;
//}
if inlen < StreamABytes {
return nil, 0, invalidInput
}
//mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
mlen := inlen - StreamABytes
//if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
//sodium_misuse();
//}
chacha, err := chacha20.NewUnauthenticatedCipher(s.k[:], s.nonce[:])
if err != nil {
return nil, 0, err
}
//crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
chacha.XORKeyStream(block[:], block[:])
//crypto_onetimeauth_poly1305_init(&poly1305_state, block);
var poly_init [32]byte
copy(poly_init[:], block[:])
poly := poly1305.New(&poly_init)
// TODO
//sodium_memzero(block, sizeof block);
//crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
//crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
//(0x10 - adlen) & 0xf);
//
//memset(block, 0, sizeof block);
memzero(block[:])
//block[0] = in[0];
block[0] = in[0]
//crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, state->nonce, 1U, state->k);
chacha.XORKeyStream(block[:], block[:])
//tag = block[0];
tag := block[0]
//block[0] = in[0];
block[0] = in[0]
//crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
if _, err = poly.Write(block[:]); err != nil {
return nil, 0, err
}
//
//c = in + (sizeof tag);
c := in[1:]
//crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
if _, err = poly.Write(c[:mlen]); err != nil {
return nil, 0, err
}
//crypto_onetimeauth_poly1305_update (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
padlen := (0x10 - len(block) + mlen) & 0xf
if _, err = poly.Write(pad0[:padlen]); err != nil {
return nil, 0, err
}
//
//STORE64_LE(slen, (uint64_t) adlen);
//crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
binary.LittleEndian.PutUint64(slen[:], uint64(0))
if _, err = poly.Write(slen[:]); err != nil {
return nil, 0, err
}
//STORE64_LE(slen, (sizeof block) + mlen);
//crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
binary.LittleEndian.PutUint64(slen[:], uint64(len(block)+mlen))
if _, err = poly.Write(slen[:]); err != nil {
return nil, 0, err
}
//
//crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
//sodium_memzero(&poly1305_state, sizeof poly1305_state);
mac := poly.Sum(nil)
//
//stored_mac = c + mlen;
stored_mac := c[mlen:]
//if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
//sodium_memzero(mac, sizeof mac);
//return -1;
//}
if !bytes.Equal(mac, stored_mac) {
return nil, 0, cryptoFailure
}
//
//crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
m := make([]byte, mlen)
chacha.XORKeyStream(m, c[:mlen])
//XOR_BUF(STATE_INONCE(state), mac, crypto_secretstream_xchacha20poly1305_INONCEBYTES);
//sodium_increment(STATE_COUNTER(state), crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
xor_buf(s.nonce[crypto_secretstream_xchacha20poly1305_COUNTERBYTES:], mac)
buf_inc(s.nonce[:crypto_secretstream_xchacha20poly1305_COUNTERBYTES])
// TODO
//if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
//sodium_is_zero(STATE_COUNTER(state),
//crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
//crypto_secretstream_xchacha20poly1305_rekey(state);
//}
//if (mlen_p != NULL) {
//*mlen_p = mlen;
//}
//if (tag_p != NULL) {
//*tag_p = tag;
//}
//return 0;
return m, tag, nil
}
func memzero(b []byte) {
for i := range b {
b[i] = 0
}
}
func xor_buf(out, in []byte) {
for i := range out {
out[i] ^= in[i]
}
}
func buf_inc(n []byte) {
c := 1
for i := range n {
c += int(n[i])
n[i] = byte(c)
c >>= 8
}
}