Merge branch 'encdec'
This commit is contained in:
commit
b14f9d1e53
7 changed files with 247 additions and 11 deletions
|
@ -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
|
||||
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
|
||||
|
@ -101,7 +101,7 @@ who they expect.
|
|||
If the receiver has the public key of the sender, they can verify that
|
||||
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
|
||||
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
|
||||
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
|
||||
recipient in possession of *to.key* can decrypt it.
|
||||
|
|
9
crypt.go
9
crypt.go
|
@ -35,17 +35,24 @@ func encrypt(args []string) {
|
|||
var keyfile string
|
||||
var envpw string
|
||||
var pw bool
|
||||
var sblksize string
|
||||
|
||||
fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`")
|
||||
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.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)
|
||||
if err != nil {
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
blksize, err := utils.ParseSize(sblksize)
|
||||
if err != nil {
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
var pws, infile string
|
||||
|
||||
if len(envpw) > 0 {
|
||||
|
@ -108,7 +115,7 @@ func encrypt(args []string) {
|
|||
outfd = outf
|
||||
}
|
||||
|
||||
en, err := sign.NewEncryptor(sk)
|
||||
en, err := sign.NewEncryptor(sk, blksize)
|
||||
if err != nil {
|
||||
die("%s", err)
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -4,7 +4,7 @@ go 1.13
|
|||
|
||||
require (
|
||||
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
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
|
|
2
go.sum
2
go.sum
|
@ -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/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.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/go.mod h1:mTLzGGUGda1Av3d34iAJlh0JIlRxmFZtmc6qoWPspK0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
|
218
sign/crypt_test.go
Normal file
218
sign/crypt_test.go
Normal 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)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// cipher.go -- Ed25519 based encrypt/decrypt
|
||||
// encrypt.go -- Ed25519 based encrypt/decrypt
|
||||
//
|
||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
||||
//
|
||||
|
@ -31,6 +31,7 @@ import (
|
|||
|
||||
// Encryption chunk size = 4MB
|
||||
const chunkSize int = 4 * 1048576
|
||||
const maxChunkSize int = 16 * 1048576
|
||||
|
||||
const _Magic = "SigTool"
|
||||
const _MagicLen = len(_Magic)
|
||||
|
@ -51,11 +52,19 @@ type Encryptor struct {
|
|||
// 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
|
||||
// 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{
|
||||
Header: Header{
|
||||
ChunkSize: uint32(chunkSize),
|
||||
ChunkSize: uint32(blksize),
|
||||
Salt: make([]byte, _AEADNonceLen),
|
||||
},
|
||||
|
||||
|
@ -282,7 +291,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -397,7 +406,7 @@ func (d *Decryptor) decrypt(i int) ([]byte, error) {
|
|||
chunklen := int(binary.BigEndian.Uint32(b[:4]))
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
|||
0.2.1
|
||||
0.3.1
|
||||
|
|
Loading…
Add table
Reference in a new issue