Better handling of sender verification

* Sender identity is never shared in the encrypted payload
* Sender signs the data-encryption key via Ed25519 if sender-auth is
  desired; else a "signature" of all zeroes is used. In either case, this
  signature is encrypted with the same data-encryption key.
* cleaned up stale code and updated tests
This commit is contained in:
Sudhi Herle 2020-03-23 10:44:40 -07:00
parent 00542dec02
commit e22fae05f7
10 changed files with 324 additions and 521 deletions

View file

@ -138,11 +138,7 @@ recipient can decrypt using their private key.
## Technical Details
### How is the private key protected?
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?
### How is the file encryption done?
The file encryption uses AES-GCM-256 in AEAD mode. The encryption uses
a random 32-byte AES-256 key. This key is mixed in with the header checksum
as a safeguard to protect the header against accidental or malicious corruption.
@ -152,13 +148,19 @@ 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?
`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
is AEAD encrypted with this key-encryption-key. Thus, each recipient
has their own individual encrypted key blob.
`sigtool` uses ephemeral Curve25519 keys to generate shared secrets
between pairs of sender & one or more recipients. This pairwise shared
secret is used as a key-encryption-key (KEK) to encrypt the
data-encryption key in AEAD mode. Thus, each recipient has their own
individual encrypted key blob.
The Ed25519 keys generated by `sigtool` are transformed to their
If the sender authenticates the encryption by providing their secret
key, the data-encryption key is signed via Ed25519 and the signature
is encrypted (using the data-encryption key) and stored in the
header. If the sender opts to not authenticate, a "signature" of all
zeroes is encrypted instead.
The Ed25519 keys generated by `sigtool` or OpenSSH are transformed to their
corresponding Curve25519 points in order to generate the shared secret.
This elliptic co-ordinate transform follows [FiloSottile's writeup][2].
@ -180,18 +182,11 @@ described as a protobuf file (sign/hdr.proto):
```protobuf
message header {
uint32 chunk_size = 1;
bytes salt = 2;
bytes pk = 3; // sender's ephemeral curve PK
sender sender_pk = 4; // sender's encrypted ed25519 PK
repeated wrapped_key keys = 5;
}
/*
* Sender info is wrapped using the data encryption key
*/
message sender {
bytes pk = 1;
uint32 chunk_size = 1;
bytes salt = 2;
bytes pk = 3; // sender's ephemeral curve PK
bytes sender_sig = 4; // ed25519 signature of the key
repeated wrapped_key keys = 5;
}
/*
@ -199,7 +194,7 @@ described as a protobuf file (sign/hdr.proto):
* key. WrappedKey describes such a wrapped key.
*/
message wrapped_key {
bytes key = 2;
bytes d_key = 1;
}
```
@ -220,6 +215,11 @@ computed.
The chunk data and AEAD tag are treated as an atomic unit for AEAD
decryption.
### How is the private key protected?
The Ed25519 private key is encrypted in AES-GCM-256 mode using a key
derived from the user's pass-phrase.
## Understanding the Code
The core logic is in `src/sign`: it is a library that exposes all the
functionality: key generation, key parsing, signing, encryption, decryption
@ -230,7 +230,7 @@ etc.
* `src/keys.go` contains key generation, serialization, de-serialization
* `src/ssh.go` contains code to parse SSH Ed25519 key files
* `src/stream.go` contains code that provides an `io.Reader` and `io.WriteCloser` interface
for encryption and decryption.
for encryption and decryption.
The generated keys and signatures are proper YAML files and human
readable.

View file

@ -43,7 +43,7 @@ func encrypt(args []string) {
fs.StringVarP(&keyfile, "sign", "s", "", "Sign using private key `S`")
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for passphrase to decrypt the private key")
fs.StringVarP(&envpw, "env-password", "", "", "Use passphrase from environment variable `E`")
fs.SizeVarP(&blksize, "block-size", "B", 128 * 1024, "Use `S` as the encryption block size")
fs.SizeVarP(&blksize, "block-size", "B", 128*1024, "Use `S` as the encryption block size")
err := fs.Parse(args)
if err != nil {
@ -264,7 +264,7 @@ func decrypt(args []string) {
infd = inf
}
}
if test {
outfd = &nullWriter{}
} else if len(outfile) > 0 && outfile != "-" {
@ -298,6 +298,14 @@ func decrypt(args []string) {
die("%s", err)
}
if pk == nil && d.AuthenticatedSender() {
var fn string = infile
if len(fn) == 0 || fn == "-" {
fn = "<stdin>"
}
warn("%s: Missing sender Public Key; can't authenticate sender ..", fn)
}
err = d.Decrypt(outfd)
if err != nil {
die("%s", err)

View file

@ -10,7 +10,6 @@
It has these top-level messages:
Header
Sender
WrappedKey
*/
package pb
@ -43,11 +42,11 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
// decoded version of this information. It is encoded in
// protobuf format before writing to disk.
type Header struct {
ChunkSize uint32 `protobuf:"varint,1,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,omitempty"`
Salt []byte `protobuf:"bytes,2,opt,name=salt,proto3" json:"salt,omitempty"`
Pk []byte `protobuf:"bytes,3,opt,name=pk,proto3" json:"pk,omitempty"`
SenderPk *Sender `protobuf:"bytes,4,opt,name=sender_pk,json=senderPk" json:"sender_pk,omitempty"`
Keys []*WrappedKey `protobuf:"bytes,5,rep,name=keys" json:"keys,omitempty"`
ChunkSize uint32 `protobuf:"varint,1,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,omitempty"`
Salt []byte `protobuf:"bytes,2,opt,name=salt,proto3" json:"salt,omitempty"`
Pk []byte `protobuf:"bytes,3,opt,name=pk,proto3" json:"pk,omitempty"`
SenderSign []byte `protobuf:"bytes,4,opt,name=sender_sign,json=senderSign,proto3" json:"sender_sign,omitempty"`
Keys []*WrappedKey `protobuf:"bytes,5,rep,name=keys" json:"keys,omitempty"`
}
func (m *Header) Reset() { *m = Header{} }
@ -75,9 +74,9 @@ func (m *Header) GetPk() []byte {
return nil
}
func (m *Header) GetSenderPk() *Sender {
func (m *Header) GetSenderSign() []byte {
if m != nil {
return m.SenderPk
return m.SenderSign
}
return nil
}
@ -89,44 +88,26 @@ func (m *Header) GetKeys() []*WrappedKey {
return nil
}
//
// Sender info is wrapped using the data encryption key
type Sender struct {
Pk []byte `protobuf:"bytes,1,opt,name=pk,proto3" json:"pk,omitempty"`
}
func (m *Sender) Reset() { *m = Sender{} }
func (*Sender) ProtoMessage() {}
func (*Sender) Descriptor() ([]byte, []int) { return fileDescriptorHdr, []int{1} }
func (m *Sender) GetPk() []byte {
if m != nil {
return m.Pk
}
return nil
}
//
// A file encryption key is wrapped by a recipient specific public
// key. WrappedKey describes such a wrapped key.
type WrappedKey struct {
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
DKey []byte `protobuf:"bytes,1,opt,name=d_key,json=dKey,proto3" json:"d_key,omitempty"`
}
func (m *WrappedKey) Reset() { *m = WrappedKey{} }
func (*WrappedKey) ProtoMessage() {}
func (*WrappedKey) Descriptor() ([]byte, []int) { return fileDescriptorHdr, []int{2} }
func (*WrappedKey) Descriptor() ([]byte, []int) { return fileDescriptorHdr, []int{1} }
func (m *WrappedKey) GetKey() []byte {
func (m *WrappedKey) GetDKey() []byte {
if m != nil {
return m.Key
return m.DKey
}
return nil
}
func init() {
proto.RegisterType((*Header)(nil), "pb.header")
proto.RegisterType((*Sender)(nil), "pb.sender")
proto.RegisterType((*WrappedKey)(nil), "pb.wrapped_key")
}
func (this *Header) Equal(that interface{}) bool {
@ -163,7 +144,7 @@ func (this *Header) Equal(that interface{}) bool {
if !bytes.Equal(this.Pk, that1.Pk) {
return false
}
if !this.SenderPk.Equal(that1.SenderPk) {
if !bytes.Equal(this.SenderSign, that1.SenderSign) {
return false
}
if len(this.Keys) != len(that1.Keys) {
@ -176,36 +157,6 @@ func (this *Header) Equal(that interface{}) bool {
}
return true
}
func (this *Sender) Equal(that interface{}) bool {
if that == nil {
if this == nil {
return true
}
return false
}
that1, ok := that.(*Sender)
if !ok {
that2, ok := that.(Sender)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
if this == nil {
return true
}
return false
} else if this == nil {
return false
}
if !bytes.Equal(this.Pk, that1.Pk) {
return false
}
return true
}
func (this *WrappedKey) Equal(that interface{}) bool {
if that == nil {
if this == nil {
@ -231,7 +182,7 @@ func (this *WrappedKey) Equal(that interface{}) bool {
} else if this == nil {
return false
}
if !bytes.Equal(this.Key, that1.Key) {
if !bytes.Equal(this.DKey, that1.DKey) {
return false
}
return true
@ -245,32 +196,20 @@ func (this *Header) GoString() string {
s = append(s, "ChunkSize: "+fmt.Sprintf("%#v", this.ChunkSize)+",\n")
s = append(s, "Salt: "+fmt.Sprintf("%#v", this.Salt)+",\n")
s = append(s, "Pk: "+fmt.Sprintf("%#v", this.Pk)+",\n")
if this.SenderPk != nil {
s = append(s, "SenderPk: "+fmt.Sprintf("%#v", this.SenderPk)+",\n")
}
s = append(s, "SenderSign: "+fmt.Sprintf("%#v", this.SenderSign)+",\n")
if this.Keys != nil {
s = append(s, "Keys: "+fmt.Sprintf("%#v", this.Keys)+",\n")
}
s = append(s, "}")
return strings.Join(s, "")
}
func (this *Sender) GoString() string {
if this == nil {
return "nil"
}
s := make([]string, 0, 5)
s = append(s, "&pb.Sender{")
s = append(s, "Pk: "+fmt.Sprintf("%#v", this.Pk)+",\n")
s = append(s, "}")
return strings.Join(s, "")
}
func (this *WrappedKey) GoString() string {
if this == nil {
return "nil"
}
s := make([]string, 0, 5)
s = append(s, "&pb.WrappedKey{")
s = append(s, "Key: "+fmt.Sprintf("%#v", this.Key)+",\n")
s = append(s, "DKey: "+fmt.Sprintf("%#v", this.DKey)+",\n")
s = append(s, "}")
return strings.Join(s, "")
}
@ -314,15 +253,11 @@ func (m *Header) MarshalTo(dAtA []byte) (int, error) {
i = encodeVarintHdr(dAtA, i, uint64(len(m.Pk)))
i += copy(dAtA[i:], m.Pk)
}
if m.SenderPk != nil {
if len(m.SenderSign) > 0 {
dAtA[i] = 0x22
i++
i = encodeVarintHdr(dAtA, i, uint64(m.SenderPk.Size()))
n1, err := m.SenderPk.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n1
i = encodeVarintHdr(dAtA, i, uint64(len(m.SenderSign)))
i += copy(dAtA[i:], m.SenderSign)
}
if len(m.Keys) > 0 {
for _, msg := range m.Keys {
@ -339,30 +274,6 @@ func (m *Header) MarshalTo(dAtA []byte) (int, error) {
return i, nil
}
func (m *Sender) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Sender) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Pk) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintHdr(dAtA, i, uint64(len(m.Pk)))
i += copy(dAtA[i:], m.Pk)
}
return i, nil
}
func (m *WrappedKey) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@ -378,11 +289,11 @@ func (m *WrappedKey) MarshalTo(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.Key) > 0 {
dAtA[i] = 0x12
if len(m.DKey) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintHdr(dAtA, i, uint64(len(m.Key)))
i += copy(dAtA[i:], m.Key)
i = encodeVarintHdr(dAtA, i, uint64(len(m.DKey)))
i += copy(dAtA[i:], m.DKey)
}
return i, nil
}
@ -428,8 +339,8 @@ func (m *Header) Size() (n int) {
if l > 0 {
n += 1 + l + sovHdr(uint64(l))
}
if m.SenderPk != nil {
l = m.SenderPk.Size()
l = len(m.SenderSign)
if l > 0 {
n += 1 + l + sovHdr(uint64(l))
}
if len(m.Keys) > 0 {
@ -441,20 +352,10 @@ func (m *Header) Size() (n int) {
return n
}
func (m *Sender) Size() (n int) {
var l int
_ = l
l = len(m.Pk)
if l > 0 {
n += 1 + l + sovHdr(uint64(l))
}
return n
}
func (m *WrappedKey) Size() (n int) {
var l int
_ = l
l = len(m.Key)
l = len(m.DKey)
if l > 0 {
n += 1 + l + sovHdr(uint64(l))
}
@ -482,28 +383,18 @@ func (this *Header) String() string {
`ChunkSize:` + fmt.Sprintf("%v", this.ChunkSize) + `,`,
`Salt:` + fmt.Sprintf("%v", this.Salt) + `,`,
`Pk:` + fmt.Sprintf("%v", this.Pk) + `,`,
`SenderPk:` + strings.Replace(fmt.Sprintf("%v", this.SenderPk), "Sender", "Sender", 1) + `,`,
`SenderSign:` + fmt.Sprintf("%v", this.SenderSign) + `,`,
`Keys:` + strings.Replace(fmt.Sprintf("%v", this.Keys), "WrappedKey", "WrappedKey", 1) + `,`,
`}`,
}, "")
return s
}
func (this *Sender) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&Sender{`,
`Pk:` + fmt.Sprintf("%v", this.Pk) + `,`,
`}`,
}, "")
return s
}
func (this *WrappedKey) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&WrappedKey{`,
`Key:` + fmt.Sprintf("%v", this.Key) + `,`,
`DKey:` + fmt.Sprintf("%v", this.DKey) + `,`,
`}`,
}, "")
return s
@ -628,9 +519,9 @@ func (m *Header) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SenderPk", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field SenderSign", wireType)
}
var msglen int
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowHdr
@ -640,23 +531,21 @@ func (m *Header) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
byteLen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
if byteLen < 0 {
return ErrInvalidLengthHdr
}
postIndex := iNdEx + msglen
postIndex := iNdEx + byteLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.SenderPk == nil {
m.SenderPk = &Sender{}
}
if err := m.SenderPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
m.SenderSign = append(m.SenderSign[:0], dAtA[iNdEx:postIndex]...)
if m.SenderSign == nil {
m.SenderSign = []byte{}
}
iNdEx = postIndex
case 5:
@ -711,87 +600,6 @@ func (m *Header) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *Sender) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowHdr
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: sender: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: sender: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Pk", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowHdr
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthHdr
}
postIndex := iNdEx + byteLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Pk = append(m.Pk[:0], dAtA[iNdEx:postIndex]...)
if m.Pk == nil {
m.Pk = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipHdr(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthHdr
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WrappedKey) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@ -821,9 +629,9 @@ func (m *WrappedKey) Unmarshal(dAtA []byte) error {
return fmt.Errorf("proto: wrapped_key: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 2:
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field DKey", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
@ -847,9 +655,9 @@ func (m *WrappedKey) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
if m.Key == nil {
m.Key = []byte{}
m.DKey = append(m.DKey[:0], dAtA[iNdEx:postIndex]...)
if m.DKey == nil {
m.DKey = []byte{}
}
iNdEx = postIndex
default:
@ -981,22 +789,21 @@ var (
func init() { proto.RegisterFile("internal/pb/hdr.proto", fileDescriptorHdr) }
var fileDescriptorHdr = []byte{
// 265 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x90, 0x4f, 0x4a, 0xc3, 0x50,
0x10, 0xc6, 0x33, 0x69, 0x0d, 0x76, 0xe2, 0x3f, 0x1e, 0x08, 0x6f, 0xe3, 0x18, 0xe2, 0xc2, 0x2c,
0x24, 0x85, 0x7a, 0x03, 0x4f, 0x20, 0xf1, 0x00, 0x21, 0x31, 0x03, 0x09, 0xaf, 0xa4, 0x8f, 0x97,
0x88, 0xa4, 0x2b, 0x8f, 0xe0, 0xd6, 0x1b, 0x78, 0x14, 0x97, 0x5d, 0xba, 0x34, 0xcf, 0x8d, 0xcb,
0x1e, 0x41, 0x9a, 0x54, 0x70, 0x35, 0x1f, 0xbf, 0xf9, 0x86, 0x6f, 0xf8, 0xf0, 0xbc, 0xaa, 0x5b,
0x36, 0x75, 0xb6, 0x9c, 0xeb, 0x7c, 0x5e, 0x16, 0x26, 0xd6, 0x66, 0xd5, 0xae, 0x84, 0xab, 0xf3,
0xf0, 0x0d, 0xd0, 0x2b, 0x39, 0x2b, 0xd8, 0x88, 0x0b, 0xc4, 0xc7, 0xf2, 0xa9, 0x56, 0x69, 0x53,
0xad, 0x59, 0x42, 0x00, 0xd1, 0x71, 0x32, 0x1b, 0xc8, 0x43, 0xb5, 0x66, 0x21, 0x70, 0xda, 0x64,
0xcb, 0x56, 0xba, 0x01, 0x44, 0x47, 0xc9, 0xa0, 0xc5, 0x09, 0xba, 0x5a, 0xc9, 0xc9, 0x40, 0x5c,
0xad, 0xc4, 0x35, 0xce, 0x1a, 0xae, 0x0b, 0x36, 0xa9, 0x56, 0x72, 0x1a, 0x40, 0xe4, 0x2f, 0x30,
0xd6, 0x79, 0x3c, 0xc2, 0xe4, 0x70, 0x9c, 0xf7, 0x4a, 0x5c, 0xe1, 0x54, 0x71, 0xd7, 0xc8, 0x83,
0x60, 0x12, 0xf9, 0x8b, 0xd3, 0x9d, 0xe7, 0xd9, 0x64, 0x5a, 0x73, 0x91, 0x2a, 0xee, 0x92, 0x61,
0x19, 0x4a, 0xf4, 0xc6, 0x83, 0x7d, 0x0e, 0xfc, 0xe5, 0x84, 0x97, 0xe8, 0xff, 0xb3, 0x8b, 0x33,
0x9c, 0x28, 0xee, 0xf6, 0x9f, 0xed, 0xe4, 0xdd, 0xcd, 0xa6, 0x27, 0xe7, 0xb3, 0x27, 0x67, 0xdb,
0x13, 0xbc, 0x58, 0x82, 0x77, 0x4b, 0xf0, 0x61, 0x09, 0x36, 0x96, 0xe0, 0xcb, 0x12, 0xfc, 0x58,
0x72, 0xb6, 0x96, 0xe0, 0xf5, 0x9b, 0x9c, 0xdc, 0x1b, 0xfa, 0xb8, 0xfd, 0x0d, 0x00, 0x00, 0xff,
0xff, 0x7f, 0xa8, 0x12, 0x55, 0x28, 0x01, 0x00, 0x00,
// 252 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0xcf, 0x31, 0x4e, 0xeb, 0x40,
0x10, 0xc6, 0x71, 0x8f, 0xe3, 0x44, 0x7a, 0xe3, 0x3c, 0x90, 0x16, 0x21, 0xb9, 0x61, 0xb0, 0x4c,
0xe3, 0x02, 0x39, 0x12, 0xdc, 0x80, 0x96, 0xce, 0x39, 0x80, 0x65, 0xe3, 0x51, 0x6c, 0x6d, 0xb4,
0x59, 0xed, 0x1a, 0x21, 0xa7, 0xe2, 0x08, 0x70, 0x0b, 0x8e, 0x42, 0x99, 0x92, 0x12, 0x2f, 0x0d,
0x65, 0x8e, 0x80, 0xb4, 0x34, 0x74, 0x9f, 0x7e, 0xff, 0x66, 0x06, 0xcf, 0x7b, 0x35, 0xb0, 0x51,
0xf5, 0x76, 0xa5, 0x9b, 0x55, 0xd7, 0x9a, 0x42, 0x9b, 0xdd, 0xb0, 0x13, 0xa1, 0x6e, 0xb2, 0x57,
0xc0, 0x45, 0xc7, 0x75, 0xcb, 0x46, 0x5c, 0x20, 0x3e, 0x74, 0x8f, 0x4a, 0x56, 0xb6, 0xdf, 0x73,
0x02, 0x29, 0xe4, 0xff, 0xcb, 0x7f, 0x5e, 0xd6, 0xfd, 0x9e, 0x85, 0xc0, 0xc8, 0xd6, 0xdb, 0x21,
0x09, 0x53, 0xc8, 0x97, 0xa5, 0xdf, 0xe2, 0x04, 0x43, 0x2d, 0x93, 0x99, 0x97, 0x50, 0x4b, 0x71,
0x89, 0xb1, 0x65, 0xd5, 0xb2, 0xa9, 0x6c, 0xbf, 0x51, 0x49, 0xe4, 0x03, 0xfe, 0xd2, 0xba, 0xdf,
0x28, 0x71, 0x85, 0x91, 0xe4, 0xd1, 0x26, 0xf3, 0x74, 0x96, 0xc7, 0x37, 0xa7, 0x85, 0x6e, 0x8a,
0x27, 0x53, 0x6b, 0xcd, 0x6d, 0x25, 0x79, 0x2c, 0x7d, 0xcc, 0x32, 0x8c, 0xff, 0xa0, 0x38, 0xc3,
0xb9, 0x1f, 0xfe, 0xa4, 0x65, 0x19, 0xb5, 0xf7, 0x3c, 0xde, 0x5d, 0x1f, 0x26, 0x0a, 0x3e, 0x26,
0x0a, 0x8e, 0x13, 0xc1, 0xb3, 0x23, 0x78, 0x73, 0x04, 0xef, 0x8e, 0xe0, 0xe0, 0x08, 0x3e, 0x1d,
0xc1, 0xb7, 0xa3, 0xe0, 0xe8, 0x08, 0x5e, 0xbe, 0x28, 0x68, 0x16, 0xfe, 0xe1, 0xdb, 0x9f, 0x00,
0x00, 0x00, 0xff, 0xff, 0x88, 0x3d, 0x22, 0x56, 0x09, 0x01, 0x00, 0x00,
}

View file

@ -16,18 +16,11 @@ package pb;
* protobuf format before writing to disk.
*/
message header {
uint32 chunk_size = 1;
bytes salt = 2;
bytes pk = 3; // sender's ephemeral curve PK
sender sender_pk = 4; // sender's encrypted ed25519 PK
repeated wrapped_key keys = 5;
}
/*
* Sender info is wrapped using the data encryption key
*/
message sender {
bytes pk = 1;
uint32 chunk_size = 1; // encryption block size
bytes salt = 2; // master salt (nonces are derived from this)
bytes pk = 3; // ephemeral curve PK
bytes sender_sign = 4; // signature block of sender
repeated wrapped_key keys = 5; // list of wrapped receiver blocks
}
/*
@ -35,5 +28,5 @@ message sender {
* key. WrappedKey describes such a wrapped key.
*/
message wrapped_key {
bytes key = 2;
bytes d_key = 1; // encrypted data key
}

View file

@ -1,97 +0,0 @@
// wrap.go - wrap keys and sender as needed
//
// (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 pb
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
)
const (
WrapReceiverNonce = "Receiver PK"
WrapSenderNonce = "Sender PK"
)
// Wrap sender's PK with the data encryption key
func WrapSenderPK(pk []byte, k, salt []byte) ([]byte, error) {
aes, err := aes.NewCipher(k)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
nonce := MakeNonce([]byte(WrapSenderNonce), salt)
buf := make([]byte, ae.Overhead()+len(pk))
out := ae.Seal(buf[:0], nonce[:ae.NonceSize()], pk, nil)
return out, nil
}
// Given a wrapped PK of sender 's', unwrap it using the given key and salt
func (s *Sender) UnwrapPK(k, salt []byte) ([]byte, error) {
aes, err := aes.NewCipher(k)
if err != nil {
return nil, fmt.Errorf("uwrap-sender: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("unwrap-sender: %s", err)
}
nonce := MakeNonce([]byte(WrapSenderNonce), salt)
want := 32 + ae.Overhead()
if len(s.Pk) != want {
return nil, fmt.Errorf("unwrap-sender: incorrect decrypt bytes (need %d, saw %d)", want, 32)
}
out := make([]byte, 32)
pk, err := ae.Open(out[:0], nonce[:ae.NonceSize()], s.Pk, nil)
if err != nil {
return nil, fmt.Errorf("unwrap-sender: %s", err)
}
return pk, nil
}
func MakeNonce(v ...[]byte) []byte {
h := sha256.New()
for _, x := range v {
h.Write(x)
}
return h.Sum(nil)[:]
}
func Clamp(k []byte) []byte {
k[0] &= 248
k[31] &= 127
k[31] |= 64
return k
}
func Randread(b []byte) []byte {
_, err := io.ReadFull(rand.Reader, b)
if err != nil {
panic(fmt.Sprintf("can't read %d bytes of random data: %s", len(b), err))
}
return b
}

View file

@ -56,6 +56,7 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/ed25519"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle"
@ -76,8 +77,12 @@ const (
_Magic = "SigTool"
_MagicLen = len(_Magic)
_AEADNonceLen = 32
_AEADNonceLen = 16
_FixedHdrLen = _MagicLen + 1 + 4
_WrapReceiverNonce = "Receiver Key Nonce"
_WrapSenderNonce = "Sender Sig Nonce"
_EncryptNonce = "Encrypt Nonce"
)
// Encryptor holds the encryption context
@ -87,9 +92,8 @@ type Encryptor struct {
ae cipher.AEAD
// sender ephemeral curve 25519 SK
// the corresponding PK is in Header above
senderSK []byte
// ephemeral key
encSK []byte
started bool
@ -98,9 +102,8 @@ type Encryptor struct {
stream bool
}
// 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.
// Create a new Encryption context for encrypting blocks of size 'blksize'.
// If 'sk' is not nil, authenticate the sender to each receiver.
func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
var blksz uint32
@ -113,7 +116,8 @@ func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
blksz = uint32(blksize)
}
csk, cpk, err := newSender()
// generate ephemeral Curve25519 keys
esk, epk, err := newSender()
if err != nil {
return nil, fmt.Errorf("encrypt: %s", err)
}
@ -121,33 +125,39 @@ func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
key := make([]byte, 32)
salt := make([]byte, _AEADNonceLen)
pb.Randread(key)
pb.Randread(salt)
randRead(key)
randRead(salt)
// if sender has provided their identity to authenticate, we will use their PK
senderPK := cpk
// if sender has provided their identity to authenticate, we sign the data-enc key
// and encrypt the signature. At no point will we send the sender's identity.
var senderSig []byte
if sk != nil {
epk := sk.PublicKey()
senderPK = epk.toCurve25519PK()
sig, err := sk.SignMessage(key, "")
if err != nil {
return nil, fmt.Errorf("encrypt: can't sign: %s", err)
}
senderSig = sig.Sig
} else {
var zero [ed25519.SignatureSize]byte
senderSig = zero[:]
}
wPk, err := pb.WrapSenderPK(senderPK, key, salt)
wSig, err := wrapSenderSig(senderSig, key, salt)
if err != nil {
return nil, fmt.Errorf("encrypt: %s", err)
}
e := &Encryptor{
Header: pb.Header{
ChunkSize: blksz,
Salt: salt,
Pk: cpk,
SenderPk: &pb.Sender{
Pk: wPk,
},
ChunkSize: blksz,
Salt: salt,
Pk: epk,
SenderSign: wSig,
},
key: key,
senderSK: csk,
key: key,
encSK: esk,
}
return e, nil
@ -159,7 +169,7 @@ func (e *Encryptor) AddRecipient(pk *PublicKey) error {
return fmt.Errorf("encrypt: can't add new recipient after encryption has started")
}
w, err := wrapKey(pk, e.key, e.senderSK, e.Salt)
w, err := e.wrapKey(pk)
if err == nil {
e.Keys = append(e.Keys, w)
}
@ -241,7 +251,7 @@ func (e *Encryptor) start(wr io.Writer) error {
// we mix the header checksum to create the encryption key
h = sha256.New()
h.Write([]byte("Encrypt Nonce"))
h.Write([]byte(_EncryptNonce))
h.Write(e.key)
h.Write(sumHdr)
key := h.Sum(nil)
@ -286,7 +296,7 @@ func fullwrite(buf []byte, wr io.Writer) error {
// additional data in the AEAD construction.
func (e *Encryptor) encrypt(buf []byte, wr io.Writer, i uint32, eof bool) error {
var b [8]byte
var noncebuf [32]byte
var nonceb [32]byte
var z uint32 = uint32(len(buf))
// mark last block
@ -300,7 +310,7 @@ func (e *Encryptor) encrypt(buf []byte, wr io.Writer, i uint32, eof bool) error
h := sha256.New()
h.Write(e.Salt)
h.Write(b[:])
nonce := h.Sum(noncebuf[:0])
nonce := h.Sum(nonceb[:0])[:e.ae.NonceSize()]
copy(e.buf[:4], b[:4])
cbuf := e.buf[4:]
@ -324,6 +334,9 @@ type Decryptor struct {
buf []byte
hdrsum []byte
// flag set to true if sender signed the key
auth bool
// Decrypted key
key []byte
eof bool
@ -401,7 +414,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
// sanity check on the wrapped keys
for i, w := range d.Keys {
if len(w.Key) <= 32+12 {
if len(w.DKey) <= 32 {
return nil, fmt.Errorf("decrypt: wrapped key %d: wrong-size encrypted key", i)
}
}
@ -416,7 +429,7 @@ func (d *Decryptor) SetPrivateKey(sk *PrivateKey, senderPk *PublicKey) error {
var key []byte
for i, w := range d.Keys {
key, err = unwrapKey(w.Key, sk, d.Pk, d.Salt)
key, err = d.unwrapKey(w, sk)
if err != nil {
return fmt.Errorf("decrypt: can't unwrap key %d: %s", i, err)
}
@ -428,25 +441,15 @@ func (d *Decryptor) SetPrivateKey(sk *PrivateKey, senderPk *PublicKey) error {
return fmt.Errorf("decrypt: wrong key")
havekey:
if senderPk != nil {
hpk, err := d.SenderPk.UnwrapPK(key, d.Salt)
if err != nil {
return fmt.Errorf("decrypt: can't unwrap sender PK: %s", err)
}
cpk := senderPk.toCurve25519PK()
if subtle.ConstantTimeCompare(cpk, hpk) == 0 {
return fmt.Errorf("decrypt: sender verification failed")
}
if err := d.verifySender(key, sk, senderPk); err != nil {
return fmt.Errorf("decrypt: %s", err)
}
// XXX do we need to verify d.Header.Sender.Key vs. d.Header.PK?
d.key = key
// we mix the header checksum into the key
h := sha256.New()
h.Write([]byte("Encrypt Nonce"))
h.Write([]byte(_EncryptNonce))
h.Write(d.key)
h.Write(d.hdrsum)
key = h.Sum(nil)
@ -464,72 +467,10 @@ havekey:
return nil
}
// Wrap data encryption key 'k' with the sender's PK and our ephemeral curve SK
func wrapKey(pk *PublicKey, k, ourSK, salt []byte) (*pb.WrappedKey, error) {
shared, err := curve25519.X25519(ourSK, pk.toCurve25519PK())
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
aes, err := aes.NewCipher(shared)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
tagsize := ae.Overhead()
nonce := pb.MakeNonce([]byte(pb.WrapReceiverNonce), salt)
buf := make([]byte, tagsize+len(shared))
out := ae.Seal(buf[:0], nonce[:ae.NonceSize()], k, pk.Pk)
return &pb.WrappedKey{
Key: out,
}, nil
}
// Unwrap a wrapped key using the receivers Ed25519 secret key 'sk' and
// senders ephemeral PublicKey
func unwrapKey(wkey []byte, sk *PrivateKey, curvePK, salt []byte) ([]byte, error) {
ourSK := sk.toCurve25519SK()
shared, err := curve25519.X25519(ourSK, curvePK)
if err != nil {
return nil, fmt.Errorf("unwrap: %s", err)
}
aes, err := aes.NewCipher(shared)
if err != nil {
return nil, fmt.Errorf("unwrap: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("unwrap: %s", err)
}
want := 32 + ae.Overhead()
if len(wkey) != want {
return nil, fmt.Errorf("unwrap: incorrect decrypt bytes (need %d, saw %d)", want, len(wkey))
}
nonce := pb.MakeNonce([]byte(pb.WrapReceiverNonce), salt)
pk := sk.PublicKey()
out := make([]byte, 32)
c, err := ae.Open(out[:0], nonce[:ae.NonceSize()], wkey, pk.Pk)
// we indicate incorrect receiver SK by returning a nil key
if err != nil {
return nil, nil
}
return c, nil
}
// Return a list of Wrapped keys in the encrypted file header
func (d *Decryptor) WrappedKeys() []*pb.WrappedKey {
return d.Keys
// AuthenticatedSender returns true if the sender authenticated themselves
// (the data-encryption key is signed).
func (d *Decryptor) AuthenticatedSender() bool {
return d.auth
}
// Decrypt the file and write to 'wr'
@ -567,6 +508,143 @@ func (d *Decryptor) Decrypt(wr io.Writer) error {
return nil
}
// Wrap sender's signature of the encryption key
func wrapSenderSig(sig []byte, key, salt []byte) ([]byte, error) {
aes, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
tagsize := ae.Overhead()
nonceSize := ae.NonceSize()
nonce := makeNonce([]byte(_WrapSenderNonce), salt)[:nonceSize]
esig := make([]byte, tagsize+len(sig))
return ae.Seal(esig[:0], nonce, sig, nil), nil
}
// unwrap sender's signature using 'key' and extract the signature
// Optionally, verify the signature using the sender's PK (if provided).
func (d *Decryptor) verifySender(key []byte, sk *PrivateKey, senderPK *PublicKey) error {
aes, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("unwrap: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return fmt.Errorf("unwrap: %s", err)
}
nonceSize := ae.NonceSize()
nonce := makeNonce([]byte(_WrapSenderNonce), d.Salt)[:nonceSize]
sig := make([]byte, ed25519.SignatureSize)
sig, err = ae.Open(sig[:0], nonce, d.SenderSign, nil)
if err != nil {
return fmt.Errorf("unwrap: can't open sender info: %s", err)
}
var zero [ed25519.SignatureSize]byte
// Did the sender actually sign anything?
if subtle.ConstantTimeCompare(zero[:], sig) == 0 {
d.auth = true
if senderPK != nil {
ss := &Signature{
Sig: sig,
}
ok := senderPK.VerifyMessage(key, ss)
if !ok {
return fmt.Errorf("unwrap: sender verification failed")
}
}
}
return nil
}
// Wrap data encryption key 'k' with the sender's PK and our ephemeral curve SK
// basically, we do two scalarmults:
// a) Ephemeral encryption/decryption SK x receiver PK
// b) Sender's SK x receiver PK
func (e *Encryptor) wrapKey(pk *PublicKey) (*pb.WrappedKey, error) {
rxPK := pk.toCurve25519PK()
dkek, err := curve25519.X25519(e.encSK, rxPK)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
aes, err := aes.NewCipher(dkek)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("wrap: %s", err)
}
tagsize := ae.Overhead()
nonceSize := ae.NonceSize()
nonceR := makeNonce([]byte(_WrapReceiverNonce), e.Salt)[:nonceSize]
ekey := make([]byte, tagsize+len(e.key))
w := &pb.WrappedKey{
DKey: ae.Seal(ekey[:0], nonceR, e.key, pk.Pk),
}
return w, nil
}
// Unwrap a wrapped key using the receivers Ed25519 secret key 'sk' and
// senders ephemeral PublicKey
func (d *Decryptor) unwrapKey(w *pb.WrappedKey, sk *PrivateKey) ([]byte, error) {
ourSK := sk.toCurve25519SK()
dkek, err := curve25519.X25519(ourSK, d.Pk)
if err != nil {
return nil, fmt.Errorf("unwrap: %s", err)
}
aes, err := aes.NewCipher(dkek)
if err != nil {
return nil, fmt.Errorf("unwrap: %s", err)
}
ae, err := cipher.NewGCM(aes)
if err != nil {
return nil, fmt.Errorf("unwrap: %s", err)
}
// 32 == AES-256 key size
want := 32 + ae.Overhead()
if len(w.DKey) != want {
return nil, fmt.Errorf("unwrap: incorrect decrypt bytes (need %d, saw %d)", want, len(w.DKey))
}
nonceSize := ae.NonceSize()
nonceR := makeNonce([]byte(_WrapReceiverNonce), d.Salt)[:nonceSize]
pk := sk.PublicKey()
dkey := make([]byte, 32) // decrypted data decryption key
// we indicate incorrect receiver SK by returning a nil key
dkey, err = ae.Open(dkey[:0], nonceR, w.DKey, pk.Pk)
if err != nil {
return nil, nil
}
return dkey, nil
}
// Decrypt exactly one chunk of data
func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
var b [8]byte
@ -605,7 +683,7 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
h := sha256.New()
h.Write(d.Salt)
h.Write(b[:])
nonce := h.Sum(nonceb[:0])
nonce := h.Sum(nonceb[:0])[:d.ae.NonceSize()]
z := m + ovh
n, err = io.ReadFull(d.rd, d.buf[:z])
@ -632,9 +710,19 @@ func expand(shared, pk []byte) ([]byte, error) {
func newSender() (sk, pk []byte, err error) {
var csk [32]byte
pb.Randread(csk[:])
pb.Clamp(csk[:])
randRead(csk[:])
clamp(csk[:])
pk, err = curve25519.X25519(csk[:], curve25519.Basepoint)
sk = csk[:]
return
}
func makeNonce(v ...[]byte) []byte {
h := sha256.New()
for _, x := range v {
h.Write(x)
}
return h.Sum(nil)[:]
}
// EOF

View file

@ -28,6 +28,7 @@ import (
"encoding/binary"
"fmt"
"hash"
"io"
"io/ioutil"
"math/big"
"os"
@ -37,7 +38,6 @@ import (
"gopkg.in/yaml.v2"
"github.com/opencoff/go-utils"
"github.com/opencoff/sigtool/internal/pb"
)
// Private Ed25519 key
@ -70,12 +70,6 @@ type Keypair struct {
Pub PublicKey
}
// An Ed25519 Signature
type Signature struct {
Sig []byte // Ed25519 sig bytes
pkhash []byte // [0:16] SHA256 hash of public key needed for verification
}
// Length of Ed25519 Public Key Hash
const PKHashLength = 16
@ -349,7 +343,7 @@ func (sk *PrivateKey) serialize(fn, comment string, getpw func() ([]byte, error)
pass := sha512.Sum512(pw)
salt := make([]byte, 32)
pb.Randread(salt)
randRead(salt)
// "32" == Length of AES-256 key
key, err := scrypt.Key(pass[:], salt, _N, _r, _p, 32)
@ -544,5 +538,13 @@ func clamp(k []byte) []byte {
return k
}
func randRead(b []byte) []byte {
_, err := io.ReadFull(rand.Reader, b)
if err != nil {
panic(fmt.Sprintf("can't read %d bytes of random data: %s", len(b), err))
}
return b
}
// EOF
// vim: noexpandtab:ts=8:sw=8:tw=92:

View file

@ -30,6 +30,12 @@ import (
"gopkg.in/yaml.v2"
)
// An Ed25519 Signature
type Signature struct {
Sig []byte // Ed25519 sig bytes
pkhash []byte // [0:16] SHA256 hash of public key needed for verification
}
// Sign a prehashed Message; return the signature as opaque bytes
// Signature is an YAML file:
// Comment: source file path
@ -147,19 +153,19 @@ func (pk *PublicKey) VerifyFile(fn string, sig *Signature) (bool, error) {
return false, err
}
return pk.VerifyMessage(ck, sig)
return pk.VerifyMessage(ck, sig), nil
}
// 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) {
func (pk *PublicKey) VerifyMessage(ck []byte, sig *Signature) bool {
h := sha512.New()
h.Write([]byte("sigtool signed message"))
h.Write(ck)
ck = h.Sum(nil)[:]
x := Ed.PublicKey(pk.Pk)
return Ed.Verify(x, ck, sig.Sig), nil
return Ed.Verify(x, ck, sig.Sig)
}
// vim: noexpandtab:ts=8:sw=8:tw=92:

View file

@ -19,8 +19,6 @@ import (
"os"
"path"
"testing"
"github.com/opencoff/sigtool/internal/pb"
)
// Return a temp dir in a temp-dir
@ -30,7 +28,7 @@ func tempdir(t *testing.T) string {
var b [10]byte
dn := os.TempDir()
pb.Randread(b[:])
randRead(b[:])
tmp := path.Join(dn, fmt.Sprintf("%x", b[:]))
err := os.MkdirAll(tmp, 0755)
@ -137,7 +135,7 @@ func TestSignRandBuf(t *testing.T) {
var ck [64]byte // simulates sha512 sum
pb.Randread(ck[:])
randRead(ck[:])
pk := &kp.Pub
sk := &kp.Sec
@ -150,17 +148,15 @@ func TestSignRandBuf(t *testing.T) {
assert(ss.IsPKMatch(pk), "pk match fail")
// Corrupt the pkhash and see
pb.Randread(ss.pkhash)
randRead(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")
ok := pk.VerifyMessage(ck[:16], ss)
assert(!ok, "bad ck verify fail")
// proper checksum == should work
ok, err = pk.VerifyMessage(ck[:], ss)
assert(err == nil, "verify err")
ok = pk.VerifyMessage(ck[:], ss)
assert(ok, "verify fail")
// Now sign a file
@ -187,7 +183,7 @@ func TestSignRandBuf(t *testing.T) {
assert(err == nil, "file.dat creat file")
for i := 0; i < 8; i++ {
pb.Randread(buf[:])
randRead(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))
@ -288,7 +284,7 @@ func benchVerify(b *testing.B, buf []byte, sig *Signature, pk *PublicKey) {
func randbuf(sz uint) []byte {
b := make([]byte, sz)
pb.Randread(b)
randRead(b)
return b
}

View file

@ -1 +1 @@
1.0.0
1.1.0