Teach sigtool to mark the last block (eof).
This commit is contained in:
parent
f82c1336ac
commit
a9c17988c4
3 changed files with 88 additions and 59 deletions
|
@ -30,8 +30,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encryption chunk size = 4MB
|
// Encryption chunk size = 4MB
|
||||||
const chunkSize int = 4 * 1048576
|
const chunkSize uint32 = 4 * 1048576
|
||||||
const maxChunkSize int = 16 * 1048576
|
const maxChunkSize uint32 = 16 * 1048576
|
||||||
|
const EOF uint32 = 1 << 31
|
||||||
|
|
||||||
const _Magic = "SigTool"
|
const _Magic = "SigTool"
|
||||||
const _MagicLen = len(_Magic)
|
const _MagicLen = len(_Magic)
|
||||||
|
@ -58,13 +59,14 @@ func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
|
||||||
return nil, fmt.Errorf("encrypt: Blocksize is too large (max 16M)")
|
return nil, fmt.Errorf("encrypt: Blocksize is too large (max 16M)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if blksize == 0 {
|
blksz := uint32(blksize)
|
||||||
blksize = uint64(chunkSize)
|
if blksz == 0 {
|
||||||
|
blksz = chunkSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e := &Encryptor{
|
e := &Encryptor{
|
||||||
Header: Header{
|
Header: Header{
|
||||||
ChunkSize: uint32(blksize),
|
ChunkSize: blksz,
|
||||||
Salt: make([]byte, _AEADNonceLen),
|
Salt: make([]byte, _AEADNonceLen),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
|
||||||
return nil, fmt.Errorf("encrypt: %s", err)
|
return nil, fmt.Errorf("encrypt: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.buf = make([]byte, chunkSize+4+e.ae.Overhead())
|
e.buf = make([]byte, blksz+4+uint32(e.ae.Overhead()))
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,15 +178,14 @@ func (e *Encryptor) Encrypt(rd io.Reader, wr io.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, e.ChunkSize)
|
buf := make([]byte, e.ChunkSize)
|
||||||
i := 0
|
|
||||||
|
|
||||||
for {
|
var i uint32
|
||||||
|
var eof bool
|
||||||
|
for !eof {
|
||||||
n, err := io.ReadAtLeast(rd, buf, int(e.ChunkSize))
|
n, err := io.ReadAtLeast(rd, buf, int(e.ChunkSize))
|
||||||
if n == 0 {
|
eof = err == io.EOF || err == io.ErrClosedPipe
|
||||||
return nil
|
if n >= 0 {
|
||||||
}
|
err = e.encrypt(buf[:n], wr, i, eof)
|
||||||
if n > 0 {
|
|
||||||
err = e.encrypt(buf[:n], wr, i)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -193,10 +194,11 @@ func (e *Encryptor) Encrypt(rd io.Reader, wr io.Writer) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF && err != io.ErrClosedPipe {
|
||||||
return fmt.Errorf("encrypt: I/O read error: %s", err)
|
return fmt.Errorf("encrypt: I/O read error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt exactly _one_ block of data
|
// encrypt exactly _one_ block of data
|
||||||
|
@ -204,12 +206,18 @@ func (e *Encryptor) Encrypt(rd io.Reader, wr io.Writer) error {
|
||||||
// This protects the output stream from re-ordering attacks and length
|
// This protects the output stream from re-ordering attacks and length
|
||||||
// modification attacks. The encoded length & block number is used as
|
// modification attacks. The encoded length & block number is used as
|
||||||
// additional data in the AEAD construction.
|
// additional data in the AEAD construction.
|
||||||
func (e *Encryptor) encrypt(buf []byte, wr io.Writer, i int) error {
|
func (e *Encryptor) encrypt(buf []byte, wr io.Writer, i uint32, eof bool) error {
|
||||||
var b [8]byte
|
var b [8]byte
|
||||||
var noncebuf [32]byte
|
var noncebuf [32]byte
|
||||||
|
var z uint32 = uint32(e.ae.Overhead() + len(buf))
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(b[:4], uint32(e.ae.Overhead()+len(buf)))
|
// mark last block
|
||||||
binary.BigEndian.PutUint32(b[4:], uint32(i))
|
if eof {
|
||||||
|
z |= EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(b[:4], z)
|
||||||
|
binary.BigEndian.PutUint32(b[4:], i)
|
||||||
|
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(e.Salt)
|
h.Write(e.Salt)
|
||||||
|
@ -248,7 +256,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
|
||||||
|
|
||||||
_, err := io.ReadFull(rd, b[:])
|
_, err := io.ReadFull(rd, b[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("decrypt: err while reading header: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Compare(b[:_MagicLen], []byte(_Magic)) != 0 {
|
if bytes.Compare(b[:_MagicLen], []byte(_Magic)) != 0 {
|
||||||
|
@ -271,7 +279,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
|
||||||
|
|
||||||
_, err = io.ReadFull(rd, hdr)
|
_, err = io.ReadFull(rd, hdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("decrypt: err while reading header: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
verify := hdr[:32]
|
verify := hdr[:32]
|
||||||
|
@ -291,7 +299,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
|
||||||
return nil, fmt.Errorf("decrypt: decode error: %s", err)
|
return nil, fmt.Errorf("decrypt: decode error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.ChunkSize == 0 || d.ChunkSize >= uint32(maxChunkSize) {
|
if d.ChunkSize == 0 || d.ChunkSize >= maxChunkSize {
|
||||||
return nil, fmt.Errorf("decrypt: invalid chunkSize %d", d.ChunkSize)
|
return nil, fmt.Errorf("decrypt: invalid chunkSize %d", d.ChunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,12 +378,13 @@ func (d *Decryptor) Decrypt(wr io.Writer) error {
|
||||||
return fmt.Errorf("decrypt: wrapped-key not decrypted (missing SetPrivateKey()?")
|
return fmt.Errorf("decrypt: wrapped-key not decrypted (missing SetPrivateKey()?")
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; ; i++ {
|
var i uint32
|
||||||
c, err := d.decrypt(i)
|
for i = 0; ; i++ {
|
||||||
|
c, eof, err := d.decrypt(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(c) == 0 {
|
if eof || len(c) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,46 +399,53 @@ func (d *Decryptor) Decrypt(wr io.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt exactly one chunk of data
|
// Decrypt exactly one chunk of data
|
||||||
func (d *Decryptor) decrypt(i int) ([]byte, error) {
|
func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
|
||||||
var b [8]byte
|
var b [8]byte
|
||||||
var nonceb [32]byte
|
var nonceb [32]byte
|
||||||
|
|
||||||
n, err := io.ReadFull(d.rd, b[:4])
|
n, err := io.ReadFull(d.rd, b[:4])
|
||||||
if n == 0 || err == io.EOF {
|
if err == io.EOF || err == io.ErrClosedPipe || n == 0 {
|
||||||
return nil, nil
|
return nil, false, fmt.Errorf("decrypt: premature EOF-1 while reading block %d", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("decrypt: can't read chunk %d length: %s", i, err)
|
return nil, false, fmt.Errorf("decrypt: can't read chunk %d length: %s", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
chunklen := int(binary.BigEndian.Uint32(b[:4]))
|
m := binary.BigEndian.Uint32(b[:4])
|
||||||
|
eof := (m & EOF) > 0
|
||||||
|
|
||||||
|
m &= (EOF-1)
|
||||||
|
|
||||||
// Sanity check - in case of corrupt header
|
// Sanity check - in case of corrupt header
|
||||||
if chunklen > (d.ae.Overhead() + chunkSize) {
|
if m > (uint32(d.ae.Overhead()) + chunkSize) {
|
||||||
return nil, fmt.Errorf("decrypt: chunksize is too large (%d)", chunklen)
|
return nil, false, fmt.Errorf("decrypt: chunksize is too large (%d)", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(b[4:], uint32(i))
|
binary.BigEndian.PutUint32(b[4:], i)
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(d.Salt)
|
h.Write(d.Salt)
|
||||||
h.Write(b[:])
|
h.Write(b[:])
|
||||||
nonce := h.Sum(nonceb[:0])
|
nonce := h.Sum(nonceb[:0])
|
||||||
|
|
||||||
n, err = io.ReadFull(d.rd, d.buf[:chunklen])
|
var p []byte
|
||||||
if n == 0 || err == io.EOF {
|
if m > 0 {
|
||||||
return nil, nil
|
n, err = io.ReadFull(d.rd, d.buf[:m])
|
||||||
}
|
if err != nil {
|
||||||
if err != nil {
|
return nil, false, fmt.Errorf("decrypt: premature EOF-2 while reading block %d: %s", i, err)
|
||||||
return nil, fmt.Errorf("decrypt: can't read chunk %d: %s", i, err)
|
}
|
||||||
|
|
||||||
|
p, err = d.ae.Open(d.buf[:0], nonce, d.buf[:n], b[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("decrypt: can't decrypt chunk %d: %s", i, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := d.ae.Open(d.buf[:0], nonce, d.buf[:chunklen], b[:])
|
if eof && len(p) != 0 {
|
||||||
if err != nil {
|
return nil, false, fmt.Errorf("decrypt: EOF set on blk %d of len %d", i, m)
|
||||||
return nil, fmt.Errorf("decrypt: can't decrypt chunk %d: %s", i, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, nil
|
return p, eof, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap a shared key with the recipient's public key 'pk' by generating an ephemeral
|
// Wrap a shared key with the recipient's public key 'pk' by generating an ephemeral
|
||||||
|
|
|
@ -64,6 +64,18 @@ func tempdir(t *testing.T) string {
|
||||||
return tmp
|
return tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return a hardcoded password
|
||||||
|
func hardcodedPw() ([]byte, error) {
|
||||||
|
return []byte("abc"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrongPw() ([]byte, error) {
|
||||||
|
return []byte("xyz"), nil
|
||||||
|
}
|
||||||
|
func emptyPw() ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Return true if file exists, false otherwise
|
// Return true if file exists, false otherwise
|
||||||
func fileExists(fn string) bool {
|
func fileExists(fn string) bool {
|
||||||
st, err := os.Stat(fn)
|
st, err := os.Stat(fn)
|
||||||
|
@ -100,7 +112,7 @@ func Test0(t *testing.T) {
|
||||||
dn := tempdir(t)
|
dn := tempdir(t)
|
||||||
bn := fmt.Sprintf("%s/t0", dn)
|
bn := fmt.Sprintf("%s/t0", dn)
|
||||||
|
|
||||||
err = kp.Serialize(bn, "", "abc")
|
err = kp.Serialize(bn, "", hardcodedPw)
|
||||||
assert(err == nil, "keyPair.Serialize() fail")
|
assert(err == nil, "keyPair.Serialize() fail")
|
||||||
|
|
||||||
pkf := fmt.Sprintf("%s.pub", bn)
|
pkf := fmt.Sprintf("%s.pub", bn)
|
||||||
|
@ -110,32 +122,28 @@ func Test0(t *testing.T) {
|
||||||
assert(fileExists(pkf), "missing pkf")
|
assert(fileExists(pkf), "missing pkf")
|
||||||
assert(fileExists(skf), "missing skf")
|
assert(fileExists(skf), "missing skf")
|
||||||
|
|
||||||
// send wrong file and see what happens
|
pk, err := ReadPublicKey(pkf)
|
||||||
pk, err := ReadPublicKey(skf)
|
|
||||||
assert(err != nil, "bad PK ReadPK fail")
|
|
||||||
|
|
||||||
pk, err = ReadPublicKey(pkf)
|
|
||||||
assert(err == nil, "ReadPK() fail")
|
assert(err == nil, "ReadPK() fail")
|
||||||
|
|
||||||
// -ditto- for Sk
|
// -ditto- for Sk
|
||||||
sk, err := ReadPrivateKey(pkf, "")
|
sk, err := ReadPrivateKey(pkf, emptyPw)
|
||||||
assert(err != nil, "bad SK ReadSK fail")
|
assert(err != nil, "bad SK ReadSK fail: %s", err)
|
||||||
|
|
||||||
sk, err = ReadPrivateKey(skf, "")
|
sk, err = ReadPrivateKey(skf, emptyPw)
|
||||||
assert(err != nil, "ReadSK() empty pw fail")
|
assert(err != nil, "ReadSK() empty pw fail: ks", err)
|
||||||
|
|
||||||
sk, err = ReadPrivateKey(skf, "abcdef")
|
sk, err = ReadPrivateKey(skf, wrongPw)
|
||||||
assert(err != nil, "ReadSK() wrong pw fail")
|
assert(err != nil, "ReadSK() wrong pw fail: %s", err)
|
||||||
|
|
||||||
badf := fmt.Sprintf("%s/badf.key", dn)
|
badf := fmt.Sprintf("%s/badf.key", dn)
|
||||||
err = ioutil.WriteFile(badf, []byte(badsk), 0600)
|
err = ioutil.WriteFile(badf, []byte(badsk), 0600)
|
||||||
assert(err == nil, "write badsk")
|
assert(err == nil, "write badsk")
|
||||||
|
|
||||||
sk, err = ReadPrivateKey(badf, "abc")
|
sk, err = ReadPrivateKey(badf, hardcodedPw)
|
||||||
assert(err != nil, "badsk read fail")
|
assert(err != nil, "badsk read fail: %s", err)
|
||||||
|
|
||||||
// Finally, with correct password it should work.
|
// Finally, with correct password it should work.
|
||||||
sk, err = ReadPrivateKey(skf, "abc")
|
sk, err = ReadPrivateKey(skf, hardcodedPw)
|
||||||
assert(err == nil, "ReadSK() correct pw fail")
|
assert(err == nil, "ReadSK() correct pw fail")
|
||||||
|
|
||||||
// And, deserialized keys should be identical
|
// And, deserialized keys should be identical
|
||||||
|
@ -186,11 +194,11 @@ func Test1(t *testing.T) {
|
||||||
pkf := fmt.Sprintf("%s.pub", bn)
|
pkf := fmt.Sprintf("%s.pub", bn)
|
||||||
skf := fmt.Sprintf("%s.key", bn)
|
skf := fmt.Sprintf("%s.key", bn)
|
||||||
|
|
||||||
err = kp.Serialize(bn, "", "")
|
err = kp.Serialize(bn, "", emptyPw)
|
||||||
assert(err == nil, "keyPair.Serialize() fail")
|
assert(err == nil, "keyPair.Serialize() fail")
|
||||||
|
|
||||||
// Now read the private key and sign
|
// Now read the private key and sign
|
||||||
sk, err = ReadPrivateKey(skf, "")
|
sk, err = ReadPrivateKey(skf, emptyPw)
|
||||||
assert(err == nil, "readSK fail")
|
assert(err == nil, "readSK fail")
|
||||||
|
|
||||||
pk, err = ReadPublicKey(pkf)
|
pk, err = ReadPublicKey(pkf)
|
||||||
|
@ -262,6 +270,11 @@ func Benchmark_Sig(b *testing.B) {
|
||||||
16,
|
16,
|
||||||
32,
|
32,
|
||||||
64,
|
64,
|
||||||
|
1024,
|
||||||
|
4096,
|
||||||
|
256*1024,
|
||||||
|
1048576,
|
||||||
|
4 * 1048576,
|
||||||
}
|
}
|
||||||
|
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
||||||
0.4.0
|
0.5.0
|
||||||
|
|
Loading…
Add table
Reference in a new issue