diff --git a/README.md b/README.md index 9c1f7f9..b713fd2 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/go.sum b/go.sum index c3fc986..07d58f7 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/sign/encrypt.go b/sign/encrypt.go index 746d533..79b61ad 100644 --- a/sign/encrypt.go +++ b/sign/encrypt.go @@ -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[:]) diff --git a/sign/crypt_test.go b/sign/encrypt_test.go similarity index 96% rename from sign/crypt_test.go rename to sign/encrypt_test.go index b65d18d..73c2fd3 100644 --- a/sign/crypt_test.go +++ b/sign/encrypt_test.go @@ -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() diff --git a/sign/sign.go b/sign/sign.go index 6169f73..96333b9 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -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) } diff --git a/sign/sign_test.go b/sign/sign_test.go index 17f87f2..70e4159 100644 --- a/sign/sign_test.go +++ b/sign/sign_test.go @@ -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, } diff --git a/sign/utils_test.go b/sign/utils_test.go new file mode 100644 index 0000000..74bebf0 --- /dev/null +++ b/sign/utils_test.go @@ -0,0 +1,43 @@ +// utils_test.go -- Test harness utilities for sign +// +// (c) 2016 Sudhi Herle +// +// 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 +} diff --git a/version b/version index b616048..faef31a 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.6.2 +0.7.0