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:
parent
00542dec02
commit
e22fae05f7
10 changed files with 324 additions and 521 deletions
50
README.md
50
README.md
|
@ -138,11 +138,7 @@ recipient can decrypt using their private key.
|
||||||
|
|
||||||
## Technical Details
|
## Technical Details
|
||||||
|
|
||||||
### How is the private key protected?
|
### How is the file encryption done?
|
||||||
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
|
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
|
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.
|
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.
|
the salt, the chunk length and the block number.
|
||||||
|
|
||||||
### What is the public-key cryptography?
|
### What is the public-key cryptography?
|
||||||
`sigtool` uses Curve25519 ECC to generate shared secrets between
|
`sigtool` uses ephemeral Curve25519 keys to generate shared secrets
|
||||||
pairs of sender & recipients. This pairwise shared secret is expanded
|
between pairs of sender & one or more recipients. This pairwise shared
|
||||||
using HKDF to generate a key-encryption-key. The file-encryption key
|
secret is used as a key-encryption-key (KEK) to encrypt the
|
||||||
is AEAD encrypted with this key-encryption-key. Thus, each recipient
|
data-encryption key in AEAD mode. Thus, each recipient has their own
|
||||||
has their own individual encrypted key blob.
|
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.
|
corresponding Curve25519 points in order to generate the shared secret.
|
||||||
This elliptic co-ordinate transform follows [FiloSottile's writeup][2].
|
This elliptic co-ordinate transform follows [FiloSottile's writeup][2].
|
||||||
|
|
||||||
|
@ -180,18 +182,11 @@ described as a protobuf file (sign/hdr.proto):
|
||||||
|
|
||||||
```protobuf
|
```protobuf
|
||||||
message header {
|
message header {
|
||||||
uint32 chunk_size = 1;
|
uint32 chunk_size = 1;
|
||||||
bytes salt = 2;
|
bytes salt = 2;
|
||||||
bytes pk = 3; // sender's ephemeral curve PK
|
bytes pk = 3; // sender's ephemeral curve PK
|
||||||
sender sender_pk = 4; // sender's encrypted ed25519 PK
|
bytes sender_sig = 4; // ed25519 signature of the key
|
||||||
repeated wrapped_key keys = 5;
|
repeated wrapped_key keys = 5;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sender info is wrapped using the data encryption key
|
|
||||||
*/
|
|
||||||
message sender {
|
|
||||||
bytes pk = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -199,7 +194,7 @@ described as a protobuf file (sign/hdr.proto):
|
||||||
* key. WrappedKey describes such a wrapped key.
|
* key. WrappedKey describes such a wrapped key.
|
||||||
*/
|
*/
|
||||||
message 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
|
The chunk data and AEAD tag are treated as an atomic unit for AEAD
|
||||||
decryption.
|
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
|
## Understanding the Code
|
||||||
The core logic is in `src/sign`: it is a library that exposes all the
|
The core logic is in `src/sign`: it is a library that exposes all the
|
||||||
functionality: key generation, key parsing, signing, encryption, decryption
|
functionality: key generation, key parsing, signing, encryption, decryption
|
||||||
|
@ -230,7 +230,7 @@ etc.
|
||||||
* `src/keys.go` contains key generation, serialization, de-serialization
|
* `src/keys.go` contains key generation, serialization, de-serialization
|
||||||
* `src/ssh.go` contains code to parse SSH Ed25519 key files
|
* `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
|
* `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
|
The generated keys and signatures are proper YAML files and human
|
||||||
readable.
|
readable.
|
||||||
|
|
12
crypt.go
12
crypt.go
|
@ -43,7 +43,7 @@ func encrypt(args []string) {
|
||||||
fs.StringVarP(&keyfile, "sign", "s", "", "Sign using private key `S`")
|
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.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.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)
|
err := fs.Parse(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -264,7 +264,7 @@ func decrypt(args []string) {
|
||||||
infd = inf
|
infd = inf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if test {
|
if test {
|
||||||
outfd = &nullWriter{}
|
outfd = &nullWriter{}
|
||||||
} else if len(outfile) > 0 && outfile != "-" {
|
} else if len(outfile) > 0 && outfile != "-" {
|
||||||
|
@ -298,6 +298,14 @@ func decrypt(args []string) {
|
||||||
die("%s", err)
|
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)
|
err = d.Decrypt(outfd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die("%s", err)
|
die("%s", err)
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
|
|
||||||
It has these top-level messages:
|
It has these top-level messages:
|
||||||
Header
|
Header
|
||||||
Sender
|
|
||||||
WrappedKey
|
WrappedKey
|
||||||
*/
|
*/
|
||||||
package pb
|
package pb
|
||||||
|
@ -43,11 +42,11 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
// decoded version of this information. It is encoded in
|
// decoded version of this information. It is encoded in
|
||||||
// protobuf format before writing to disk.
|
// protobuf format before writing to disk.
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ChunkSize uint32 `protobuf:"varint,1,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,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"`
|
Salt []byte `protobuf:"bytes,2,opt,name=salt,proto3" json:"salt,omitempty"`
|
||||||
Pk []byte `protobuf:"bytes,3,opt,name=pk,proto3" json:"pk,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"`
|
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"`
|
Keys []*WrappedKey `protobuf:"bytes,5,rep,name=keys" json:"keys,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Header) Reset() { *m = Header{} }
|
func (m *Header) Reset() { *m = Header{} }
|
||||||
|
@ -75,9 +74,9 @@ func (m *Header) GetPk() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Header) GetSenderPk() *Sender {
|
func (m *Header) GetSenderSign() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.SenderPk
|
return m.SenderSign
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -89,44 +88,26 @@ func (m *Header) GetKeys() []*WrappedKey {
|
||||||
return nil
|
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
|
// A file encryption key is wrapped by a recipient specific public
|
||||||
// key. WrappedKey describes such a wrapped key.
|
// key. WrappedKey describes such a wrapped key.
|
||||||
type WrappedKey struct {
|
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 (m *WrappedKey) Reset() { *m = WrappedKey{} }
|
||||||
func (*WrappedKey) ProtoMessage() {}
|
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 {
|
if m != nil {
|
||||||
return m.Key
|
return m.DKey
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*Header)(nil), "pb.header")
|
proto.RegisterType((*Header)(nil), "pb.header")
|
||||||
proto.RegisterType((*Sender)(nil), "pb.sender")
|
|
||||||
proto.RegisterType((*WrappedKey)(nil), "pb.wrapped_key")
|
proto.RegisterType((*WrappedKey)(nil), "pb.wrapped_key")
|
||||||
}
|
}
|
||||||
func (this *Header) Equal(that interface{}) bool {
|
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) {
|
if !bytes.Equal(this.Pk, that1.Pk) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !this.SenderPk.Equal(that1.SenderPk) {
|
if !bytes.Equal(this.SenderSign, that1.SenderSign) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(this.Keys) != len(that1.Keys) {
|
if len(this.Keys) != len(that1.Keys) {
|
||||||
|
@ -176,36 +157,6 @@ func (this *Header) Equal(that interface{}) bool {
|
||||||
}
|
}
|
||||||
return true
|
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 {
|
func (this *WrappedKey) Equal(that interface{}) bool {
|
||||||
if that == nil {
|
if that == nil {
|
||||||
if this == nil {
|
if this == nil {
|
||||||
|
@ -231,7 +182,7 @@ func (this *WrappedKey) Equal(that interface{}) bool {
|
||||||
} else if this == nil {
|
} else if this == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !bytes.Equal(this.Key, that1.Key) {
|
if !bytes.Equal(this.DKey, that1.DKey) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -245,32 +196,20 @@ func (this *Header) GoString() string {
|
||||||
s = append(s, "ChunkSize: "+fmt.Sprintf("%#v", this.ChunkSize)+",\n")
|
s = append(s, "ChunkSize: "+fmt.Sprintf("%#v", this.ChunkSize)+",\n")
|
||||||
s = append(s, "Salt: "+fmt.Sprintf("%#v", this.Salt)+",\n")
|
s = append(s, "Salt: "+fmt.Sprintf("%#v", this.Salt)+",\n")
|
||||||
s = append(s, "Pk: "+fmt.Sprintf("%#v", this.Pk)+",\n")
|
s = append(s, "Pk: "+fmt.Sprintf("%#v", this.Pk)+",\n")
|
||||||
if this.SenderPk != nil {
|
s = append(s, "SenderSign: "+fmt.Sprintf("%#v", this.SenderSign)+",\n")
|
||||||
s = append(s, "SenderPk: "+fmt.Sprintf("%#v", this.SenderPk)+",\n")
|
|
||||||
}
|
|
||||||
if this.Keys != nil {
|
if this.Keys != nil {
|
||||||
s = append(s, "Keys: "+fmt.Sprintf("%#v", this.Keys)+",\n")
|
s = append(s, "Keys: "+fmt.Sprintf("%#v", this.Keys)+",\n")
|
||||||
}
|
}
|
||||||
s = append(s, "}")
|
s = append(s, "}")
|
||||||
return strings.Join(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 {
|
func (this *WrappedKey) GoString() string {
|
||||||
if this == nil {
|
if this == nil {
|
||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
s := make([]string, 0, 5)
|
s := make([]string, 0, 5)
|
||||||
s = append(s, "&pb.WrappedKey{")
|
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, "}")
|
s = append(s, "}")
|
||||||
return strings.Join(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 = encodeVarintHdr(dAtA, i, uint64(len(m.Pk)))
|
||||||
i += copy(dAtA[i:], m.Pk)
|
i += copy(dAtA[i:], m.Pk)
|
||||||
}
|
}
|
||||||
if m.SenderPk != nil {
|
if len(m.SenderSign) > 0 {
|
||||||
dAtA[i] = 0x22
|
dAtA[i] = 0x22
|
||||||
i++
|
i++
|
||||||
i = encodeVarintHdr(dAtA, i, uint64(m.SenderPk.Size()))
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.SenderSign)))
|
||||||
n1, err := m.SenderPk.MarshalTo(dAtA[i:])
|
i += copy(dAtA[i:], m.SenderSign)
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
i += n1
|
|
||||||
}
|
}
|
||||||
if len(m.Keys) > 0 {
|
if len(m.Keys) > 0 {
|
||||||
for _, msg := range m.Keys {
|
for _, msg := range m.Keys {
|
||||||
|
@ -339,30 +274,6 @@ func (m *Header) MarshalTo(dAtA []byte) (int, error) {
|
||||||
return i, nil
|
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) {
|
func (m *WrappedKey) Marshal() (dAtA []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
dAtA = make([]byte, size)
|
dAtA = make([]byte, size)
|
||||||
|
@ -378,11 +289,11 @@ func (m *WrappedKey) MarshalTo(dAtA []byte) (int, error) {
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
if len(m.Key) > 0 {
|
if len(m.DKey) > 0 {
|
||||||
dAtA[i] = 0x12
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintHdr(dAtA, i, uint64(len(m.Key)))
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.DKey)))
|
||||||
i += copy(dAtA[i:], m.Key)
|
i += copy(dAtA[i:], m.DKey)
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
@ -428,8 +339,8 @@ func (m *Header) Size() (n int) {
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovHdr(uint64(l))
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
}
|
}
|
||||||
if m.SenderPk != nil {
|
l = len(m.SenderSign)
|
||||||
l = m.SenderPk.Size()
|
if l > 0 {
|
||||||
n += 1 + l + sovHdr(uint64(l))
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
}
|
}
|
||||||
if len(m.Keys) > 0 {
|
if len(m.Keys) > 0 {
|
||||||
|
@ -441,20 +352,10 @@ func (m *Header) Size() (n int) {
|
||||||
return n
|
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) {
|
func (m *WrappedKey) Size() (n int) {
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
l = len(m.Key)
|
l = len(m.DKey)
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovHdr(uint64(l))
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
}
|
}
|
||||||
|
@ -482,28 +383,18 @@ func (this *Header) String() string {
|
||||||
`ChunkSize:` + fmt.Sprintf("%v", this.ChunkSize) + `,`,
|
`ChunkSize:` + fmt.Sprintf("%v", this.ChunkSize) + `,`,
|
||||||
`Salt:` + fmt.Sprintf("%v", this.Salt) + `,`,
|
`Salt:` + fmt.Sprintf("%v", this.Salt) + `,`,
|
||||||
`Pk:` + fmt.Sprintf("%v", this.Pk) + `,`,
|
`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) + `,`,
|
`Keys:` + strings.Replace(fmt.Sprintf("%v", this.Keys), "WrappedKey", "WrappedKey", 1) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
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 {
|
func (this *WrappedKey) String() string {
|
||||||
if this == nil {
|
if this == nil {
|
||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
s := strings.Join([]string{`&WrappedKey{`,
|
s := strings.Join([]string{`&WrappedKey{`,
|
||||||
`Key:` + fmt.Sprintf("%v", this.Key) + `,`,
|
`DKey:` + fmt.Sprintf("%v", this.DKey) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
|
@ -628,9 +519,9 @@ func (m *Header) Unmarshal(dAtA []byte) error {
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
case 4:
|
case 4:
|
||||||
if wireType != 2 {
|
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 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
if shift >= 64 {
|
if shift >= 64 {
|
||||||
return ErrIntOverflowHdr
|
return ErrIntOverflowHdr
|
||||||
|
@ -640,23 +531,21 @@ func (m *Header) Unmarshal(dAtA []byte) error {
|
||||||
}
|
}
|
||||||
b := dAtA[iNdEx]
|
b := dAtA[iNdEx]
|
||||||
iNdEx++
|
iNdEx++
|
||||||
msglen |= (int(b) & 0x7F) << shift
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if msglen < 0 {
|
if byteLen < 0 {
|
||||||
return ErrInvalidLengthHdr
|
return ErrInvalidLengthHdr
|
||||||
}
|
}
|
||||||
postIndex := iNdEx + msglen
|
postIndex := iNdEx + byteLen
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
if m.SenderPk == nil {
|
m.SenderSign = append(m.SenderSign[:0], dAtA[iNdEx:postIndex]...)
|
||||||
m.SenderPk = &Sender{}
|
if m.SenderSign == nil {
|
||||||
}
|
m.SenderSign = []byte{}
|
||||||
if err := m.SenderPk.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
case 5:
|
case 5:
|
||||||
|
@ -711,87 +600,6 @@ func (m *Header) Unmarshal(dAtA []byte) error {
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
func (m *WrappedKey) Unmarshal(dAtA []byte) error {
|
||||||
l := len(dAtA)
|
l := len(dAtA)
|
||||||
iNdEx := 0
|
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)
|
return fmt.Errorf("proto: wrapped_key: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
}
|
}
|
||||||
switch fieldNum {
|
switch fieldNum {
|
||||||
case 2:
|
case 1:
|
||||||
if wireType != 2 {
|
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
|
var byteLen int
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
@ -847,9 +655,9 @@ func (m *WrappedKey) Unmarshal(dAtA []byte) error {
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
|
m.DKey = append(m.DKey[:0], dAtA[iNdEx:postIndex]...)
|
||||||
if m.Key == nil {
|
if m.DKey == nil {
|
||||||
m.Key = []byte{}
|
m.DKey = []byte{}
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
|
@ -981,22 +789,21 @@ var (
|
||||||
func init() { proto.RegisterFile("internal/pb/hdr.proto", fileDescriptorHdr) }
|
func init() { proto.RegisterFile("internal/pb/hdr.proto", fileDescriptorHdr) }
|
||||||
|
|
||||||
var fileDescriptorHdr = []byte{
|
var fileDescriptorHdr = []byte{
|
||||||
// 265 bytes of a gzipped FileDescriptorProto
|
// 252 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x90, 0x4f, 0x4a, 0xc3, 0x50,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0xcf, 0x31, 0x4e, 0xeb, 0x40,
|
||||||
0x10, 0xc6, 0x33, 0x69, 0x0d, 0x76, 0xe2, 0x3f, 0x1e, 0x08, 0x6f, 0xe3, 0x18, 0xe2, 0xc2, 0x2c,
|
0x10, 0xc6, 0x71, 0x8f, 0xe3, 0x44, 0x7a, 0xe3, 0x3c, 0x90, 0x16, 0x21, 0xb9, 0x61, 0xb0, 0x4c,
|
||||||
0x24, 0x85, 0x7a, 0x03, 0x4f, 0x20, 0xf1, 0x00, 0x21, 0x31, 0x03, 0x09, 0xaf, 0xa4, 0x8f, 0x97,
|
0xe3, 0x02, 0x39, 0x12, 0xdc, 0x80, 0x96, 0xce, 0x39, 0x80, 0x65, 0xe3, 0x51, 0x6c, 0x6d, 0xb4,
|
||||||
0x88, 0xa4, 0x2b, 0x8f, 0xe0, 0xd6, 0x1b, 0x78, 0x14, 0x97, 0x5d, 0xba, 0x34, 0xcf, 0x8d, 0xcb,
|
0x59, 0xed, 0x1a, 0x21, 0xa7, 0xe2, 0x08, 0x70, 0x0b, 0x8e, 0x42, 0x99, 0x92, 0x12, 0x2f, 0x0d,
|
||||||
0x1e, 0x41, 0x9a, 0x54, 0x70, 0x35, 0x1f, 0xbf, 0xf9, 0x86, 0x6f, 0xf8, 0xf0, 0xbc, 0xaa, 0x5b,
|
0x65, 0x8e, 0x80, 0xb4, 0x34, 0x74, 0x9f, 0x7e, 0xff, 0x66, 0x06, 0xcf, 0x7b, 0x35, 0xb0, 0x51,
|
||||||
0x36, 0x75, 0xb6, 0x9c, 0xeb, 0x7c, 0x5e, 0x16, 0x26, 0xd6, 0x66, 0xd5, 0xae, 0x84, 0xab, 0xf3,
|
0xf5, 0x76, 0xa5, 0x9b, 0x55, 0xd7, 0x9a, 0x42, 0x9b, 0xdd, 0xb0, 0x13, 0xa1, 0x6e, 0xb2, 0x57,
|
||||||
0xf0, 0x0d, 0xd0, 0x2b, 0x39, 0x2b, 0xd8, 0x88, 0x0b, 0xc4, 0xc7, 0xf2, 0xa9, 0x56, 0x69, 0x53,
|
0xc0, 0x45, 0xc7, 0x75, 0xcb, 0x46, 0x5c, 0x20, 0x3e, 0x74, 0x8f, 0x4a, 0x56, 0xb6, 0xdf, 0x73,
|
||||||
0xad, 0x59, 0x42, 0x00, 0xd1, 0x71, 0x32, 0x1b, 0xc8, 0x43, 0xb5, 0x66, 0x21, 0x70, 0xda, 0x64,
|
0x02, 0x29, 0xe4, 0xff, 0xcb, 0x7f, 0x5e, 0xd6, 0xfd, 0x9e, 0x85, 0xc0, 0xc8, 0xd6, 0xdb, 0x21,
|
||||||
0xcb, 0x56, 0xba, 0x01, 0x44, 0x47, 0xc9, 0xa0, 0xc5, 0x09, 0xba, 0x5a, 0xc9, 0xc9, 0x40, 0x5c,
|
0x09, 0x53, 0xc8, 0x97, 0xa5, 0xdf, 0xe2, 0x04, 0x43, 0x2d, 0x93, 0x99, 0x97, 0x50, 0x4b, 0x71,
|
||||||
0xad, 0xc4, 0x35, 0xce, 0x1a, 0xae, 0x0b, 0x36, 0xa9, 0x56, 0x72, 0x1a, 0x40, 0xe4, 0x2f, 0x30,
|
0x89, 0xb1, 0x65, 0xd5, 0xb2, 0xa9, 0x6c, 0xbf, 0x51, 0x49, 0xe4, 0x03, 0xfe, 0xd2, 0xba, 0xdf,
|
||||||
0xd6, 0x79, 0x3c, 0xc2, 0xe4, 0x70, 0x9c, 0xf7, 0x4a, 0x5c, 0xe1, 0x54, 0x71, 0xd7, 0xc8, 0x83,
|
0x28, 0x71, 0x85, 0x91, 0xe4, 0xd1, 0x26, 0xf3, 0x74, 0x96, 0xc7, 0x37, 0xa7, 0x85, 0x6e, 0x8a,
|
||||||
0x60, 0x12, 0xf9, 0x8b, 0xd3, 0x9d, 0xe7, 0xd9, 0x64, 0x5a, 0x73, 0x91, 0x2a, 0xee, 0x92, 0x61,
|
0x27, 0x53, 0x6b, 0xcd, 0x6d, 0x25, 0x79, 0x2c, 0x7d, 0xcc, 0x32, 0x8c, 0xff, 0xa0, 0x38, 0xc3,
|
||||||
0x19, 0x4a, 0xf4, 0xc6, 0x83, 0x7d, 0x0e, 0xfc, 0xe5, 0x84, 0x97, 0xe8, 0xff, 0xb3, 0x8b, 0x33,
|
0xb9, 0x1f, 0xfe, 0xa4, 0x65, 0x19, 0xb5, 0xf7, 0x3c, 0xde, 0x5d, 0x1f, 0x26, 0x0a, 0x3e, 0x26,
|
||||||
0x9c, 0x28, 0xee, 0xf6, 0x9f, 0xed, 0xe4, 0xdd, 0xcd, 0xa6, 0x27, 0xe7, 0xb3, 0x27, 0x67, 0xdb,
|
0x0a, 0x8e, 0x13, 0xc1, 0xb3, 0x23, 0x78, 0x73, 0x04, 0xef, 0x8e, 0xe0, 0xe0, 0x08, 0x3e, 0x1d,
|
||||||
0x13, 0xbc, 0x58, 0x82, 0x77, 0x4b, 0xf0, 0x61, 0x09, 0x36, 0x96, 0xe0, 0xcb, 0x12, 0xfc, 0x58,
|
0xc1, 0xb7, 0xa3, 0xe0, 0xe8, 0x08, 0x5e, 0xbe, 0x28, 0x68, 0x16, 0xfe, 0xe1, 0xdb, 0x9f, 0x00,
|
||||||
0x72, 0xb6, 0x96, 0xe0, 0xf5, 0x9b, 0x9c, 0xdc, 0x1b, 0xfa, 0xb8, 0xfd, 0x0d, 0x00, 0x00, 0xff,
|
0x00, 0x00, 0xff, 0xff, 0x88, 0x3d, 0x22, 0x56, 0x09, 0x01, 0x00, 0x00,
|
||||||
0xff, 0x7f, 0xa8, 0x12, 0x55, 0x28, 0x01, 0x00, 0x00,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,11 @@ package pb;
|
||||||
* protobuf format before writing to disk.
|
* protobuf format before writing to disk.
|
||||||
*/
|
*/
|
||||||
message header {
|
message header {
|
||||||
uint32 chunk_size = 1;
|
uint32 chunk_size = 1; // encryption block size
|
||||||
bytes salt = 2;
|
bytes salt = 2; // master salt (nonces are derived from this)
|
||||||
bytes pk = 3; // sender's ephemeral curve PK
|
bytes pk = 3; // ephemeral curve PK
|
||||||
sender sender_pk = 4; // sender's encrypted ed25519 PK
|
bytes sender_sign = 4; // signature block of sender
|
||||||
repeated wrapped_key keys = 5;
|
repeated wrapped_key keys = 5; // list of wrapped receiver blocks
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sender info is wrapped using the data encryption key
|
|
||||||
*/
|
|
||||||
message sender {
|
|
||||||
bytes pk = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -35,5 +28,5 @@ message sender {
|
||||||
* key. WrappedKey describes such a wrapped key.
|
* key. WrappedKey describes such a wrapped key.
|
||||||
*/
|
*/
|
||||||
message wrapped_key {
|
message wrapped_key {
|
||||||
bytes key = 2;
|
bytes d_key = 1; // encrypted data key
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
310
sign/encrypt.go
310
sign/encrypt.go
|
@ -56,6 +56,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
@ -76,8 +77,12 @@ const (
|
||||||
|
|
||||||
_Magic = "SigTool"
|
_Magic = "SigTool"
|
||||||
_MagicLen = len(_Magic)
|
_MagicLen = len(_Magic)
|
||||||
_AEADNonceLen = 32
|
_AEADNonceLen = 16
|
||||||
_FixedHdrLen = _MagicLen + 1 + 4
|
_FixedHdrLen = _MagicLen + 1 + 4
|
||||||
|
|
||||||
|
_WrapReceiverNonce = "Receiver Key Nonce"
|
||||||
|
_WrapSenderNonce = "Sender Sig Nonce"
|
||||||
|
_EncryptNonce = "Encrypt Nonce"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encryptor holds the encryption context
|
// Encryptor holds the encryption context
|
||||||
|
@ -87,9 +92,8 @@ type Encryptor struct {
|
||||||
|
|
||||||
ae cipher.AEAD
|
ae cipher.AEAD
|
||||||
|
|
||||||
// sender ephemeral curve 25519 SK
|
// ephemeral key
|
||||||
// the corresponding PK is in Header above
|
encSK []byte
|
||||||
senderSK []byte
|
|
||||||
|
|
||||||
started bool
|
started bool
|
||||||
|
|
||||||
|
@ -98,9 +102,8 @@ type Encryptor struct {
|
||||||
stream bool
|
stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Encryption context and use the optional private key 'sk' for
|
// Create a new Encryption context for encrypting blocks of size 'blksize'.
|
||||||
// signing any recipient keys. If 'sk' is nil, then ephmeral Curve25519 keys
|
// If 'sk' is not nil, authenticate the sender to each receiver.
|
||||||
// are generated and used with recipient's public key.
|
|
||||||
func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
|
func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
|
||||||
var blksz uint32
|
var blksz uint32
|
||||||
|
|
||||||
|
@ -113,7 +116,8 @@ func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
|
||||||
blksz = uint32(blksize)
|
blksz = uint32(blksize)
|
||||||
}
|
}
|
||||||
|
|
||||||
csk, cpk, err := newSender()
|
// generate ephemeral Curve25519 keys
|
||||||
|
esk, epk, err := newSender()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("encrypt: %s", err)
|
return nil, fmt.Errorf("encrypt: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -121,33 +125,39 @@ func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
|
||||||
key := make([]byte, 32)
|
key := make([]byte, 32)
|
||||||
salt := make([]byte, _AEADNonceLen)
|
salt := make([]byte, _AEADNonceLen)
|
||||||
|
|
||||||
pb.Randread(key)
|
randRead(key)
|
||||||
pb.Randread(salt)
|
randRead(salt)
|
||||||
|
|
||||||
// if sender has provided their identity to authenticate, we will use their PK
|
// if sender has provided their identity to authenticate, we sign the data-enc key
|
||||||
senderPK := cpk
|
// and encrypt the signature. At no point will we send the sender's identity.
|
||||||
|
var senderSig []byte
|
||||||
if sk != nil {
|
if sk != nil {
|
||||||
epk := sk.PublicKey()
|
sig, err := sk.SignMessage(key, "")
|
||||||
senderPK = epk.toCurve25519PK()
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("encrypt: %s", err)
|
return nil, fmt.Errorf("encrypt: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e := &Encryptor{
|
e := &Encryptor{
|
||||||
Header: pb.Header{
|
Header: pb.Header{
|
||||||
ChunkSize: blksz,
|
ChunkSize: blksz,
|
||||||
Salt: salt,
|
Salt: salt,
|
||||||
Pk: cpk,
|
Pk: epk,
|
||||||
SenderPk: &pb.Sender{
|
SenderSign: wSig,
|
||||||
Pk: wPk,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
key: key,
|
key: key,
|
||||||
senderSK: csk,
|
encSK: esk,
|
||||||
}
|
}
|
||||||
|
|
||||||
return e, nil
|
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")
|
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 {
|
if err == nil {
|
||||||
e.Keys = append(e.Keys, w)
|
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
|
// we mix the header checksum to create the encryption key
|
||||||
h = sha256.New()
|
h = sha256.New()
|
||||||
h.Write([]byte("Encrypt Nonce"))
|
h.Write([]byte(_EncryptNonce))
|
||||||
h.Write(e.key)
|
h.Write(e.key)
|
||||||
h.Write(sumHdr)
|
h.Write(sumHdr)
|
||||||
key := h.Sum(nil)
|
key := h.Sum(nil)
|
||||||
|
@ -286,7 +296,7 @@ func fullwrite(buf []byte, wr io.Writer) error {
|
||||||
// additional data in the AEAD construction.
|
// additional data in the AEAD construction.
|
||||||
func (e *Encryptor) encrypt(buf []byte, wr io.Writer, i uint32, eof bool) error {
|
func (e *Encryptor) encrypt(buf []byte, wr io.Writer, i uint32, eof bool) error {
|
||||||
var b [8]byte
|
var b [8]byte
|
||||||
var noncebuf [32]byte
|
var nonceb [32]byte
|
||||||
var z uint32 = uint32(len(buf))
|
var z uint32 = uint32(len(buf))
|
||||||
|
|
||||||
// mark last block
|
// 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 := sha256.New()
|
||||||
h.Write(e.Salt)
|
h.Write(e.Salt)
|
||||||
h.Write(b[:])
|
h.Write(b[:])
|
||||||
nonce := h.Sum(noncebuf[:0])
|
nonce := h.Sum(nonceb[:0])[:e.ae.NonceSize()]
|
||||||
|
|
||||||
copy(e.buf[:4], b[:4])
|
copy(e.buf[:4], b[:4])
|
||||||
cbuf := e.buf[4:]
|
cbuf := e.buf[4:]
|
||||||
|
@ -324,6 +334,9 @@ type Decryptor struct {
|
||||||
buf []byte
|
buf []byte
|
||||||
hdrsum []byte
|
hdrsum []byte
|
||||||
|
|
||||||
|
// flag set to true if sender signed the key
|
||||||
|
auth bool
|
||||||
|
|
||||||
// Decrypted key
|
// Decrypted key
|
||||||
key []byte
|
key []byte
|
||||||
eof bool
|
eof bool
|
||||||
|
@ -401,7 +414,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
|
||||||
|
|
||||||
// sanity check on the wrapped keys
|
// sanity check on the wrapped keys
|
||||||
for i, w := range d.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)
|
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
|
var key []byte
|
||||||
|
|
||||||
for i, w := range d.Keys {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("decrypt: can't unwrap key %d: %s", i, err)
|
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")
|
return fmt.Errorf("decrypt: wrong key")
|
||||||
|
|
||||||
havekey:
|
havekey:
|
||||||
if senderPk != nil {
|
if err := d.verifySender(key, sk, senderPk); err != nil {
|
||||||
hpk, err := d.SenderPk.UnwrapPK(key, d.Salt)
|
return fmt.Errorf("decrypt: %s", err)
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX do we need to verify d.Header.Sender.Key vs. d.Header.PK?
|
|
||||||
|
|
||||||
d.key = key
|
d.key = key
|
||||||
|
|
||||||
// we mix the header checksum into the key
|
// we mix the header checksum into the key
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write([]byte("Encrypt Nonce"))
|
h.Write([]byte(_EncryptNonce))
|
||||||
h.Write(d.key)
|
h.Write(d.key)
|
||||||
h.Write(d.hdrsum)
|
h.Write(d.hdrsum)
|
||||||
key = h.Sum(nil)
|
key = h.Sum(nil)
|
||||||
|
@ -464,72 +467,10 @@ havekey:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap data encryption key 'k' with the sender's PK and our ephemeral curve SK
|
// AuthenticatedSender returns true if the sender authenticated themselves
|
||||||
func wrapKey(pk *PublicKey, k, ourSK, salt []byte) (*pb.WrappedKey, error) {
|
// (the data-encryption key is signed).
|
||||||
shared, err := curve25519.X25519(ourSK, pk.toCurve25519PK())
|
func (d *Decryptor) AuthenticatedSender() bool {
|
||||||
if err != nil {
|
return d.auth
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the file and write to 'wr'
|
// Decrypt the file and write to 'wr'
|
||||||
|
@ -567,6 +508,143 @@ func (d *Decryptor) Decrypt(wr io.Writer) error {
|
||||||
return nil
|
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
|
// Decrypt exactly one chunk of data
|
||||||
func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
|
func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
|
||||||
var b [8]byte
|
var b [8]byte
|
||||||
|
@ -605,7 +683,7 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(d.Salt)
|
h.Write(d.Salt)
|
||||||
h.Write(b[:])
|
h.Write(b[:])
|
||||||
nonce := h.Sum(nonceb[:0])
|
nonce := h.Sum(nonceb[:0])[:d.ae.NonceSize()]
|
||||||
|
|
||||||
z := m + ovh
|
z := m + ovh
|
||||||
n, err = io.ReadFull(d.rd, d.buf[:z])
|
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) {
|
func newSender() (sk, pk []byte, err error) {
|
||||||
var csk [32]byte
|
var csk [32]byte
|
||||||
|
|
||||||
pb.Randread(csk[:])
|
randRead(csk[:])
|
||||||
pb.Clamp(csk[:])
|
clamp(csk[:])
|
||||||
pk, err = curve25519.X25519(csk[:], curve25519.Basepoint)
|
pk, err = curve25519.X25519(csk[:], curve25519.Basepoint)
|
||||||
sk = csk[:]
|
sk = csk[:]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeNonce(v ...[]byte) []byte {
|
||||||
|
h := sha256.New()
|
||||||
|
for _, x := range v {
|
||||||
|
h.Write(x)
|
||||||
|
}
|
||||||
|
return h.Sum(nil)[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
|
18
sign/keys.go
18
sign/keys.go
|
@ -28,6 +28,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
@ -37,7 +38,6 @@ import (
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/opencoff/go-utils"
|
"github.com/opencoff/go-utils"
|
||||||
"github.com/opencoff/sigtool/internal/pb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Private Ed25519 key
|
// Private Ed25519 key
|
||||||
|
@ -70,12 +70,6 @@ type Keypair struct {
|
||||||
Pub PublicKey
|
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
|
// Length of Ed25519 Public Key Hash
|
||||||
const PKHashLength = 16
|
const PKHashLength = 16
|
||||||
|
|
||||||
|
@ -349,7 +343,7 @@ func (sk *PrivateKey) serialize(fn, comment string, getpw func() ([]byte, error)
|
||||||
pass := sha512.Sum512(pw)
|
pass := sha512.Sum512(pw)
|
||||||
salt := make([]byte, 32)
|
salt := make([]byte, 32)
|
||||||
|
|
||||||
pb.Randread(salt)
|
randRead(salt)
|
||||||
|
|
||||||
// "32" == Length of AES-256 key
|
// "32" == Length of AES-256 key
|
||||||
key, err := scrypt.Key(pass[:], salt, _N, _r, _p, 32)
|
key, err := scrypt.Key(pass[:], salt, _N, _r, _p, 32)
|
||||||
|
@ -544,5 +538,13 @@ func clamp(k []byte) []byte {
|
||||||
return k
|
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
|
// EOF
|
||||||
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
||||||
|
|
12
sign/sign.go
12
sign/sign.go
|
@ -30,6 +30,12 @@ import (
|
||||||
"gopkg.in/yaml.v2"
|
"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
|
// Sign a prehashed Message; return the signature as opaque bytes
|
||||||
// Signature is an YAML file:
|
// Signature is an YAML file:
|
||||||
// Comment: source file path
|
// Comment: source file path
|
||||||
|
@ -147,19 +153,19 @@ func (pk *PublicKey) VerifyFile(fn string, sig *Signature) (bool, error) {
|
||||||
return false, err
|
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'
|
// Verify a signature 'sig' for a pre-calculated checksum 'ck' against public key 'pk'
|
||||||
// Return True if signature matches, False otherwise
|
// 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 := sha512.New()
|
||||||
h.Write([]byte("sigtool signed message"))
|
h.Write([]byte("sigtool signed message"))
|
||||||
h.Write(ck)
|
h.Write(ck)
|
||||||
ck = h.Sum(nil)[:]
|
ck = h.Sum(nil)[:]
|
||||||
|
|
||||||
x := Ed.PublicKey(pk.Pk)
|
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:
|
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
||||||
|
|
|
@ -19,8 +19,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/opencoff/sigtool/internal/pb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Return a temp dir in a temp-dir
|
// Return a temp dir in a temp-dir
|
||||||
|
@ -30,7 +28,7 @@ func tempdir(t *testing.T) string {
|
||||||
var b [10]byte
|
var b [10]byte
|
||||||
|
|
||||||
dn := os.TempDir()
|
dn := os.TempDir()
|
||||||
pb.Randread(b[:])
|
randRead(b[:])
|
||||||
|
|
||||||
tmp := path.Join(dn, fmt.Sprintf("%x", b[:]))
|
tmp := path.Join(dn, fmt.Sprintf("%x", b[:]))
|
||||||
err := os.MkdirAll(tmp, 0755)
|
err := os.MkdirAll(tmp, 0755)
|
||||||
|
@ -137,7 +135,7 @@ func TestSignRandBuf(t *testing.T) {
|
||||||
|
|
||||||
var ck [64]byte // simulates sha512 sum
|
var ck [64]byte // simulates sha512 sum
|
||||||
|
|
||||||
pb.Randread(ck[:])
|
randRead(ck[:])
|
||||||
|
|
||||||
pk := &kp.Pub
|
pk := &kp.Pub
|
||||||
sk := &kp.Sec
|
sk := &kp.Sec
|
||||||
|
@ -150,17 +148,15 @@ func TestSignRandBuf(t *testing.T) {
|
||||||
assert(ss.IsPKMatch(pk), "pk match fail")
|
assert(ss.IsPKMatch(pk), "pk match fail")
|
||||||
|
|
||||||
// Corrupt the pkhash and see
|
// Corrupt the pkhash and see
|
||||||
pb.Randread(ss.pkhash)
|
randRead(ss.pkhash)
|
||||||
assert(!ss.IsPKMatch(pk), "corrupt pk match fail")
|
assert(!ss.IsPKMatch(pk), "corrupt pk match fail")
|
||||||
|
|
||||||
// Incorrect checksum == should fail verification
|
// Incorrect checksum == should fail verification
|
||||||
ok, err := pk.VerifyMessage(ck[:16], ss)
|
ok := pk.VerifyMessage(ck[:16], ss)
|
||||||
assert(err == nil, "bad ck verify err fail")
|
|
||||||
assert(!ok, "bad ck verify fail")
|
assert(!ok, "bad ck verify fail")
|
||||||
|
|
||||||
// proper checksum == should work
|
// proper checksum == should work
|
||||||
ok, err = pk.VerifyMessage(ck[:], ss)
|
ok = pk.VerifyMessage(ck[:], ss)
|
||||||
assert(err == nil, "verify err")
|
|
||||||
assert(ok, "verify fail")
|
assert(ok, "verify fail")
|
||||||
|
|
||||||
// Now sign a file
|
// Now sign a file
|
||||||
|
@ -187,7 +183,7 @@ func TestSignRandBuf(t *testing.T) {
|
||||||
assert(err == nil, "file.dat creat file")
|
assert(err == nil, "file.dat creat file")
|
||||||
|
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
pb.Randread(buf[:])
|
randRead(buf[:])
|
||||||
n, err := fd.Write(buf[:])
|
n, err := fd.Write(buf[:])
|
||||||
assert(err == nil, fmt.Sprintf("file.dat write fail: %s", err))
|
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))
|
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 {
|
func randbuf(sz uint) []byte {
|
||||||
b := make([]byte, sz)
|
b := make([]byte, sz)
|
||||||
pb.Randread(b)
|
randRead(b)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
||||||
1.0.0
|
1.1.0
|
||||||
|
|
Loading…
Add table
Reference in a new issue