diff --git a/sign/encrypt.go b/sign/encrypt.go index d8e6a71..9831b86 100644 --- a/sign/encrypt.go +++ b/sign/encrypt.go @@ -260,7 +260,7 @@ func (e *Encryptor) start(wr io.Writer) error { } // encrypt exactly _one_ block of data -// The nonce for the block is: sha256(salt || chunkLen || block#) +// The nonce is constructed from the salt, block# and block-size. // This protects the output stream from re-ordering attacks and length // modification attacks. The encoded length & block number is used as // additional data in the AEAD construction. @@ -473,7 +473,6 @@ func (d *Decryptor) Decrypt(wr io.Writer) error { return nil } } - return nil } // Decrypt exactly one chunk of data @@ -490,7 +489,6 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) { m := binary.BigEndian.Uint32(b[:4]) eof := (m & _EOF) > 0 - m &= (_EOF - 1) // Sanity check - in case of corrupt header @@ -504,9 +502,6 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) { } return p, eof, nil - case m < ovh: - return nil, false, fmt.Errorf("decrypt: chunksize is too small (%d)", m) - default: } diff --git a/sign/encrypt_test.go b/sign/encrypt_test.go index 4283e10..e3ad9cb 100644 --- a/sign/encrypt_test.go +++ b/sign/encrypt_test.go @@ -76,6 +76,57 @@ func TestEncryptSimple(t *testing.T) { assert(byteEq(b, buf), "decrypt content mismatch") } +// one sender, one receiver - small blocks +func TestEncryptSmallSizes(t *testing.T) { + assert := newAsserter(t) + + receiver, err := NewKeypair() + assert(err == nil, "receiver keypair gen failed: %s", err) + + var blkSize int = 8 + var size int = (blkSize * 4) + + // cleartext + bigbuf := make([]byte, size) + for i := 0; i < len(bigbuf); i++ { + bigbuf[i] = byte(i & 0xff) + } + + // encrypt progressively larger bufs + for i := 1; i < len(bigbuf); i++ { + buf := bigbuf[:i] + + ee, err := NewEncryptor(nil, uint64(blkSize)) + assert(err == nil, "encryptor-%d create fail: %s", i, err) + + err = ee.AddRecipient(&receiver.Pub) + assert(err == nil, "encryptor-%d: can't add recipient: %s", i, err) + + rd := bytes.NewBuffer(buf) + wr := Buffer{} + + err = ee.Encrypt(rd, &wr) + assert(err == nil, "encrypt-%d fail: %s", i, err) + + rd = bytes.NewBuffer(wr.Bytes()) + + dd, err := NewDecryptor(rd) + assert(err == nil, "decryptor-%d create fail: %s", i, err) + + err = dd.SetPrivateKey(&receiver.Sec, nil) + assert(err == nil, "decryptor-%d can't add SK: %s", i, err) + + wr = Buffer{} + err = dd.Decrypt(&wr) + assert(err == nil, "decrypt-%d fail: %s", i, err) + + b := wr.Bytes() + assert(len(b) == len(buf), "decrypt-%d length mismatch: exp %d, saw %d", i, len(buf), len(b)) + + assert(byteEq(b, buf), "decrypt-%d content mismatch", i) + } +} + // test corrupted header or corrupted input func TestEncryptCorrupted(t *testing.T) { assert := newAsserter(t) @@ -313,6 +364,93 @@ func TestStreamIO(t *testing.T) { } +// Test stream write and read with small sizes +func TestSmallSizeStreamIO(t *testing.T) { + assert := newAsserter(t) + + receiver, err := NewKeypair() + assert(err == nil, "receiver keypair gen failed: %s", err) + + var blkSize int = 8 + var size int = blkSize * 10 + + // cleartext + bigbuf := make([]byte, size) + for i := 0; i < len(bigbuf); i++ { + bigbuf[i] = byte(i & 0xff) + } + + for i := 1; i < len(bigbuf); i++ { + buf := bigbuf[:i] + t.Logf("small-size-stream: size %d, chunksize %d\n", i, blkSize) + + ee, err := NewEncryptor(nil, uint64(blkSize)) + assert(err == nil, "encryptor create fail: %s", err) + + err = ee.AddRecipient(&receiver.Pub) + assert(err == nil, "can't add recipient: %s", err) + + wr := Buffer{} + wio, err := ee.NewStreamWriter(&wr) + assert(err == nil, "can't start stream writer: %s", err) + + // chunksize for writing to stream + csize := blkSize - 1 + rbuf := buf + for len(rbuf) > 0 { + m := csize + if len(rbuf) < m { + m = len(rbuf) + } + + n, err := wio.Write(rbuf[:m]) + assert(err == nil, "stream write failed: %s", err) + assert(n == m, "stream write mismatch: exp %d, saw %d", m, n) + + rbuf = rbuf[m:] + } + err = wio.Close() + assert(err == nil, "stream close failed: %s", err) + + _, err = wio.Write(buf[:csize]) + assert(err != nil, "stream write accepted I/O after close: %s", err) + + rd := bytes.NewBuffer(wr.Bytes()) + + dd, err := NewDecryptor(rd) + assert(err == nil, "decryptor create fail: %s", err) + + err = dd.SetPrivateKey(&receiver.Sec, nil) + assert(err == nil, "decryptor can't add SK: %s", err) + + rio, err := dd.NewStreamReader() + assert(err == nil, "stream reader failed: %s", err) + + rbuf = make([]byte, csize) + wr = Buffer{} + n := 0 + for { + m, err := rio.Read(rbuf) + assert(err == nil || err == io.EOF, "streamread fail: %s", err) + + if m > 0 { + wr.Write(rbuf[:m]) + n += m + } + if err == io.EOF || m == 0 { + break + } + } + + b := wr.Bytes() + assert(n == len(b), "streamread: bad buflen; exp %d, saw %d", n, len(b)) + assert(n == len(buf), "streamread: decrypt len mismatch; exp %d, saw %d", len(buf), n) + + assert(byteEq(b, buf), "decrypt content mismatch") + } + +} + func randint() int { var b [4]byte diff --git a/sign/ssh.go b/sign/ssh.go index 418622a..5e59ac6 100644 --- a/sign/ssh.go +++ b/sign/ssh.go @@ -57,8 +57,8 @@ func parseSSHPrivateKey(data []byte, getpw func() ([]byte, error)) (*PrivateKey, } func parseSSHPublicKey(in []byte) (*PublicKey, error) { - splitter := regexp.MustCompile("[ \\t]+"); - v := splitter.Split(string(in), -1); + splitter := regexp.MustCompile("[ \\t]+") + v := splitter.Split(string(in), -1) if len(v) != 3 { return nil, ErrBadPublicKey } diff --git a/sign/stream.go b/sign/stream.go index 3304b22..6f02d18 100644 --- a/sign/stream.go +++ b/sign/stream.go @@ -29,6 +29,8 @@ type encWriter struct { err error } +var _ io.WriteCloser = &encWriter{} + // NewStreamWriter begins stream encryption to an underlying destination writer 'wr'. // It returns an io.WriteCloser. func (e *Encryptor) NewStreamWriter(wr io.WriteCloser) (io.WriteCloser, error) { @@ -108,6 +110,8 @@ type encReader struct { blk uint32 } +var _ io.Reader = &encReader{} + // NewStreamReader returns an io.Reader to read from the decrypted stream func (d *Decryptor) NewStreamReader() (io.Reader, error) { if d.key == nil {