Moved go-sign from external repo to this repo; updated README; added Makefile
This commit is contained in:
parent
191b7a457d
commit
15477d6197
8 changed files with 1083 additions and 139 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,7 +27,6 @@ vendor/*
|
|||
# vendor management
|
||||
vendor/src/*
|
||||
vendor/pkg/*
|
||||
src/*
|
||||
bin/*
|
||||
|
||||
.??*.sw?
|
||||
|
|
17
Makefile
Normal file
17
Makefile
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
pwd = $(shell pwd)
|
||||
GOPATH := $(pwd)/vendor:$(pwd)
|
||||
export GOPATH
|
||||
|
||||
all:
|
||||
mkdir -p bin
|
||||
go get -d .
|
||||
go build -o bin/sigtool .
|
||||
|
||||
test:
|
||||
go test sign
|
||||
clean:
|
||||
rm -f bin/sigtool
|
||||
|
||||
realclean: clean
|
||||
rm -rf vendor
|
166
README.md
Normal file
166
README.md
Normal file
|
@ -0,0 +1,166 @@
|
|||
[](https://godoc.org/github.com/opencoff/go-sign)
|
||||
|
||||
# README for sigtool
|
||||
|
||||
|
||||
## What is this?
|
||||
`sigtool` is an opinionated tool to generate, sign and verify Ed25519
|
||||
signatures on files. In many ways, it is like like OpenBSD's signify_
|
||||
-- except written in Golang and definitely easier to use.
|
||||
|
||||
It can sign and verify very large files - it prehashes the files
|
||||
with SHA-512 and then signs the SHA-512 checksum.
|
||||
|
||||
All the artifacts produced by this tool are standard YAML files -
|
||||
thus, human readable.
|
||||
|
||||
## How do I build it?
|
||||
With Go 1.5 and later:
|
||||
|
||||
git clone https://github.com/opencoff/sigtool
|
||||
cd sigtool
|
||||
make
|
||||
|
||||
The binary will be in `./sigtool`.
|
||||
|
||||
## How do I use it?
|
||||
Broadly, the tool can:
|
||||
|
||||
- generate new key pairs (public key and private key)
|
||||
- sign a file
|
||||
- verify a file against its signature
|
||||
|
||||
### Generate Key pair
|
||||
To start with, you generate a new key pair (a public key used for
|
||||
verification and a private key used for signing). e.g.,
|
||||
|
||||
sigtool gen /tmp/testkey
|
||||
|
||||
The tool then generates */tmp/testkey.pub* and */tmp/testkey.key*. The secret
|
||||
key (".key") can optionally be encrypted with a user supplied pass
|
||||
phrase - which the user has to enter via interactive prompt:
|
||||
|
||||
sigtool gen -p /tmp/testkey
|
||||
|
||||
### Sign a file
|
||||
Signing a file requires the user to provide a previously generated
|
||||
Ed25519 private key. The signature (YAML) is written to STDOUT.
|
||||
e.g., to sign `archive.tar.gz` with private key `/tmp/testkey.key`:
|
||||
|
||||
sigtool sign /tmp/testkey.key archive.tar.gz
|
||||
|
||||
If *testkey.key* was encrypted with a user pass phrase:
|
||||
|
||||
sigtool sign -p /tmp/testkey.key archive.tar.gz
|
||||
|
||||
|
||||
The signature can also be written directly to a user supplied output
|
||||
file.
|
||||
|
||||
sigtool sign -p -o archive.sig /tmp/testkey.key archive.tar.gz
|
||||
|
||||
|
||||
### Verify a signature against a file
|
||||
Verifying a signature of a file requires the user to supply three
|
||||
pieces of information:
|
||||
|
||||
- the Ed25519 public key to be used for verification
|
||||
- the Ed25519 signature
|
||||
- the file whose signature must be verified
|
||||
|
||||
e.g., to verify the signature of *archive.tar.gz* against
|
||||
*testkey.pub* using the signature *archive.sig*
|
||||
|
||||
sigtool verify /tmp/testkey.pub archive.sig archive.tar.gz
|
||||
|
||||
## 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.
|
||||
|
||||
## Understanding the Code
|
||||
`src/sign` is a library to generate, verify and store Ed25519 keys
|
||||
and signatures. It uses the extended library (golang.org/x/crypto)
|
||||
for the underlying operations.
|
||||
|
||||
The generated keys and signatures are proper YAML files and human
|
||||
readable.
|
||||
|
||||
The signature file contains a hash of the public key - so that at
|
||||
verification time, the right private key may be used (in situations
|
||||
where there are lots of keys).
|
||||
|
||||
Signatures on large files are calculated efficiently by reading them
|
||||
in memory mapped mode (```mmap(2)```) and hashing the file contents
|
||||
using SHA-512. The Ed25519 signature is calculated on the file-hash.
|
||||
|
||||
## Example of Keys, Signature
|
||||
|
||||
### Ed25519 Public Key
|
||||
A serialized Ed25519 public key looks like so:
|
||||
|
||||
pk: uxpDh+gqXojAmxA/6vxZHzA+Uk+8wogUwvEhPBlWgvo=
|
||||
|
||||
### Ed25519 Private Key
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
Where, ```N```, ```r```, ```p``` are Scrypt parameters. In our
|
||||
implementation:
|
||||
|
||||
N = 131072
|
||||
r = 16
|
||||
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:
|
||||
|
||||
comment: inpfile=/tmp/file.txt
|
||||
pkhash: 36z9tCwTIVNwwDlExrB0SQ==
|
||||
signature: ow2oBP+buDbEvlNakOrsxgB5Yc/7PYyPVZCkfyu7oahw8BakF4Qf32uswPaKGZ8RVz4uXboYHdZtfrEjCgP/Cg==
|
||||
|
||||
Here, ```pkhash`` is a SHA256 of the public key needed to verify
|
||||
this signature.
|
||||
|
||||
## Licensing Terms
|
||||
The tool and code is licensed under the terms of the
|
||||
GNU Public License v2.0 (strictly v2.0). If you need a commercial
|
||||
license or a different license, please get in touch with me.
|
||||
|
||||
See the file ``LICENSE.md`` for the full terms of the license.
|
||||
|
||||
## Author
|
||||
Sudhi Herle <sw@herle.net>
|
||||
|
||||
.. _signify: https://www.openbsd.org/papers/bsdcan-signify.html
|
137
README.rst
137
README.rst
|
@ -1,137 +0,0 @@
|
|||
==================
|
||||
README for sigtool
|
||||
==================
|
||||
|
||||
|
||||
What is this?
|
||||
=============
|
||||
This is a tool to generate, sign and verify Ed25519 signatures. In
|
||||
many ways, it is like like OpenBSD's signify_ -- except written in Golang
|
||||
and designed to be easier to use.
|
||||
|
||||
It can sign and verify very large files - it prehashes the files
|
||||
with SHA-512 and then signs the SHA-512 checksum.
|
||||
|
||||
All the artifacts produced by this tool are standard YAML files -
|
||||
thus, human readable.
|
||||
|
||||
How do I build it?
|
||||
==================
|
||||
With Go 1.5 and later::
|
||||
|
||||
mkdir sigtool
|
||||
cd sigtool
|
||||
env GOPATH=`pwd` go get -u github.com/opencoff/sigtool
|
||||
|
||||
The binary will be in ``bin/sigtool``.
|
||||
|
||||
How do I use it?
|
||||
================
|
||||
Broadly, the tool can:
|
||||
|
||||
- generate new key pairs (public key and private key)
|
||||
- sign a file
|
||||
- verify a file against its signature
|
||||
|
||||
Generate Key pair
|
||||
-----------------
|
||||
To start with, you generate a new key pair (a public key used for
|
||||
verification and a private key used for signing). e.g., ::
|
||||
|
||||
sigtool gen /tmp/testkey
|
||||
|
||||
The tool then generates */tmp/testkey.pub* and */tmp/testkey.key*. The secret
|
||||
key (".key") can optionally be encrypted with a user supplied pass
|
||||
phrase - which the user has to enter via interactive prompt::
|
||||
|
||||
sigtool gen -p /tmp/testkey
|
||||
|
||||
Sign a file
|
||||
-----------
|
||||
Signing a file requires the user to provide a previously generated
|
||||
Ed25519 private key. The signature (YAML) is written to STDOUT.
|
||||
e.g., to sign ``archive.tar.gz`` with private key ``/tmp/testkey.key`` ::
|
||||
|
||||
sigtool sign /tmp/testkey.key archive.tar.gz
|
||||
|
||||
If *testkey.key* was encrypted with a user pass phrase::
|
||||
|
||||
sigtool sign -p /tmp/testkey.key archive.tar.gz
|
||||
|
||||
|
||||
The signature can also be written directly to a user supplied output
|
||||
file.::
|
||||
|
||||
sigtool sign -p -o archive.sig /tmp/testkey.key archive.tar.gz
|
||||
|
||||
|
||||
Verify a signature against a file
|
||||
---------------------------------
|
||||
Verifying a signature of a file requires the user to supply three
|
||||
pieces of information:
|
||||
|
||||
- the Ed25519 public key to be used for verification
|
||||
- the Ed25519 signature
|
||||
- the file whose signature must be verified
|
||||
|
||||
e.g., to verify the signature of *archive.tar.gz* against
|
||||
*testkey.pub* using the signature *archive.sig*::
|
||||
|
||||
sigtool verify /tmp/testkey.pub archive.sig archive.tar.gz
|
||||
|
||||
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.
|
||||
|
||||
In Pseudo-code::
|
||||
|
||||
passwd = get_user_password()
|
||||
hpass = SHA512(passwd)
|
||||
salt = randombytes(32)
|
||||
xorkey = Scrypt(hpass, salt, N, r, p)
|
||||
|
||||
cksum = SHA256(salt, xorkey)
|
||||
enckey = ed25519_private_key ^ xorkey
|
||||
|
||||
And, ``cksum``, ``enckey`` are the entities stored as on-disk
|
||||
private key.
|
||||
|
||||
The Scrypt parameters used by the ``sign`` library are:
|
||||
|
||||
- N: 131072
|
||||
- r: 16
|
||||
- p: 1
|
||||
|
||||
Understanding the Code
|
||||
======================
|
||||
The tool uses a companion library that manages the keys and
|
||||
signatures. It is part of a growing set of Golang libraries that are
|
||||
useful in multiple projects. You can find them on github_.
|
||||
|
||||
The core code is in the ``sign`` library. This library is
|
||||
can be reused in any of your projects.
|
||||
|
||||
.. _github: https://github.com/opencoff/go-sign/
|
||||
|
||||
Licensing Terms
|
||||
===============
|
||||
The tool is licensed under the terms of the GNU Public License v2.0
|
||||
(strictly v2.0). If you need a commercial license or a different
|
||||
license, please get in touch with me.
|
||||
|
||||
See the file ``LICENSE.md`` for the full terms of the license.
|
||||
|
||||
Author
|
||||
======
|
||||
Sudhi Herle <sw@herle.net>
|
||||
|
||||
.. _signify: https://www.openbsd.org/papers/bsdcan-signify.html
|
|
@ -21,8 +21,10 @@ import (
|
|||
"path"
|
||||
|
||||
flag "github.com/ogier/pflag"
|
||||
"github.com/opencoff/go-sign"
|
||||
"github.com/opencoff/go-utils"
|
||||
|
||||
// My signing library
|
||||
"sign"
|
||||
)
|
||||
|
||||
// This will be filled in by "build"
|
||||
|
|
46
src/sign/.gitignore
vendored
Normal file
46
src/sign/.gitignore
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
bin/*
|
||||
.*.sw?
|
||||
.idea
|
||||
logs/*
|
||||
|
||||
# gg ignores
|
||||
vendor/src/*
|
||||
vendor/pkg/*
|
||||
servers.iml
|
||||
*.DS_Store
|
||||
|
||||
# vagrant ignores
|
||||
tools/vagrant/.vagrant
|
||||
tools/vagrant/adsrv-conf/.frontend
|
||||
tools/vagrant/adsrv-conf/.bidder
|
||||
tools/vagrant/adsrv-conf/.transcoder
|
||||
tools/vagrant/redis-cluster-conf/7777/nodes.conf
|
||||
tools/vagrant/redis-cluster-conf/7778/nodes.conf
|
||||
tools/vagrant/redis-cluster-conf/7779/nodes.conf
|
||||
*.aof
|
||||
*.rdb
|
541
src/sign/sign.go
Normal file
541
src/sign/sign.go
Normal file
|
@ -0,0 +1,541 @@
|
|||
// sign.go -- Ed25519 keys and signature handling
|
||||
//
|
||||
// (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 implements Ed25519 signing, verification on files.
|
||||
// It builds upon golang.org/x/crypto/ed25519 by adding methods
|
||||
// for serializing and deserializing Ed25519 private & public keys.
|
||||
// In addition, it works with large files - by precalculating their
|
||||
// SHA512 checksum in mmap'd mode and sending the 64 byte signature
|
||||
// for Ed25519 signing.
|
||||
package sign
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
Ed "golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/opencoff/go-utils"
|
||||
)
|
||||
|
||||
// Private Ed25519 key
|
||||
type PrivateKey struct {
|
||||
Sk []byte
|
||||
|
||||
// Cached copy of the public key
|
||||
// In reality, it is a pointer to Sk[32:]
|
||||
pk []byte
|
||||
}
|
||||
|
||||
// Public Ed25519 key
|
||||
type PublicKey struct {
|
||||
Pk []byte
|
||||
}
|
||||
|
||||
// Ed25519 key pair
|
||||
type Keypair struct {
|
||||
Sec PrivateKey
|
||||
Pub PublicKey
|
||||
}
|
||||
|
||||
// An Ed25519 Signature
|
||||
type Signature struct {
|
||||
Sig []byte // 32 byte digital signature
|
||||
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"
|
||||
|
||||
// Scrypt parameters
|
||||
const _N = 1 << 17
|
||||
const _r = 16
|
||||
const _p = 1
|
||||
|
||||
// Encrypted Private key
|
||||
type encPrivKey struct {
|
||||
// Encrypted Sk
|
||||
Esk []byte
|
||||
|
||||
// parameters for Sk serialization
|
||||
Salt []byte
|
||||
|
||||
// Algorithm used for checksum and KDF
|
||||
Algo string
|
||||
|
||||
// Checksum to verify passphrase before we xor it
|
||||
Verify []byte
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// serialized representation of public key
|
||||
type serializedPubKey struct {
|
||||
Comment string `yaml:"comment,omitempty"`
|
||||
Pk string `yaml:"pk"`
|
||||
}
|
||||
|
||||
// Serialized signature
|
||||
type signature struct {
|
||||
Comment string `yaml:"comment,omitempty"`
|
||||
Pkhash string `yaml:"pkhash,omitempty"`
|
||||
Signature string `yaml:"signature"`
|
||||
}
|
||||
|
||||
// Generate a new Ed25519 keypair
|
||||
func NewKeypair() (*Keypair, error) {
|
||||
//kp := &Keypair{Sec: PrivateKey{N: 1 << 17, r: 64, p: 1}}
|
||||
kp := &Keypair{}
|
||||
sk := &kp.Sec
|
||||
pk := &kp.Pub
|
||||
|
||||
p, s, err := Ed.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Can't generate Ed25519 keys: %s", err)
|
||||
}
|
||||
|
||||
pk.Pk = []byte(p)
|
||||
sk.Sk = []byte(s)
|
||||
|
||||
return kp, nil
|
||||
}
|
||||
|
||||
// Serialize the keypair to two separate files. The basename of the
|
||||
// file is 'bn'; the public key goes in $bn.pub and the private key
|
||||
// goes in $bn.key.
|
||||
// If password is non-empty, then the private key is encrypted
|
||||
// before writing to disk.
|
||||
func (kp *Keypair) Serialize(bn, comment string, pw string) error {
|
||||
|
||||
sk := &kp.Sec
|
||||
pk := &kp.Pub
|
||||
|
||||
skf := fmt.Sprintf("%s.key", bn)
|
||||
pkf := fmt.Sprintf("%s.pub", bn)
|
||||
|
||||
err := pk.serialize(pkf, comment)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't serialize to %s: %s", pkf, err)
|
||||
}
|
||||
|
||||
err = sk.serialize(skf, comment, pw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't serialize to %s: %s", pkf, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read the private key in 'fn', optionally decrypting it using
|
||||
// password 'pw' and create new instance of PrivateKey
|
||||
func ReadPrivateKey(fn string, pw string) (*PrivateKey, error) {
|
||||
yml, err := ioutil.ReadFile(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return MakePrivateKey(yml, pw)
|
||||
}
|
||||
|
||||
// Make a private key from bytes 'yml' and password 'pw'. The bytes
|
||||
// are assumed to be serialized version of the private key.
|
||||
func MakePrivateKey(yml []byte, pw string) (*PrivateKey, error) {
|
||||
var ssk serializedPrivKey
|
||||
|
||||
err := yaml.Unmarshal(yml, &ssk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't decode YAML:Esk: %s", err)
|
||||
}
|
||||
|
||||
esk.Salt, err = b64(ssk.Salt)
|
||||
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)
|
||||
}
|
||||
|
||||
sk := &PrivateKey{}
|
||||
|
||||
// We take short passwords and extend them
|
||||
pwb := sha512.Sum512([]byte(pw))
|
||||
|
||||
xork, err := scrypt.Key(pwb[:], esk.Salt, int(esk.N), int(esk.r), int(esk.p), len(esk.Esk))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("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")
|
||||
}
|
||||
|
||||
// Everything works. Now, decode the key
|
||||
sk.Sk = make([]byte, len(esk.Esk))
|
||||
for i := 0; i < len(esk.Esk); i++ {
|
||||
sk.Sk[i] = esk.Esk[i] ^ xork[i]
|
||||
}
|
||||
|
||||
return sk, nil
|
||||
}
|
||||
|
||||
// Serialize the private key to a file
|
||||
// Format: YAML
|
||||
// All []byte are in base64 (RawEncoding)
|
||||
func (sk *PrivateKey) serialize(fn, comment string, pw string) error {
|
||||
|
||||
b64 := base64.StdEncoding.EncodeToString
|
||||
esk := &encPrivKey{}
|
||||
ssk := &serializedPrivKey{Comment: comment}
|
||||
|
||||
// Even with an empty password, we still encrypt and store.
|
||||
|
||||
// expand the password into 64 bytes
|
||||
pwb := sha512.Sum512([]byte(pw))
|
||||
|
||||
esk.N = _N
|
||||
esk.r = _r
|
||||
esk.p = _p
|
||||
|
||||
esk.Salt = make([]byte, 32)
|
||||
esk.Esk = make([]byte, len(sk.Sk))
|
||||
|
||||
_, err := rand.Read(esk.Salt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't read random salt: %s", err)
|
||||
}
|
||||
|
||||
xork, err := scrypt.Key(pwb[:], esk.Salt, int(esk.N), int(esk.r), int(esk.p), len(sk.Sk))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't derive scrypt key: %s", err)
|
||||
}
|
||||
|
||||
hh := sha256.New()
|
||||
hh.Write(esk.Salt)
|
||||
hh.Write(xork)
|
||||
esk.Verify = hh.Sum(nil)
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't marahal to YAML: %s", err)
|
||||
}
|
||||
|
||||
return writeFile(fn, out, 0600)
|
||||
}
|
||||
|
||||
// Sign a prehashed Message; return the signature as opaque bytes
|
||||
// Signature is an YAML file:
|
||||
// Comment: source file path
|
||||
// Signature: Ed25519 signature
|
||||
func (sk *PrivateKey) SignMessage(ck []byte, comment string) (*Signature, error) {
|
||||
x := Ed.PrivateKey(sk.Sk)
|
||||
|
||||
sig, err := x.Sign(rand.Reader, ck, crypto.Hash(0))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't sign %x: %s", ck, err)
|
||||
}
|
||||
|
||||
esk := Ed.PrivateKey(sk.Sk) // type cast
|
||||
epk := esk.Public() // interface
|
||||
xpk := epk.(Ed.PublicKey) // type assertion
|
||||
pk := []byte(xpk) // cast
|
||||
pkh := sha256.Sum256(pk)
|
||||
|
||||
return &Signature{Sig: sig, pkhash: pkh[:16]}, nil
|
||||
}
|
||||
|
||||
// Read and sign a file
|
||||
//
|
||||
// We calculate the signature differently here: We first calculate
|
||||
// the SHA-512 checksum of the file and its size. We sign the
|
||||
// checksum.
|
||||
func (sk *PrivateKey) SignFile(fn string) (*Signature, error) {
|
||||
|
||||
ck, err := fileCksum(fn, sha512.New())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sk.SignMessage(ck, fn)
|
||||
}
|
||||
|
||||
// -- Signature Methods --
|
||||
|
||||
// Read serialized signature from file 'fn' and construct a
|
||||
// Signature object
|
||||
func ReadSignature(fn string) (*Signature, error) {
|
||||
yml, err := ioutil.ReadFile(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return MakeSignature(yml)
|
||||
}
|
||||
|
||||
// Parse serialized signature from bytes 'b' and construct a
|
||||
// Signature object
|
||||
func MakeSignature(b []byte) (*Signature, error) {
|
||||
var ss signature
|
||||
err := yaml.Unmarshal(b, &ss)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse YAML signature: %s", err)
|
||||
}
|
||||
|
||||
b64 := base64.StdEncoding.DecodeString
|
||||
|
||||
s, err := b64(ss.Signature)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't decode Base64:Signature <%s>: %s", ss.Signature, err)
|
||||
}
|
||||
|
||||
p, err := b64(ss.Pkhash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't decode Base64:Pkhash <%s>: %s", ss.Pkhash, err)
|
||||
}
|
||||
|
||||
return &Signature{Sig: s, pkhash: p}, nil
|
||||
}
|
||||
|
||||
// Serialize a signature suitable for storing in durable media
|
||||
func (sig *Signature) Serialize(comment string) ([]byte, error) {
|
||||
|
||||
sigs := base64.StdEncoding.EncodeToString(sig.Sig)
|
||||
pks := base64.StdEncoding.EncodeToString(sig.pkhash)
|
||||
ss := &signature{Comment: comment, Pkhash: pks, Signature: sigs}
|
||||
|
||||
out, err := yaml.Marshal(ss)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't marshal signature of %x to YAML: %s", sig.Sig, err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SerializeFile serializes the signature to an output file 'f'
|
||||
func (sig *Signature) SerializeFile(fn, comment string) error {
|
||||
b, err := sig.Serialize(comment)
|
||||
if err == nil {
|
||||
err = writeFile(fn, b, 0644)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// IsPKMatch returns true if public key 'pk' can potentially validate
|
||||
// the signature. It does this by comparing the hash of 'pk' against
|
||||
// 'Pkhash' of 'sig'.
|
||||
func (sig *Signature) IsPKMatch(pk *PublicKey) bool {
|
||||
h := sha256.Sum256(pk.Pk)
|
||||
return subtle.ConstantTimeCompare(h[:16], sig.pkhash) == 1
|
||||
}
|
||||
|
||||
// --- Public Key Methods ---
|
||||
|
||||
// Read the public key from 'fn' and create new instance of
|
||||
// PublicKey
|
||||
func ReadPublicKey(fn string) (*PublicKey, error) {
|
||||
var err error
|
||||
var yml []byte
|
||||
|
||||
if yml, err = ioutil.ReadFile(fn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return MakePublicKey(yml)
|
||||
}
|
||||
|
||||
// Parse a serialized public in 'yml' and return the resulting
|
||||
// public key instance
|
||||
func MakePublicKey(yml []byte) (*PublicKey, error) {
|
||||
var spk serializedPubKey
|
||||
var err error
|
||||
|
||||
if err = yaml.Unmarshal(yml, &spk); err != nil {
|
||||
return nil, fmt.Errorf("can't parse YAML: %s", err)
|
||||
}
|
||||
|
||||
pk := &PublicKey{}
|
||||
b64 := base64.StdEncoding.DecodeString
|
||||
|
||||
if pk.Pk, err = b64(spk.Pk); err != nil {
|
||||
return nil, fmt.Errorf("can't decode YAML:Pk: %s", err)
|
||||
}
|
||||
|
||||
// Simple sanity checks
|
||||
if len(pk.Pk) == 0 {
|
||||
return nil, fmt.Errorf("public key data is empty?")
|
||||
}
|
||||
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
// Serialize Public Keys
|
||||
func (pk *PublicKey) serialize(fn, comment string) error {
|
||||
b64 := base64.StdEncoding.EncodeToString
|
||||
spk := &serializedPubKey{Comment: comment}
|
||||
|
||||
spk.Pk = b64(pk.Pk)
|
||||
|
||||
out, err := yaml.Marshal(spk)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't marahal to YAML: %s", err)
|
||||
}
|
||||
|
||||
return writeFile(fn, out, 0644)
|
||||
}
|
||||
|
||||
// Verify a signature 'sig' for file 'fn' against public key 'pk'
|
||||
// Return True if signature matches, False otherwise
|
||||
func (pk *PublicKey) VerifyFile(fn string, sig *Signature) (bool, error) {
|
||||
|
||||
ck, err := fileCksum(fn, sha512.New())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return pk.VerifyMessage(ck, sig)
|
||||
}
|
||||
|
||||
// Verify a signature 'sig' for a pre-calculated checksum 'ck' against public key 'pk'
|
||||
// Return True if signature matches, False otherwise
|
||||
func (pk *PublicKey) VerifyMessage(ck []byte, sig *Signature) (bool, error) {
|
||||
|
||||
x := Ed.PublicKey(pk.Pk)
|
||||
return Ed.Verify(x, ck, sig.Sig), nil
|
||||
}
|
||||
|
||||
// -- Internal Utility Functions --
|
||||
|
||||
// Unlink a file.
|
||||
func unlink(f string) {
|
||||
st, err := os.Stat(f)
|
||||
if err == nil {
|
||||
if !st.Mode().IsRegular() {
|
||||
panic(fmt.Sprintf("%s can't be unlinked. Not a regular file?", f))
|
||||
}
|
||||
|
||||
os.Remove(f)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Simple function to reliably write data to a file.
|
||||
// Does MORE than ioutil.WriteFile() - in that it doesn't trash the
|
||||
// existing file with an incomplete write.
|
||||
func writeFile(fn string, b []byte, mode uint32) error {
|
||||
tmp := fmt.Sprintf("%s.tmp", fn)
|
||||
unlink(tmp)
|
||||
|
||||
fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(mode))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't create file %s: %s", tmp, err)
|
||||
}
|
||||
|
||||
_, err = fd.Write(b)
|
||||
if err != nil {
|
||||
fd.Close()
|
||||
// XXX Do we delete the tmp file?
|
||||
return fmt.Errorf("Can't write %v bytes to %s: %s", len(b), tmp, err)
|
||||
}
|
||||
|
||||
fd.Close() // we ignore close(2) errors; unrecoverable anyway.
|
||||
|
||||
os.Rename(tmp, fn)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate file checksum out of hash function h
|
||||
func fileCksum(fn string, h hash.Hash) ([]byte, error) {
|
||||
|
||||
fd, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't open %s: %s", fn, err)
|
||||
}
|
||||
|
||||
defer fd.Close()
|
||||
|
||||
sz, err := utils.MmapReader(fd, 0, 0, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var b [8]byte
|
||||
binary.BigEndian.PutUint64(b[:], uint64(sz))
|
||||
h.Write(b[:])
|
||||
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
// EOF
|
||||
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
310
src/sign/sign_test.go
Normal file
310
src/sign/sign_test.go
Normal file
|
@ -0,0 +1,310 @@
|
|||
// sign_test.go -- Test harness 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/rand"
|
||||
"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)
|
||||
|
||||
var b [10]byte
|
||||
|
||||
dn := os.TempDir()
|
||||
rand.Read(b[:])
|
||||
|
||||
tmp := path.Join(dn, fmt.Sprintf("%x", b[:]))
|
||||
err := os.MkdirAll(tmp, 0755)
|
||||
assert(err == nil, fmt.Sprintf("mkdir -p %s: %s", tmp, err))
|
||||
|
||||
//t.Logf("Tempdir is %s", tmp)
|
||||
return tmp
|
||||
}
|
||||
|
||||
// Return true if file exists, false otherwise
|
||||
func fileExists(fn string) bool {
|
||||
st, err := os.Stat(fn)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if st.Mode().IsRegular() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const badsk string = `
|
||||
esk: q8AP3/6C5F0zB8CLiuJsidx2gJYmrnyOmuoazEbKL5Uh+Jn/Zgw85fTbYfhjcbt48CJejBzsgPYRYR7wWECFRA==
|
||||
salt: uIdTQZotfnkaLkth9jsHvoQKMWdNZuE7dgVNADrRoeY=
|
||||
algo: scrypt-sha256
|
||||
verify: AOFLLC6h29+mvstWtMU1/zZFwHLBMMiI4mlW9DHpYdM=
|
||||
Z: 65536
|
||||
r: 8
|
||||
p: 1
|
||||
`
|
||||
|
||||
// #1. Create new key pair, and read them back.
|
||||
func Test0(t *testing.T) {
|
||||
assert := newAsserter(t)
|
||||
|
||||
kp, err := NewKeypair()
|
||||
assert(err == nil, "NewKeyPair() fail")
|
||||
|
||||
dn := tempdir(t)
|
||||
bn := fmt.Sprintf("%s/t0", dn)
|
||||
|
||||
err = kp.Serialize(bn, "", "abc")
|
||||
assert(err == nil, "keyPair.Serialize() fail")
|
||||
|
||||
pkf := fmt.Sprintf("%s.pub", bn)
|
||||
skf := fmt.Sprintf("%s.key", bn)
|
||||
|
||||
// We must find these two files
|
||||
assert(fileExists(pkf), "missing pkf")
|
||||
assert(fileExists(skf), "missing skf")
|
||||
|
||||
// send wrong file and see what happens
|
||||
pk, err := ReadPublicKey(skf)
|
||||
assert(err != nil, "bad PK ReadPK fail")
|
||||
|
||||
pk, err = ReadPublicKey(pkf)
|
||||
assert(err == nil, "ReadPK() fail")
|
||||
|
||||
// -ditto- for Sk
|
||||
sk, err := ReadPrivateKey(pkf, "")
|
||||
assert(err != nil, "bad SK ReadSK fail")
|
||||
|
||||
sk, err = ReadPrivateKey(skf, "")
|
||||
assert(err != nil, "ReadSK() empty pw fail")
|
||||
|
||||
sk, err = ReadPrivateKey(skf, "abcdef")
|
||||
assert(err != nil, "ReadSK() wrong pw fail")
|
||||
|
||||
badf := fmt.Sprintf("%s/badf.key", dn)
|
||||
err = ioutil.WriteFile(badf, []byte(badsk), 0600)
|
||||
assert(err == nil, "write badsk")
|
||||
|
||||
sk, err = ReadPrivateKey(badf, "abc")
|
||||
assert(err != nil, "badsk read fail")
|
||||
|
||||
// Finally, with correct password it should work.
|
||||
sk, err = ReadPrivateKey(skf, "abc")
|
||||
assert(err == nil, "ReadSK() correct pw fail")
|
||||
|
||||
// And, deserialized keys should be identical
|
||||
assert(byteEq(pk.Pk, kp.Pub.Pk), "pkbytes unequal")
|
||||
assert(byteEq(sk.Sk, kp.Sec.Sk), "skbytes unequal")
|
||||
|
||||
os.RemoveAll(dn)
|
||||
}
|
||||
|
||||
// #2. Create new key pair, sign a rand buffer and verify
|
||||
func Test1(t *testing.T) {
|
||||
assert := newAsserter(t)
|
||||
kp, err := NewKeypair()
|
||||
assert(err == nil, "NewKeyPair() fail")
|
||||
|
||||
var ck [64]byte // simulates sha512 sum
|
||||
|
||||
rand.Read(ck[:])
|
||||
|
||||
pk := &kp.Pub
|
||||
sk := &kp.Sec
|
||||
|
||||
ss, err := sk.SignMessage(ck[:], "")
|
||||
assert(err == nil, "sk.sign fail")
|
||||
assert(ss != nil, "sig is null")
|
||||
|
||||
// verify sig
|
||||
assert(ss.IsPKMatch(pk), "pk match fail")
|
||||
|
||||
// Corrupt the pkhash and see
|
||||
rand.Read(ss.pkhash[:])
|
||||
assert(!ss.IsPKMatch(pk), "corrupt pk match fail")
|
||||
|
||||
// Incorrect checksum == should fail verification
|
||||
ok, err := pk.VerifyMessage(ck[:16], ss)
|
||||
assert(err == nil, "bad ck verify err fail")
|
||||
assert(!ok, "bad ck verify fail")
|
||||
|
||||
// proper checksum == should work
|
||||
ok, err = pk.VerifyMessage(ck[:], ss)
|
||||
assert(err == nil, "verify err")
|
||||
assert(ok, "verify fail")
|
||||
|
||||
// Now sign a file
|
||||
dn := tempdir(t)
|
||||
bn := fmt.Sprintf("%s/k", dn)
|
||||
|
||||
pkf := fmt.Sprintf("%s.pub", bn)
|
||||
skf := fmt.Sprintf("%s.key", bn)
|
||||
|
||||
err = kp.Serialize(bn, "", "")
|
||||
assert(err == nil, "keyPair.Serialize() fail")
|
||||
|
||||
// Now read the private key and sign
|
||||
sk, err = ReadPrivateKey(skf, "")
|
||||
assert(err == nil, "readSK fail")
|
||||
|
||||
pk, err = ReadPublicKey(pkf)
|
||||
assert(err == nil, "ReadPK fail")
|
||||
|
||||
var buf [8192]byte
|
||||
|
||||
zf := fmt.Sprintf("%s/file.dat", dn)
|
||||
fd, err := os.OpenFile(zf, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
assert(err == nil, "file.dat creat file")
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
rand.Read(buf[:])
|
||||
n, err := fd.Write(buf[:])
|
||||
assert(err == nil, fmt.Sprintf("file.dat write fail: %s", err))
|
||||
assert(n == 8192, fmt.Sprintf("file.dat i/o fail: exp 8192 saw %v", n))
|
||||
}
|
||||
fd.Sync()
|
||||
fd.Close()
|
||||
|
||||
sig, err := sk.SignFile(zf)
|
||||
assert(err == nil, "file.dat sign fail")
|
||||
assert(sig != nil, "file.dat sign nil")
|
||||
|
||||
ok, err = pk.VerifyFile(zf, sig)
|
||||
assert(err == nil, "file.dat verify fail")
|
||||
assert(ok, "file.dat verify false")
|
||||
|
||||
// Now, serialize the signature and read it back
|
||||
sf := fmt.Sprintf("%s/file.sig", dn)
|
||||
err = sig.SerializeFile(sf, "")
|
||||
assert(err == nil, "sig serialize fail")
|
||||
|
||||
s2, err := ReadSignature(sf)
|
||||
assert(err == nil, "file.sig read fail")
|
||||
assert(s2 != nil, "file.sig sig nil")
|
||||
|
||||
assert(byteEq(s2.Sig, sig.Sig), "sig compare fail")
|
||||
|
||||
// If we give a wrong file, verify must fail
|
||||
st, err := os.Stat(zf)
|
||||
assert(err == nil, "file.dat stat fail")
|
||||
|
||||
n := st.Size()
|
||||
assert(n == 8192*8, "file.dat size fail")
|
||||
|
||||
os.Truncate(zf, n-1)
|
||||
|
||||
st, err = os.Stat(zf)
|
||||
assert(err == nil, "file.dat stat2 fail")
|
||||
assert(st.Size() == (n-1), "truncate fail")
|
||||
|
||||
// Now verify this corrupt file
|
||||
ok, err = pk.VerifyFile(zf, sig)
|
||||
assert(err == nil, "file.dat corrupt i/o fail")
|
||||
assert(!ok, "file.dat corrupt verify false")
|
||||
|
||||
os.RemoveAll(dn)
|
||||
}
|
||||
|
||||
|
||||
func Benchmark_Keygen(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = NewKeypair()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func Benchmark_Sig(b *testing.B) {
|
||||
var sizes = [...]uint{
|
||||
16,
|
||||
32,
|
||||
64,
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
kp, _ := NewKeypair()
|
||||
var sig *Signature
|
||||
for _, sz := range sizes {
|
||||
buf := randbuf(sz)
|
||||
s0 := fmt.Sprintf("%d byte sign", sz)
|
||||
s1 := fmt.Sprintf("%d byte verify", sz)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.Run(s0, func (b *testing.B) {
|
||||
sig = benchSign(b, buf, &kp.Sec)
|
||||
})
|
||||
|
||||
b.Run(s1, func (b *testing.B) {
|
||||
benchVerify(b, buf, sig, &kp.Pub)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchSign(b *testing.B, buf []byte, sk *PrivateKey) (sig *Signature) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sig, _ = sk.SignMessage(buf, "")
|
||||
}
|
||||
return sig
|
||||
}
|
||||
|
||||
func benchVerify(b *testing.B, buf []byte, sig *Signature, pk *PublicKey) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
pk.VerifyMessage(buf, sig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func randbuf(sz uint) []byte {
|
||||
b := make([]byte, sz)
|
||||
rand.Read(b)
|
||||
return b
|
||||
}
|
||||
|
||||
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
Loading…
Add table
Reference in a new issue