THIS IS A BREAKING CHANGE! Private Keys generated by previous versions won't work with this version.

* Refactored the private key protection to use standard AEAD
  construction.
* Fix sanity check of decrypted block length to stay within verified
  bounds
* Cleanup test harness to split into utility file (assert()); cleaned up
  names of test functions.
* Fixed scrypt params to not take too long (N=2^19)
* Updated README with these changes
This commit is contained in:
Sudhi Herle 2020-01-08 09:17:54 -08:00
parent 262a554356
commit f32525a864
8 changed files with 231 additions and 235 deletions

View file

@ -139,16 +139,8 @@ recipient can decrypt using their private key.
## Technical Details
### How is the private key protected?
The Ed25519 private key is encrypted using a key derived from the
user supplied pass phrase. This pass phrase is used to derive an
encryption key using the Scrypt key derivation algorithm. The
resulting derived key is XOR'd with the Ed25519 private key before
being committed to disk. To protect the integrity of the process,
the essential parameters used for deriving the key, and the derived
key are hashed via SHA256 and stored along with the encrypted key.
As an additional security measure, the user supplied pass phrase is
hashed with SHA512.
The Ed25519 private key is encrypted in AES-GCM-256 mode using a key
derived from the user's pass phrase.
### How is the Encryption done?
The file encryption uses AES-GCM-256 in AEAD mode. The encryption uses
@ -158,7 +150,7 @@ is 4MB (4 * 1048576 bytes). Each chunk generates its own nonce
from a global salt. The nonce is calculated as a SHA256 hash of
the salt, the chunk length and the block number.
### What is the public-key cryptography used?
### What is the public-key cryptography?
`sigtool` uses Curve25519 ECC to generate shared secrets between
pairs of sender & recipients. This pairwise shared secret is expanded
using HKDF to generate a key-encryption-key. The file-encryption key
@ -170,14 +162,20 @@ corresponding Curve25519 points in order to generate the shared secret.
This elliptic co-ordinate transform follows [FiloSottile's writeup][2].
### Format of the Encrypted File
Every encrypted file starts with a header:
Every encrypted file starts with a header and the header-checksum:
* Fixed-size header
* Variable-length header
* SHA256 sum of both of the above
The fixed length header is:
7 byte magic ("SigTool")
1 byte version number
4 byte header length (big endian encoding)
32 byte SHA256 of the encryption-header
The encryption-header is described as a protobuf file (sign/hdr.proto):
The variable length header has the per-recipient wrapped keys. This is
described as a protobuf file (sign/hdr.proto):
```protobuf
message header {
@ -194,6 +192,8 @@ The encryption-header is described as a protobuf file (sign/hdr.proto):
}
```
The SHA256 sum covers the fixed-length and variable-length headers.
The encrypted data immediately follows the headers above. Each encrypted
chunk is encoded the same way:
@ -239,36 +239,30 @@ And, a serialized Ed25519 private key looks like so:
esk: t3vfqHbgUiA733KKPymFjWT8DdnBEkiMfsDHolPUdQWpvVn/F1Z4J6KYV3M5rGO9xgKxh5RAmqt+6LKgOiJAMQ==
salt: pPHKG55UJYtJ5wU0G9hBvNQJ0DvT0a7T4Fmj4aPB84s=
algo: scrypt-sha256
verify: JvjRjJMKhJhBmZngC3Pvq7x3KCLKt7gar1AAz7HB4qM=
Z: 131072
r: 16
p: 1
```
The Ed25519 private key is encrypted using Scrypt password hashing
mechanism. A user supplied passphrase to protect the private key
is first pre-hashed using SHA-512 before being used in
```scrypt()```. In pseudo code, this operation looks like below:
The Ed25519 private key is encrypted using AES-256-GCM AEAD mode;
the encryption key is derived from the user supplied passphrase
using scrypt KDF. A user supplied passphrase is first expanded
using SHA-512 before being used in ```scrypt()```. In pseudo code,
this operation looks like below:
passphrase = get_user_passphrase()
hpass = SHA512(passphrase)
salt = randombytes(32)
xorkey = Scrypt(hpass, salt, N, r, p)
verify = SHA256(salt, xorkey)
esk = ed25519_private_key ^ xorkey
key = Scrypt(hpass, salt, N, r, p)
esk = AES256_GCM(ed25519_private_key, key)
Where, ```N```, ```r```, ```p``` are Scrypt parameters. In our
implementation:
N = 131072
r = 16
N = 2^19 (1 << 19)
r = 8
p = 1
```verify``` is used during the decryption of the Ed25519 private
key - *before* actually doing the "xor" operation. This check
ensures that the supplied passphrase yields the same value as
```verify```.
### Ed25519 Signature
A generated signature looks like below after serialization:

1
go.sum
View file

@ -22,6 +22,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=

View file

@ -13,15 +13,19 @@
//
// Notes
// =====
// Header: Fixed size + Variable list of recipients
// Header: has 3 parts:
// - Fixed sized header
// - Variable sized protobuf encoded header
// - SHA256 sum of both above.
//
// Fixed size header:
// - Magic: 7 bytes
// - Version: 1 byte
// - VLen: 4 byte
//
// Variable Length Segment:
// - Protobuf encoded, per-recipient wrapped keys
// - Shasum: 32 bytes (SHA256 of full header)
// - Protobuf encoded recipient info
//
// The variable length segment consists of one or more
// recipients, their wrapped keys etc. This is encoded as
@ -48,14 +52,16 @@ import (
)
// Encryption chunk size = 4MB
const chunkSize uint32 = 4 * 1048576
const maxChunkSize uint32 = 16 * 1048576
const EOF uint32 = 1 << 31
const (
chunkSize uint32 = 4 * 1048576
maxChunkSize uint32 = 16 * 1048576
_EOF uint32 = 1 << 31
const _Magic = "SigTool"
const _MagicLen = len(_Magic)
const _AEADNonceLen = 32
const _FixedHdrLen = _MagicLen + 1 + 4
_Magic = "SigTool"
_MagicLen = len(_Magic)
_AEADNonceLen = 32
_FixedHdrLen = _MagicLen + 1 + 4
)
// Encryptor holds the encryption context
type Encryptor struct {
@ -73,7 +79,6 @@ type Encryptor struct {
// 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, blksize uint64) (*Encryptor, error) {
if blksize >= uint64(maxChunkSize) {
return nil, fmt.Errorf("encrypt: Blocksize is too large (max 16M)")
}
@ -131,61 +136,6 @@ func (e *Encryptor) AddRecipient(pk *PublicKey) error {
return nil
}
// Begin the encryption process by writing the header
func (e *Encryptor) start(wr io.Writer) error {
varHdrLen := sha256.Size + e.Size()
hdrBuf := make([]byte, _FixedHdrLen + varHdrLen)
fixedHdr := hdrBuf[:_FixedHdrLen]
varHdr := hdrBuf[_FixedHdrLen:]
cksum := varHdr[:sha256.Size]
pbHdr := varHdr[sha256.Size:]
// Now assemble the fixed header
copy(fixedHdr[:], []byte(_Magic))
fixedHdr[_MagicLen] = 1 // version #
binary.BigEndian.PutUint32(fixedHdr[_MagicLen+1:], uint32(varHdrLen))
// Now marshal the variable portion
_, err := e.MarshalToSizedBuffer(pbHdr)
if err != nil {
return fmt.Errorf("encrypt: can't marshal header: %s", err)
}
// Now calculate checksum of everything
h := sha256.New()
h.Write(fixedHdr)
h.Write(pbHdr)
h.Sum(cksum[:0])
// Finally write it out
err = fullwrite(hdrBuf, wr)
if err != nil {
return fmt.Errorf("encrypt: %s", err)
}
e.started = true
return nil
}
// Write _all_ bytes of buffer 'buf'
func fullwrite(buf []byte, wr io.Writer) error {
n := len(buf)
for n > 0 {
m, err := wr.Write(buf)
if err != nil {
return fmt.Errorf("I/O error: %s", err)
}
n -= m
buf = buf[m:]
}
return nil
}
// Encrypt the input stream 'rd' and write encrypted stream to 'wr'
func (e *Encryptor) Encrypt(rd io.Reader, wr io.Writer) error {
if !e.started {
@ -219,6 +169,57 @@ func (e *Encryptor) Encrypt(rd io.Reader, wr io.Writer) error {
return nil
}
// Begin the encryption process by writing the header
func (e *Encryptor) start(wr io.Writer) error {
varSize := e.Size()
buffer := make([]byte, _FixedHdrLen+varSize+sha256.Size)
fixHdr := buffer[:_FixedHdrLen]
varHdr := buffer[_FixedHdrLen:]
sumHdr := varHdr[varSize:]
// Now assemble the fixed header
copy(fixHdr[:], []byte(_Magic))
fixHdr[_MagicLen] = 1 // version #
binary.BigEndian.PutUint32(fixHdr[_MagicLen+1:], uint32(varSize))
// Now marshal the variable portion
_, err := e.MarshalToSizedBuffer(varHdr[:varSize])
if err != nil {
return fmt.Errorf("encrypt: can't marshal header: %s", err)
}
// Now calculate checksum of everything
h := sha256.New()
h.Write(buffer[:_FixedHdrLen+varSize])
h.Sum(sumHdr[:0])
// Finally write it out
err = fullwrite(buffer, wr)
if err != nil {
return fmt.Errorf("encrypt: %s", err)
}
e.started = true
return nil
}
// Write _all_ bytes of buffer 'buf'
func fullwrite(buf []byte, wr io.Writer) error {
n := len(buf)
for n > 0 {
m, err := wr.Write(buf)
if err != nil {
return fmt.Errorf("I/O error: %s", err)
}
n -= m
buf = buf[m:]
}
return nil
}
// encrypt exactly _one_ block of data
// The nonce for the block is: sha256(salt || chunkLen || block#)
// This protects the output stream from re-ordering attacks and length
@ -231,7 +232,7 @@ func (e *Encryptor) encrypt(buf []byte, wr io.Writer, i uint32, eof bool) error
// mark last block
if eof {
z |= EOF
z |= _EOF
}
binary.BigEndian.PutUint32(b[:4], z)
@ -285,29 +286,29 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
return nil, fmt.Errorf("decrypt: Unsupported version %d", b[_MagicLen])
}
hdrlen := binary.BigEndian.Uint32(b[_MagicLen+1:])
varSize := binary.BigEndian.Uint32(b[_MagicLen+1:])
// sanity check on variable segment length
if hdrlen > 1048576 {
if varSize > 1048576 {
return nil, fmt.Errorf("decrypt: header too large (max 1048576)")
}
if hdrlen < 32 {
if varSize < 32 {
return nil, fmt.Errorf("decrypt: header too small (min 32)")
}
hdr := make([]byte, hdrlen)
// SHA256 is the trailer part of the file-header
varBuf := make([]byte, varSize+sha256.Size)
_, err = io.ReadFull(rd, hdr)
_, err = io.ReadFull(rd, varBuf)
if err != nil {
return nil, fmt.Errorf("decrypt: err while reading header: %s", err)
}
verify := hdr[:sha256.Size]
pbHdr := hdr[sha256.Size:]
verify := varBuf[varSize:]
h := sha256.New()
h.Write(b[:])
h.Write(pbHdr)
h.Write(varBuf[:varSize])
cksum := h.Sum(nil)
if subtle.ConstantTimeCompare(verify, cksum[:]) == 0 {
@ -318,7 +319,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
rd: rd,
}
err = d.Header.Unmarshal(pbHdr)
err = d.Header.Unmarshal(varBuf[:varSize])
if err != nil {
return nil, fmt.Errorf("decrypt: decode error: %s", err)
}
@ -429,7 +430,7 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
n, err := io.ReadFull(d.rd, b[:4])
if err == io.EOF || err == io.ErrClosedPipe || n == 0 {
return nil, false, fmt.Errorf("decrypt: premature EOF-1 while reading block %d", i)
return nil, false, fmt.Errorf("decrypt: premature EOF while reading header block %d", i)
}
if err != nil {
@ -437,12 +438,12 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
}
m := binary.BigEndian.Uint32(b[:4])
eof := (m & EOF) > 0
eof := (m & _EOF) > 0
m &= (EOF-1)
m &= (_EOF - 1)
// Sanity check - in case of corrupt header
if m > (uint32(d.ae.Overhead()) + chunkSize) {
if m > (uint32(d.ae.Overhead()) + d.ChunkSize) {
return nil, false, fmt.Errorf("decrypt: chunksize is too large (%d)", m)
}
@ -456,7 +457,7 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
if m > 0 {
n, err = io.ReadFull(d.rd, d.buf[:m])
if err != nil {
return nil, false, fmt.Errorf("decrypt: premature EOF-2 while reading block %d: %s", i, err)
return nil, false, fmt.Errorf("decrypt: premature EOF while reading block %d: %s", i, err)
}
p, err = d.ae.Open(d.buf[:0], nonce, d.buf[:n], b[:])

View file

@ -23,7 +23,7 @@ import (
)
// one sender, one receiver no verification of sender
func TestSimple(t *testing.T) {
func TestEncryptSimple(t *testing.T) {
assert := newAsserter(t)
receiver, err := NewKeypair()
@ -66,7 +66,7 @@ func TestSimple(t *testing.T) {
}
// test corrupted header or corrupted input
func TestCorrupted(t *testing.T) {
func TestEncryptCorrupted(t *testing.T) {
assert := newAsserter(t)
receiver, err := NewKeypair()
@ -105,7 +105,7 @@ func TestCorrupted(t *testing.T) {
}
// one sender, one receiver with verification of sender
func TestSenderVerified(t *testing.T) {
func TestEncryptSenderVerified(t *testing.T) {
assert := newAsserter(t)
sender, err := NewKeypair()
@ -151,7 +151,7 @@ func TestSenderVerified(t *testing.T) {
}
// one sender, multiple receivers, each decrypting the blob
func TestMultiReceiver(t *testing.T) {
func TestEncryptMultiReceiver(t *testing.T) {
assert := newAsserter(t)
sender, err := NewKeypair()

View file

@ -22,6 +22,8 @@ package sign
import (
"bytes"
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
@ -77,50 +79,38 @@ type Signature struct {
pkhash []byte // [0:16] SHA256 hash of public key needed for verification
}
// Algorithm used in the encrypted private key
const sk_algo = "scrypt-sha256"
const sig_algo = "sha512-ed25519"
// Length of Ed25519 Public Key Hash
const PKHashLength = 16
// Scrypt parameters
const _N = 1 << 17
const _r = 16
const _p = 1
const (
// Scrypt parameters
_N int = 1 << 19
_r int = 8
_p int = 1
// Algorithm used in the encrypted private key
sk_algo = "scrypt-sha256"
sig_algo = "sha512-ed25519"
)
// Encrypted Private key
type encPrivKey struct {
// Encrypted Sk
Esk []byte
type serializedPrivKey struct {
Comment string `yaml:"comment,omitempty"`
// parameters for Sk serialization
Salt []byte
// Encrypted Sk
Esk string `yaml:"esk"`
Salt string `yaml:"salt,omitempty"`
// Algorithm used for checksum and KDF
Algo string
// Checksum to verify passphrase before we xor it
Verify []byte
Algo string `yaml:"algo,omitempty"`
// These are params for scrypt.Key()
// CPU Cost parameter; must be a power of 2
N uint32
// r * p should be less than 2^30
r uint32
p uint32
}
N int `yaml:"Z,flow,omitempty"`
// Serialized representation of private key
type serializedPrivKey struct {
Comment string `yaml:"comment,omitempty"`
Esk string `yaml:"esk"`
Salt string `yaml:"salt,omitempty"`
Algo string `yaml:"algo,omitempty"`
Verify string `yaml:"verify,omitempty"`
N uint32 `yaml:"Z,flow,omitempty"`
R uint32 `yaml:"r,flow,omitempty"`
P uint32 `yaml:"p,flow,omitempty"`
// r * p should be less than 2^30
R int `yaml:"r,flow,omitempty"`
P int `yaml:"p,flow,omitempty"`
}
// serialized representation of public key
@ -213,48 +203,43 @@ func MakePrivateKey(yml []byte, pw []byte) (*PrivateKey, error) {
err := yaml.Unmarshal(yml, &ssk)
if err != nil {
return nil, fmt.Errorf("can't parse YAML: %s", err)
return nil, fmt.Errorf("make priv key: can't parse YAML: %s", err)
}
esk := &encPrivKey{N: ssk.N, r: ssk.R, p: ssk.P, Algo: ssk.Algo}
b64 := base64.StdEncoding.DecodeString
esk.Esk, err = b64(ssk.Esk)
salt, err := b64(ssk.Salt)
if err != nil {
return nil, fmt.Errorf("can't decode YAML:Esk: %s", err)
return nil, fmt.Errorf("make priv key: can't decode salt: %s", err)
}
esk.Salt, err = b64(ssk.Salt)
esk, err := b64(ssk.Esk)
if err != nil {
return nil, fmt.Errorf("can't decode YAML:Salt: %s", err)
}
esk.Verify, err = b64(ssk.Verify)
if err != nil {
return nil, fmt.Errorf("can't decode YAML:Verify: %s", err)
return nil, fmt.Errorf("make priv key: can't decode key: %s", err)
}
// We take short passwords and extend them
pwb := sha512.Sum512(pw)
xork, err := scrypt.Key(pwb[:], esk.Salt, int(esk.N), int(esk.r), int(esk.p), len(esk.Esk))
// "32" == Length of AES-256 key
key, err := scrypt.Key(pwb[:], salt, ssk.N, ssk.R, ssk.P, 32)
if err != nil {
return nil, fmt.Errorf("can't derive key: %s", err)
return nil, fmt.Errorf("make priv key: can't derive key: %s", err)
}
hh := sha256.New()
hh.Write(esk.Salt)
hh.Write(xork)
ck := hh.Sum(nil)
if subtle.ConstantTimeCompare(esk.Verify, ck) != 1 {
return nil, fmt.Errorf("incorrect private key password")
aes, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("make priv key: aes failure: %s", err)
}
// Everything works. Now, decode the key
skb := make([]byte, len(esk.Esk))
for i := 0; i < len(esk.Esk); i++ {
skb[i] = esk.Esk[i] ^ xork[i]
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("make priv key: aes failure: %s", err)
}
skb, err := ae.Open(nil, salt[:ae.NonceSize()], esk, nil)
if err != nil {
return nil, fmt.Errorf("make priv key: wrong password")
}
return PrivateKeyFromBytes(skb)
@ -295,60 +280,58 @@ func (pk *PublicKey) Hash() []byte {
}
// Serialize the private key to a file
// AEAD encryption for protecting the private key
// Format: YAML
// All []byte are in base64 (RawEncoding)
func (sk *PrivateKey) serialize(fn, comment string, getpw func() ([]byte, error)) error {
pw, err := getpw()
if err != nil {
return err
}
b64 := base64.StdEncoding.EncodeToString
esk := &encPrivKey{}
ssk := &serializedPrivKey{Comment: comment}
// expand the password into 64 bytes
pwb := sha512.Sum512(pw)
pass := sha512.Sum512(pw)
salt := make([]byte, 32)
esk.N = _N
esk.r = _r
esk.p = _p
randread(salt)
esk.Salt = make([]byte, 32)
esk.Esk = make([]byte, len(sk.Sk))
randread(esk.Salt)
xork, err := scrypt.Key(pwb[:], esk.Salt, int(esk.N), int(esk.r), int(esk.p), len(sk.Sk))
// "32" == Length of AES-256 key
key, err := scrypt.Key(pass[:], salt, _N, _r, _p, 32)
if err != nil {
return fmt.Errorf("Can't derive scrypt key: %s", err)
return fmt.Errorf("marshal: can't derive scrypt key: %s", err)
}
hh := sha256.New()
hh.Write(esk.Salt)
hh.Write(xork)
esk.Verify = hh.Sum(nil)
aes, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("marshal: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return fmt.Errorf("marshal: %s", err)
}
tl := ae.Overhead()
buf := make([]byte, tl+len(sk.Sk))
esk := ae.Seal(buf[:0], salt[:ae.NonceSize()], sk.Sk, nil)
enc := base64.StdEncoding.EncodeToString
ssk := serializedPrivKey{
Comment: comment,
Esk: enc(esk),
Salt: enc(salt),
Algo: sk_algo,
N: _N,
R: _r,
P: _p,
}
// We won't protect the Scrypt parameters with the hash above
// because it is not needed. If the parameters are wrong, the
// derived key will be wrong and thus, the hash will not match.
esk.Algo = sk_algo // global var
// Finally setup the encrypted key
for i := 0; i < len(sk.Sk); i++ {
esk.Esk[i] = sk.Sk[i] ^ xork[i]
}
ssk.Esk = b64(esk.Esk)
ssk.Salt = b64(esk.Salt)
ssk.Verify = b64(esk.Verify)
ssk.Algo = esk.Algo
ssk.N = esk.N
ssk.R = esk.r
ssk.P = esk.p
out, err := yaml.Marshal(ssk)
out, err := yaml.Marshal(&ssk)
if err != nil {
return fmt.Errorf("can't marahal to YAML: %s", err)
}

View file

@ -14,39 +14,13 @@
package sign
import (
"crypto/subtle"
"fmt"
"io/ioutil"
"os"
"path"
"runtime"
"testing"
// module under test
//"github.com/sign"
)
func newAsserter(t *testing.T) func(cond bool, msg string, args ...interface{}) {
return func(cond bool, msg string, args ...interface{}) {
if cond {
return
}
_, file, line, ok := runtime.Caller(1)
if !ok {
file = "???"
line = 0
}
s := fmt.Sprintf(msg, args...)
t.Fatalf("%s: %d: Assertion failed: %s\n", file, line, s)
}
}
// Return true if two byte arrays are equal
func byteEq(x, y []byte) bool {
return subtle.ConstantTimeCompare(x, y) == 1
}
// Return a temp dir in a temp-dir
func tempdir(t *testing.T) string {
assert := newAsserter(t)
@ -72,7 +46,7 @@ func hardcodedPw() ([]byte, error) {
func wrongPw() ([]byte, error) {
return []byte("xyz"), nil
}
func emptyPw() ([]byte, error) {
func emptyPw() ([]byte, error) {
return nil, nil
}
@ -103,7 +77,7 @@ p: 1
`
// #1. Create new key pair, and read them back.
func Test0(t *testing.T) {
func TestSignSimple(t *testing.T) {
assert := newAsserter(t)
kp, err := NewKeypair()
@ -154,7 +128,7 @@ func Test0(t *testing.T) {
}
// #2. Create new key pair, sign a rand buffer and verify
func Test1(t *testing.T) {
func TestSignRandBuf(t *testing.T) {
assert := newAsserter(t)
kp, err := NewKeypair()
assert(err == nil, "NewKeyPair() fail")
@ -272,7 +246,7 @@ func Benchmark_Sig(b *testing.B) {
64,
1024,
4096,
256*1024,
256 * 1024,
1048576,
4 * 1048576,
}

43
sign/utils_test.go Normal file
View file

@ -0,0 +1,43 @@
// utils_test.go -- Test harness utilities for sign
//
// (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 (
"crypto/subtle"
"fmt"
"runtime"
"testing"
)
func newAsserter(t *testing.T) func(cond bool, msg string, args ...interface{}) {
return func(cond bool, msg string, args ...interface{}) {
if cond {
return
}
_, file, line, ok := runtime.Caller(1)
if !ok {
file = "???"
line = 0
}
s := fmt.Sprintf(msg, args...)
t.Fatalf("%s: %d: Assertion failed: %s\n", file, line, s)
}
}
// Return true if two byte arrays are equal
func byteEq(x, y []byte) bool {
return subtle.ConstantTimeCompare(x, y) == 1
}

View file

@ -1 +1 @@
0.6.2
0.7.0