Merge branch 'encdec'

This commit is contained in:
Sudhi Herle 2019-10-22 20:12:51 -07:00
commit b14f9d1e53
7 changed files with 247 additions and 11 deletions

View file

@ -89,7 +89,7 @@ e.g., to verify the signature of *archive.tar.gz* against
If the sender wishes to prove to the recipient that they encrypted If the sender wishes to prove to the recipient that they encrypted
a file: a file:
sigtool encrypt -s sender.key to.pub -o archive.tar.gz.enc archive.tar.gz sigtool encrypt -s sender.key to.pub -o archive.tar.gz.enc archive.tar.gz
This will create an encrypted file *archive.tar.gz.enc* such that the This will create an encrypted file *archive.tar.gz.enc* such that the
@ -101,7 +101,7 @@ who they expect.
If the receiver has the public key of the sender, they can verify that If the receiver has the public key of the sender, they can verify that
they indeed sent the file by cryptographically checking the output: they indeed sent the file by cryptographically checking the output:
sigtool decrypt -o archive.tar.gz -v sender.pub to.key archive.tar.gz.enc sigtool decrypt -o archive.tar.gz -v sender.pub to.key archive.tar.gz.enc
Note that the verification is optional and if the `-v` option is not Note that the verification is optional and if the `-v` option is not
used, then decryption will proceed without verifying the sender. used, then decryption will proceed without verifying the sender.
@ -110,7 +110,7 @@ used, then decryption will proceed without verifying the sender.
`sigtool` can generate ephemeral keys for encrypting a file such that `sigtool` can generate ephemeral keys for encrypting a file such that
the receiver doesn't need to authenticate the sender: the receiver doesn't need to authenticate the sender:
sigtool encrypt to.pub -o archive.tar.gz.enc archive.tar.gz sigtool encrypt to.pub -o archive.tar.gz.enc archive.tar.gz
This will create an encrypted file *archive.tar.gz.enc* such that the This will create an encrypted file *archive.tar.gz.enc* such that the
recipient in possession of *to.key* can decrypt it. recipient in possession of *to.key* can decrypt it.

View file

@ -35,17 +35,24 @@ func encrypt(args []string) {
var keyfile string var keyfile string
var envpw string var envpw string
var pw bool var pw bool
var sblksize string
fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`") fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`")
fs.StringVarP(&keyfile, "sign", "s", "", "Sign using private key `S`") fs.StringVarP(&keyfile, "sign", "s", "", "Sign using private key `S`")
fs.BoolVarP(&pw, "password", "p", false, "Ask for passphrase to decrypt the private key") fs.BoolVarP(&pw, "password", "p", false, "Ask for passphrase to decrypt the private key")
fs.StringVarP(&envpw, "env-password", "", "", "Use passphrase from environment variable `E`") fs.StringVarP(&envpw, "env-password", "", "", "Use passphrase from environment variable `E`")
fs.StringVarP(&sblksize, "block-size", "B", "4M", "Use `S` as the encryption block size")
err := fs.Parse(args) err := fs.Parse(args)
if err != nil { if err != nil {
die("%s", err) die("%s", err)
} }
blksize, err := utils.ParseSize(sblksize)
if err != nil {
die("%s", err)
}
var pws, infile string var pws, infile string
if len(envpw) > 0 { if len(envpw) > 0 {
@ -108,7 +115,7 @@ func encrypt(args []string) {
outfd = outf outfd = outf
} }
en, err := sign.NewEncryptor(sk) en, err := sign.NewEncryptor(sk, blksize)
if err != nil { if err != nil {
die("%s", err) die("%s", err)
} }

2
go.mod
View file

@ -4,7 +4,7 @@ go 1.13
require ( require (
github.com/gogo/protobuf v1.3.1 github.com/gogo/protobuf v1.3.1
github.com/opencoff/go-utils v0.3.0 github.com/opencoff/go-utils v0.4.0
github.com/opencoff/pflag v0.3.3 github.com/opencoff/pflag v0.3.3
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc
gopkg.in/yaml.v2 v2.2.4 gopkg.in/yaml.v2 v2.2.4

2
go.sum
View file

@ -4,6 +4,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/opencoff/go-utils v0.3.0 h1:/TQXjf50o3GSB9MItog5L8Gf4GWJ4B5+rmqjB4g2RZQ= github.com/opencoff/go-utils v0.3.0 h1:/TQXjf50o3GSB9MItog5L8Gf4GWJ4B5+rmqjB4g2RZQ=
github.com/opencoff/go-utils v0.3.0/go.mod h1:c+7QUAiCCHcNH6OGvsZ0fviG7cgse8Y3ucg+xy7sGXM= github.com/opencoff/go-utils v0.3.0/go.mod h1:c+7QUAiCCHcNH6OGvsZ0fviG7cgse8Y3ucg+xy7sGXM=
github.com/opencoff/go-utils v0.4.0 h1:pu08Om//u2+YGvLkHa2CyL6eI+/1J0bXih1Z6nuITp8=
github.com/opencoff/go-utils v0.4.0/go.mod h1:c+7QUAiCCHcNH6OGvsZ0fviG7cgse8Y3ucg+xy7sGXM=
github.com/opencoff/pflag v0.3.3 h1:yohZkwYGPkB34WXvUQzU5GyLhImnjfePDARUaE8me3U= github.com/opencoff/pflag v0.3.3 h1:yohZkwYGPkB34WXvUQzU5GyLhImnjfePDARUaE8me3U=
github.com/opencoff/pflag v0.3.3/go.mod h1:mTLzGGUGda1Av3d34iAJlh0JIlRxmFZtmc6qoWPspK0= github.com/opencoff/pflag v0.3.3/go.mod h1:mTLzGGUGda1Av3d34iAJlh0JIlRxmFZtmc6qoWPspK0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

218
sign/crypt_test.go Normal file
View file

@ -0,0 +1,218 @@
// crypt_test.go -- Test harness for encrypt/decrypt bits
//
// (c) 2016 Sudhi Herle <sudhi@herle.net>
//
// Licensing Terms: GPLv2
//
// If you need a commercial license for this work, please contact
// the author.
//
// This software does not come with any express or implied
// warranty; it is provided "as is". No claim is made to its
// suitability for any purpose.
package sign
import (
"bytes"
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"testing"
)
// one sender, one receiver no verification of sender
func TestSimple(t *testing.T) {
assert := newAsserter(t)
receiver, err := NewKeypair()
assert(err == nil, "receiver keypair gen failed: %s", err)
// cleartext
buf := make([]byte, 64*1024)
for i := 0; i < len(buf); i++ {
buf[i] = byte(i & 0xff)
}
ee, err := NewEncryptor(nil, 4096)
assert(err == nil, "encryptor create fail: %s", err)
err = ee.AddRecipient(&receiver.Pub)
assert(err == nil, "can't add recipient: %s", err)
rd := bytes.NewBuffer(buf)
wr := bytes.Buffer{}
err = ee.Encrypt(rd, &wr)
assert(err == nil, "encrypt fail: %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)
wr = bytes.Buffer{}
err = dd.Decrypt(&wr)
assert(err == nil, "decrypt fail: %s", err)
b := wr.Bytes()
assert(len(b) == len(buf), "decrypt length mismatch: exp %d, saw %d", len(buf), len(b))
assert(byteEq(b, buf), "decrypt content mismatch")
}
// test corrupted header or corrupted input
func TestCorrupted(t *testing.T) {
assert := newAsserter(t)
receiver, err := NewKeypair()
assert(err == nil, "receiver keypair gen failed: %s", err)
// cleartext
buf := make([]byte, 64*1024)
for i := 0; i < len(buf); i++ {
buf[i] = byte(i & 0xff)
}
ee, err := NewEncryptor(nil, 4096)
assert(err == nil, "encryptor create fail: %s", err)
err = ee.AddRecipient(&receiver.Pub)
assert(err == nil, "can't add recipient: %s", err)
rd := bytes.NewBuffer(buf)
wr := bytes.Buffer{}
err = ee.Encrypt(rd, &wr)
assert(err == nil, "encrypt fail: %s", err)
rb := wr.Bytes()
n := len(rb)
for i := 0; i < n; i++ {
j := randint() % n
rb[j] = byte(randint() & 0xff)
}
rd = bytes.NewBuffer(rb)
dd, err := NewDecryptor(rd)
assert(err != nil, "decryptor works on bad input")
assert(dd == nil, "decryptor not nil for bad input")
}
// one sender, one receiver with verification of sender
func TestSenderVerified(t *testing.T) {
assert := newAsserter(t)
sender, err := NewKeypair()
assert(err == nil, "sender keypair gen failed: %s", err)
receiver, err := NewKeypair()
assert(err == nil, "receiver keypair gen failed: %s", err)
// cleartext
buf := make([]byte, 64*1024)
for i := 0; i < len(buf); i++ {
buf[i] = byte(i & 0xff)
}
ee, err := NewEncryptor(&sender.Sec, 4096)
assert(err == nil, "encryptor create fail: %s", err)
err = ee.AddRecipient(&receiver.Pub)
assert(err == nil, "can't add recipient: %s", err)
rd := bytes.NewBuffer(buf)
wr := bytes.Buffer{}
err = ee.Encrypt(rd, &wr)
assert(err == nil, "encrypt fail: %s", err)
rd = bytes.NewBuffer(wr.Bytes())
dd, err := NewDecryptor(rd)
assert(err == nil, "decryptor create fail: %s", err)
err = dd.SetPrivateKey(&receiver.Sec, &sender.Pub)
assert(err == nil, "decryptor can't add SK: %s", err)
wr = bytes.Buffer{}
err = dd.Decrypt(&wr)
assert(err == nil, "decrypt fail: %s", err)
b := wr.Bytes()
assert(len(b) == len(buf), "decrypt length mismatch: exp %d, saw %d", len(buf), len(b))
assert(byteEq(b, buf), "decrypt content mismatch")
}
// one sender, multiple receivers, each decrypting the blob
func TestMultiReceiver(t *testing.T) {
assert := newAsserter(t)
sender, err := NewKeypair()
assert(err == nil, "sender keypair gen failed: %s", err)
// cleartext
buf := make([]byte, 64*1024)
for i := 0; i < len(buf); i++ {
buf[i] = byte(i & 0xff)
}
ee, err := NewEncryptor(&sender.Sec, 4096)
assert(err == nil, "encryptor create fail: %s", err)
n := 4
rx := make([]*Keypair, n)
for i := 0; i < n; i++ {
r, err := NewKeypair()
assert(err == nil, "can't make receiver key %d: %s", i, err)
rx[i] = r
err = ee.AddRecipient(&r.Pub)
assert(err == nil, "can't add recipient %d: %s", i, err)
}
rd := bytes.NewBuffer(buf)
wr := bytes.Buffer{}
err = ee.Encrypt(rd, &wr)
assert(err == nil, "encrypt fail: %s", err)
encBytes := wr.Bytes()
for i := 0; i < n; i++ {
rd = bytes.NewBuffer(encBytes)
dd, err := NewDecryptor(rd)
assert(err == nil, "decryptor %d create fail: %s", i, err)
err = dd.SetPrivateKey(&rx[i].Sec, &sender.Pub)
assert(err == nil, "decryptor can't add SK %d: %s", i, err)
wr = bytes.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)
}
}
func randint() int {
var b [4]byte
_, err := io.ReadFull(rand.Reader, b[:])
if err != nil {
panic(fmt.Sprintf("can't read 4 rand bytes: %s", err))
}
u := binary.BigEndian.Uint32(b[:])
return int(u & 0x7fffffff)
}

View file

@ -1,4 +1,4 @@
// cipher.go -- Ed25519 based encrypt/decrypt // encrypt.go -- Ed25519 based encrypt/decrypt
// //
// (c) 2016 Sudhi Herle <sudhi@herle.net> // (c) 2016 Sudhi Herle <sudhi@herle.net>
// //
@ -31,6 +31,7 @@ import (
// Encryption chunk size = 4MB // Encryption chunk size = 4MB
const chunkSize int = 4 * 1048576 const chunkSize int = 4 * 1048576
const maxChunkSize int = 16 * 1048576
const _Magic = "SigTool" const _Magic = "SigTool"
const _MagicLen = len(_Magic) const _MagicLen = len(_Magic)
@ -51,11 +52,19 @@ type Encryptor struct {
// Create a new Encryption context and use the optional private key 'sk' for // Create a new Encryption context and use the optional private key 'sk' for
// signing any recipient keys. If 'sk' is nil, then ephmeral Curve25519 keys // signing any recipient keys. If 'sk' is nil, then ephmeral Curve25519 keys
// are generated and used with recipient's public key. // are generated and used with recipient's public key.
func NewEncryptor(sk *PrivateKey) (*Encryptor, error) { func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
if blksize >= uint64(maxChunkSize) {
return nil, fmt.Errorf("encrypt: Blocksize is too large (max 16M)")
}
if blksize == 0 {
blksize = uint64(chunkSize)
}
e := &Encryptor{ e := &Encryptor{
Header: Header{ Header: Header{
ChunkSize: uint32(chunkSize), ChunkSize: uint32(blksize),
Salt: make([]byte, _AEADNonceLen), Salt: make([]byte, _AEADNonceLen),
}, },
@ -282,7 +291,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 > (16*1048576) { if d.ChunkSize == 0 || d.ChunkSize >= uint32(maxChunkSize) {
return nil, fmt.Errorf("decrypt: invalid chunkSize %d", d.ChunkSize) return nil, fmt.Errorf("decrypt: invalid chunkSize %d", d.ChunkSize)
} }
@ -397,7 +406,7 @@ func (d *Decryptor) decrypt(i int) ([]byte, error) {
chunklen := int(binary.BigEndian.Uint32(b[:4])) chunklen := int(binary.BigEndian.Uint32(b[:4]))
// Sanity check - in case of corrupt header // Sanity check - in case of corrupt header
if chunklen > (d.ae.Overhead()+chunkSize) { if chunklen > (d.ae.Overhead() + chunkSize) {
return nil, fmt.Errorf("decrypt: chunksize is too large (%d)", chunklen) return nil, fmt.Errorf("decrypt: chunksize is too large (%d)", chunklen)
} }

View file

@ -1 +1 @@
0.2.1 0.3.1