Add sender authenticated message integrity; fixup KDF
- use HKDF for producing keys, nonces - add running hmac of plaintext; sender-sign the hmac as trailer - use header checksum as "salt" for data encryption keys, nonces - generate explicit nonce for wrapping root keys for each recipient (previous impl had brittleness)
This commit is contained in:
parent
a428db8feb
commit
f343d45a8e
6 changed files with 495 additions and 249 deletions
|
@ -34,7 +34,7 @@ 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"`
|
||||||
SenderSign []byte `protobuf:"bytes,4,opt,name=sender_sign,json=senderSign,proto3" json:"sender_sign,omitempty"`
|
Sender []byte `protobuf:"bytes,4,opt,name=sender,proto3" json:"sender,omitempty"`
|
||||||
Keys []*WrappedKey `protobuf:"bytes,5,rep,name=keys,proto3" json:"keys,omitempty"`
|
Keys []*WrappedKey `protobuf:"bytes,5,rep,name=keys,proto3" json:"keys,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,9 +91,9 @@ func (m *Header) GetPk() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Header) GetSenderSign() []byte {
|
func (m *Header) GetSender() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.SenderSign
|
return m.Sender
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,7 @@ func (m *Header) GetKeys() []*WrappedKey {
|
||||||
// key. WrappedKey describes such a wrapped key.
|
// key. WrappedKey describes such a wrapped key.
|
||||||
type WrappedKey struct {
|
type WrappedKey struct {
|
||||||
DKey []byte `protobuf:"bytes,1,opt,name=d_key,json=dKey,proto3" json:"d_key,omitempty"`
|
DKey []byte `protobuf:"bytes,1,opt,name=d_key,json=dKey,proto3" json:"d_key,omitempty"`
|
||||||
|
Nonce []byte `protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WrappedKey) Reset() { *m = WrappedKey{} }
|
func (m *WrappedKey) Reset() { *m = WrappedKey{} }
|
||||||
|
@ -151,6 +152,13 @@ func (m *WrappedKey) GetDKey() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) GetNonce() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Nonce
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*Header)(nil), "pb.header")
|
proto.RegisterType((*Header)(nil), "pb.header")
|
||||||
proto.RegisterType((*WrappedKey)(nil), "pb.wrapped_key")
|
proto.RegisterType((*WrappedKey)(nil), "pb.wrapped_key")
|
||||||
|
@ -159,24 +167,24 @@ func init() {
|
||||||
func init() { proto.RegisterFile("internal/pb/hdr.proto", fileDescriptor_c715362029a696e2) }
|
func init() { proto.RegisterFile("internal/pb/hdr.proto", fileDescriptor_c715362029a696e2) }
|
||||||
|
|
||||||
var fileDescriptor_c715362029a696e2 = []byte{
|
var fileDescriptor_c715362029a696e2 = []byte{
|
||||||
// 257 bytes of a gzipped FileDescriptorProto
|
// 262 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0xcf, 0x31, 0x4e, 0xeb, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0xd0, 0x3f, 0x4e, 0xf3, 0x40,
|
||||||
0x10, 0xc6, 0xf1, 0x1d, 0xc7, 0x89, 0xf4, 0x26, 0x79, 0x20, 0x2d, 0x42, 0x72, 0xc3, 0x60, 0x99,
|
0x10, 0x05, 0xf0, 0x1d, 0xc7, 0x89, 0xf4, 0x4d, 0xf2, 0x81, 0xb4, 0xfc, 0x91, 0x1b, 0x46, 0x56,
|
||||||
0xc6, 0x95, 0x23, 0x01, 0x27, 0xa0, 0xa5, 0x73, 0x0e, 0x60, 0xd9, 0x78, 0x14, 0x5b, 0x8e, 0x36,
|
0x68, 0x5c, 0x39, 0x12, 0x50, 0x50, 0xd3, 0xd2, 0x99, 0x03, 0x44, 0x76, 0x3c, 0x92, 0x2d, 0x47,
|
||||||
0xab, 0xb5, 0x11, 0x72, 0x2a, 0x8e, 0x00, 0xb7, 0xe0, 0x28, 0x94, 0x2e, 0x53, 0xe2, 0x75, 0x43,
|
0xeb, 0xd5, 0xda, 0x08, 0x39, 0x15, 0x25, 0x25, 0xc7, 0xe0, 0x28, 0x94, 0x2e, 0x53, 0xe2, 0x75,
|
||||||
0x99, 0x23, 0x20, 0x2d, 0x0d, 0xdd, 0xa7, 0xdf, 0xbf, 0x99, 0xc1, 0xcb, 0x5a, 0x75, 0x6c, 0x54,
|
0x43, 0x99, 0x23, 0x20, 0x2d, 0x29, 0xe8, 0xde, 0xfb, 0x4d, 0xf3, 0x34, 0x78, 0x51, 0xaa, 0x96,
|
||||||
0xbe, 0x5b, 0xeb, 0x62, 0x5d, 0x95, 0x26, 0xd1, 0x66, 0xdf, 0xed, 0xa5, 0xa7, 0x8b, 0xe8, 0x1d,
|
0x8d, 0x4a, 0xb7, 0x2b, 0x9d, 0xad, 0x8a, 0xdc, 0xc4, 0xda, 0xd4, 0x6d, 0x2d, 0x3d, 0x9d, 0x2d,
|
||||||
0x70, 0x51, 0x71, 0x5e, 0xb2, 0x91, 0x57, 0x88, 0x4f, 0xd5, 0xb3, 0x6a, 0xb2, 0xb6, 0x3e, 0x70,
|
0xdf, 0x00, 0x67, 0x05, 0xa7, 0x39, 0x1b, 0x79, 0x85, 0xb8, 0x29, 0x9e, 0x55, 0xb5, 0x6e, 0xca,
|
||||||
0x00, 0x21, 0xc4, 0xff, 0xd3, 0x7f, 0x4e, 0x36, 0xf5, 0x81, 0xa5, 0x44, 0xbf, 0xcd, 0x77, 0x5d,
|
0x1d, 0x07, 0x10, 0x42, 0xf4, 0x3f, 0xf9, 0xe7, 0xe4, 0xa9, 0xdc, 0xb1, 0x94, 0xe8, 0x37, 0xe9,
|
||||||
0xe0, 0x85, 0x10, 0xaf, 0x52, 0xb7, 0xe5, 0x19, 0x7a, 0xba, 0x09, 0x66, 0x4e, 0x3c, 0xdd, 0xc8,
|
0xb6, 0x0d, 0xbc, 0x10, 0xa2, 0x45, 0xe2, 0xb2, 0x3c, 0x41, 0x4f, 0x57, 0xc1, 0xc4, 0x89, 0xa7,
|
||||||
0x6b, 0x5c, 0xb6, 0xac, 0x4a, 0x36, 0x59, 0x5b, 0x6f, 0x55, 0xe0, 0xbb, 0x80, 0xbf, 0xb4, 0xa9,
|
0x2b, 0x79, 0x89, 0xb3, 0x86, 0x55, 0xce, 0x26, 0xf0, 0x9d, 0x1d, 0x9b, 0xbc, 0x46, 0xbf, 0xe2,
|
||||||
0xb7, 0x4a, 0xde, 0xa0, 0xdf, 0x70, 0xdf, 0x06, 0xf3, 0x70, 0x16, 0x2f, 0x6f, 0xcf, 0x13, 0x5d,
|
0xae, 0x09, 0xa6, 0xe1, 0x24, 0x9a, 0xdf, 0x9c, 0xc6, 0x3a, 0x8b, 0x5f, 0x4c, 0xaa, 0x35, 0xe7,
|
||||||
0x24, 0x2f, 0x26, 0xd7, 0x9a, 0xcb, 0xac, 0xe1, 0x3e, 0x75, 0x31, 0x8a, 0x70, 0xf9, 0x07, 0xe5,
|
0xeb, 0x8a, 0xbb, 0xc4, 0x1d, 0x97, 0xf7, 0x38, 0xff, 0x83, 0xf2, 0x0c, 0xa7, 0x2e, 0xb8, 0x25,
|
||||||
0x05, 0xce, 0xdd, 0x70, 0x27, 0xad, 0x52, 0xbf, 0x7c, 0xe4, 0xfe, 0xe1, 0x7e, 0x18, 0x49, 0x1c,
|
0x8b, 0xc4, 0xcf, 0x1f, 0xb9, 0x93, 0xe7, 0x38, 0x55, 0xb5, 0xda, 0xf0, 0x71, 0xc5, 0x6f, 0x79,
|
||||||
0x47, 0x12, 0xa7, 0x91, 0xe0, 0xd5, 0x12, 0x7c, 0x58, 0x82, 0x4f, 0x4b, 0x30, 0x58, 0x82, 0x2f,
|
0xb8, 0xeb, 0x07, 0x12, 0xfb, 0x81, 0xc4, 0x61, 0x20, 0x78, 0xb5, 0x04, 0x1f, 0x96, 0xe0, 0xd3,
|
||||||
0x4b, 0xf0, 0x6d, 0x49, 0x9c, 0x2c, 0xc1, 0xdb, 0x44, 0x62, 0x98, 0x48, 0x1c, 0x27, 0x12, 0xc5,
|
0x12, 0xf4, 0x96, 0xe0, 0xcb, 0x12, 0x7c, 0x5b, 0x12, 0x07, 0x4b, 0xf0, 0x3e, 0x92, 0xe8, 0x47,
|
||||||
0xc2, 0x3d, 0x7e, 0xf7, 0x13, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x7a, 0x39, 0x5a, 0x11, 0x01, 0x00,
|
0x12, 0xfb, 0x91, 0x44, 0x36, 0x73, 0x5f, 0xb8, 0xfd, 0x09, 0x00, 0x00, 0xff, 0xff, 0x06, 0x40,
|
||||||
0x00,
|
0x0b, 0xb4, 0x1e, 0x01, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Header) Equal(that interface{}) bool {
|
func (this *Header) Equal(that interface{}) bool {
|
||||||
|
@ -207,7 +215,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 !bytes.Equal(this.SenderSign, that1.SenderSign) {
|
if !bytes.Equal(this.Sender, that1.Sender) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(this.Keys) != len(that1.Keys) {
|
if len(this.Keys) != len(that1.Keys) {
|
||||||
|
@ -242,6 +250,9 @@ func (this *WrappedKey) Equal(that interface{}) bool {
|
||||||
if !bytes.Equal(this.DKey, that1.DKey) {
|
if !bytes.Equal(this.DKey, that1.DKey) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if !bytes.Equal(this.Nonce, that1.Nonce) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
func (this *Header) GoString() string {
|
func (this *Header) GoString() string {
|
||||||
|
@ -253,7 +264,7 @@ 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")
|
||||||
s = append(s, "SenderSign: "+fmt.Sprintf("%#v", this.SenderSign)+",\n")
|
s = append(s, "Sender: "+fmt.Sprintf("%#v", this.Sender)+",\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")
|
||||||
}
|
}
|
||||||
|
@ -264,9 +275,10 @@ func (this *WrappedKey) GoString() string {
|
||||||
if this == nil {
|
if this == nil {
|
||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
s := make([]string, 0, 5)
|
s := make([]string, 0, 6)
|
||||||
s = append(s, "&pb.WrappedKey{")
|
s = append(s, "&pb.WrappedKey{")
|
||||||
s = append(s, "DKey: "+fmt.Sprintf("%#v", this.DKey)+",\n")
|
s = append(s, "DKey: "+fmt.Sprintf("%#v", this.DKey)+",\n")
|
||||||
|
s = append(s, "Nonce: "+fmt.Sprintf("%#v", this.Nonce)+",\n")
|
||||||
s = append(s, "}")
|
s = append(s, "}")
|
||||||
return strings.Join(s, "")
|
return strings.Join(s, "")
|
||||||
}
|
}
|
||||||
|
@ -312,10 +324,10 @@ func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
dAtA[i] = 0x2a
|
dAtA[i] = 0x2a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(m.SenderSign) > 0 {
|
if len(m.Sender) > 0 {
|
||||||
i -= len(m.SenderSign)
|
i -= len(m.Sender)
|
||||||
copy(dAtA[i:], m.SenderSign)
|
copy(dAtA[i:], m.Sender)
|
||||||
i = encodeVarintHdr(dAtA, i, uint64(len(m.SenderSign)))
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.Sender)))
|
||||||
i--
|
i--
|
||||||
dAtA[i] = 0x22
|
dAtA[i] = 0x22
|
||||||
}
|
}
|
||||||
|
@ -361,6 +373,13 @@ func (m *WrappedKey) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
_ = i
|
_ = i
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
|
if len(m.Nonce) > 0 {
|
||||||
|
i -= len(m.Nonce)
|
||||||
|
copy(dAtA[i:], m.Nonce)
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.Nonce)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
}
|
||||||
if len(m.DKey) > 0 {
|
if len(m.DKey) > 0 {
|
||||||
i -= len(m.DKey)
|
i -= len(m.DKey)
|
||||||
copy(dAtA[i:], m.DKey)
|
copy(dAtA[i:], m.DKey)
|
||||||
|
@ -399,7 +418,7 @@ func (m *Header) Size() (n int) {
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovHdr(uint64(l))
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
}
|
}
|
||||||
l = len(m.SenderSign)
|
l = len(m.Sender)
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovHdr(uint64(l))
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
}
|
}
|
||||||
|
@ -422,6 +441,10 @@ func (m *WrappedKey) Size() (n int) {
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovHdr(uint64(l))
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
}
|
}
|
||||||
|
l = len(m.Nonce)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +467,7 @@ 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) + `,`,
|
||||||
`SenderSign:` + fmt.Sprintf("%v", this.SenderSign) + `,`,
|
`Sender:` + fmt.Sprintf("%v", this.Sender) + `,`,
|
||||||
`Keys:` + repeatedStringForKeys + `,`,
|
`Keys:` + repeatedStringForKeys + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
|
@ -456,6 +479,7 @@ func (this *WrappedKey) String() string {
|
||||||
}
|
}
|
||||||
s := strings.Join([]string{`&WrappedKey{`,
|
s := strings.Join([]string{`&WrappedKey{`,
|
||||||
`DKey:` + fmt.Sprintf("%v", this.DKey) + `,`,
|
`DKey:` + fmt.Sprintf("%v", this.DKey) + `,`,
|
||||||
|
`Nonce:` + fmt.Sprintf("%v", this.Nonce) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
|
@ -586,7 +610,7 @@ 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 SenderSign", wireType)
|
return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
|
||||||
}
|
}
|
||||||
var byteLen int
|
var byteLen int
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
@ -613,9 +637,9 @@ func (m *Header) Unmarshal(dAtA []byte) error {
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
m.SenderSign = append(m.SenderSign[:0], dAtA[iNdEx:postIndex]...)
|
m.Sender = append(m.Sender[:0], dAtA[iNdEx:postIndex]...)
|
||||||
if m.SenderSign == nil {
|
if m.Sender == nil {
|
||||||
m.SenderSign = []byte{}
|
m.Sender = []byte{}
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
case 5:
|
case 5:
|
||||||
|
@ -736,6 +760,40 @@ func (m *WrappedKey) Unmarshal(dAtA []byte) error {
|
||||||
m.DKey = []byte{}
|
m.DKey = []byte{}
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Nonce", 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 < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Nonce = append(m.Nonce[:0], dAtA[iNdEx:postIndex]...)
|
||||||
|
if m.Nonce == nil {
|
||||||
|
m.Nonce = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipHdr(dAtA[iNdEx:])
|
skippy, err := skipHdr(dAtA[iNdEx:])
|
||||||
|
|
|
@ -19,7 +19,7 @@ message header {
|
||||||
uint32 chunk_size = 1; // encryption block size
|
uint32 chunk_size = 1; // encryption block size
|
||||||
bytes salt = 2; // master salt (nonces are derived from this)
|
bytes salt = 2; // master salt (nonces are derived from this)
|
||||||
bytes pk = 3; // ephemeral curve PK
|
bytes pk = 3; // ephemeral curve PK
|
||||||
bytes sender_sign = 4; // signature block of sender
|
bytes sender = 4; // sender signed artifacts
|
||||||
repeated wrapped_key keys = 5; // list of wrapped receiver blocks
|
repeated wrapped_key keys = 5; // list of wrapped receiver blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,4 +29,6 @@ message header {
|
||||||
*/
|
*/
|
||||||
message wrapped_key {
|
message wrapped_key {
|
||||||
bytes d_key = 1; // encrypted data key
|
bytes d_key = 1; // encrypted data key
|
||||||
|
bytes nonce = 2; // nonce used for encryption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
523
sign/encrypt.go
523
sign/encrypt.go
|
@ -26,29 +26,36 @@
|
||||||
//
|
//
|
||||||
// Variable Length Segment:
|
// Variable Length Segment:
|
||||||
// - Protobuf encoded, per-recipient wrapped keys
|
// - Protobuf encoded, per-recipient wrapped keys
|
||||||
// - Shasum: 32 bytes (SHA256 of full header)
|
|
||||||
//
|
//
|
||||||
// The variable length segment consists of one or more
|
// The variable length segment consists of one or more
|
||||||
// recipients, each with their wrapped keys. This is encoded as
|
// recipients, each with their individually wrapped keys.
|
||||||
// a protobuf message. This protobuf encoded message immediately
|
|
||||||
// follows the fixed length header.
|
|
||||||
//
|
//
|
||||||
// The input data is encrypted with an expanded random 32-byte key:
|
// The input data is encrypted with an expanded random 32-byte key:
|
||||||
// - Prefix_string = "Encrypt Nonce"
|
// - hkdf-sha512 of random key, salt, context
|
||||||
// - datakey = SHA256(Prefix_string || header_checksum || random_key)
|
// - the hkdf process yields a data-encryption key, nonce and hmac key.
|
||||||
// - The header checksum is mixed in the above process to ensure we
|
// - we use the header checksum as the 'salt' for HKDF; this ensures that
|
||||||
// catch any malicious modification of the header.
|
// any modification of the header yields different keys
|
||||||
|
//
|
||||||
|
// We also calculate the cumulative hmac-sha256 of the plaintext blocks.
|
||||||
|
// - When sender identity is present, we sign the final hmac and append
|
||||||
|
// the signature as the "trailer".
|
||||||
|
// - When sender identity is NOT present, we put random bytes as the
|
||||||
|
// "signature". ie in either case, there is a trailer.
|
||||||
|
//
|
||||||
|
// Note: If the trailer is missing from a sigtool encrypted file - the
|
||||||
|
// recipient has no guarantees of content immutability (ie tampering
|
||||||
|
// from one of the _other_ recipients).
|
||||||
//
|
//
|
||||||
// The input data is broken up into "chunks"; each no larger than
|
// The input data is broken up into "chunks"; each no larger than
|
||||||
// maxChunkSize. The default block size is "chunkSize". Each block
|
// maxChunkSize. The default block size is "chunkSize". Each block
|
||||||
// is AEAD encrypted:
|
// is AEAD encrypted:
|
||||||
// AEAD nonce = header.salt || block# || block-size
|
// AEAD nonce = header.nonce || block#
|
||||||
|
// AD of AEAD = chunk length+eof marker
|
||||||
//
|
//
|
||||||
// The encrypted block (includes the AEAD tag) length is written
|
// The encrypted block (includes the AEAD tag) length is written
|
||||||
// as a big-endian 4-byte prefix. The high-order bit of this length
|
// as a big-endian 4-byte prefix. The high-order bit of this length
|
||||||
// field is set for the last-block (denoting EOF).
|
// field is set for the last-block (denoting EOF).
|
||||||
//
|
//
|
||||||
// The encrypted blocks use an opinionated nonce length of 32 (_AEADNonceLen).
|
|
||||||
|
|
||||||
package sign
|
package sign
|
||||||
|
|
||||||
|
@ -57,6 +64,7 @@ import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
@ -64,42 +72,55 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/opencoff/sigtool/internal/pb"
|
"github.com/opencoff/sigtool/internal/pb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encryption chunk size = 4MB
|
// Encryption chunk size = 4MB
|
||||||
const (
|
const (
|
||||||
chunkSize uint32 = 4 * 1048576
|
// The latest version of the tool's output file format
|
||||||
|
_SigtoolVersion = 3
|
||||||
|
|
||||||
|
chunkSize uint32 = 4 * 1048576 // 4 MB
|
||||||
maxChunkSize uint32 = 1 << 30
|
maxChunkSize uint32 = 1 << 30
|
||||||
_EOF uint32 = 1 << 31
|
_EOF uint32 = 1 << 31
|
||||||
|
|
||||||
_Magic = "SigTool"
|
_Magic = "SigTool"
|
||||||
_MagicLen = len(_Magic)
|
_MagicLen = len(_Magic)
|
||||||
_SigtoolVersion = 2
|
_FixedHdrLen = _MagicLen + 1 + 4 // 1: version, 4: len of variable segment
|
||||||
_AEADNonceLen = 32
|
|
||||||
_FixedHdrLen = _MagicLen + 1 + 4
|
|
||||||
|
|
||||||
_WrapReceiverNonce = "Receiver Key Nonce"
|
_AesKeySize = 32
|
||||||
_WrapSenderNonce = "Sender Sig Nonce"
|
_AEADNonceSize = 12
|
||||||
_EncryptNonce = "Encrypt Nonce"
|
_SaltSize = 32
|
||||||
|
_RxNonceSize = 12 // nonce size of per-recipient encrypted blocks
|
||||||
|
|
||||||
|
_WrapReceiver = "Receiver Key"
|
||||||
|
_WrapSender = "Sender Sig"
|
||||||
|
_DataKeyExpansion = "Data Key Expansion"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encryptor holds the encryption context
|
// Encryptor holds the encryption context
|
||||||
type Encryptor struct {
|
type Encryptor struct {
|
||||||
pb.Header
|
pb.Header
|
||||||
key []byte // file encryption key
|
key []byte // root key
|
||||||
|
|
||||||
|
nonce []byte // nonce for the data encrypting cipher
|
||||||
|
buf []byte // I/O buf (chunk-sized)
|
||||||
|
|
||||||
ae cipher.AEAD
|
ae cipher.AEAD
|
||||||
|
hmac hash.Hash
|
||||||
|
|
||||||
// ephemeral key
|
// ephemeral key
|
||||||
encSK []byte
|
encSK []byte
|
||||||
|
|
||||||
started bool
|
// sender identity
|
||||||
|
sender *PrivateKey
|
||||||
|
|
||||||
hdrsum []byte
|
auth bool // set if the sender idetity is sent
|
||||||
buf []byte
|
started bool
|
||||||
stream bool
|
stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,27 +144,23 @@ func NewEncryptor(sk *PrivateKey, blksize uint64) (*Encryptor, error) {
|
||||||
return nil, fmt.Errorf("encrypt: %w", err)
|
return nil, fmt.Errorf("encrypt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key := make([]byte, 32)
|
key := randBuf(_AesKeySize)
|
||||||
salt := make([]byte, _AEADNonceLen)
|
salt := randBuf(_SaltSize)
|
||||||
|
|
||||||
randRead(key)
|
|
||||||
randRead(salt)
|
|
||||||
|
|
||||||
wSig, err := wrapSenderSig(sk, key, salt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("encrypt: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
e := &Encryptor{
|
e := &Encryptor{
|
||||||
Header: pb.Header{
|
Header: pb.Header{
|
||||||
ChunkSize: blksz,
|
ChunkSize: blksz,
|
||||||
Salt: salt,
|
Salt: salt,
|
||||||
Pk: epk,
|
Pk: epk,
|
||||||
SenderSign: wSig,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
key: key,
|
key: key,
|
||||||
encSK: esk,
|
encSK: esk,
|
||||||
|
sender: sk,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = e.addSenderSig(sk); err != nil {
|
||||||
|
return nil, fmt.Errorf("encrypt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return e, nil
|
return e, nil
|
||||||
|
@ -210,8 +227,8 @@ func (e *Encryptor) start(wr io.Writer) error {
|
||||||
|
|
||||||
buffer := make([]byte, _FixedHdrLen+varSize+sha256.Size)
|
buffer := make([]byte, _FixedHdrLen+varSize+sha256.Size)
|
||||||
fixHdr := buffer[:_FixedHdrLen]
|
fixHdr := buffer[:_FixedHdrLen]
|
||||||
varHdr := buffer[_FixedHdrLen:]
|
varHdr := buffer[_FixedHdrLen : _FixedHdrLen+varSize]
|
||||||
sumHdr := varHdr[varSize:]
|
sumHdr := buffer[_FixedHdrLen+varSize:]
|
||||||
|
|
||||||
// Now assemble the fixed header
|
// Now assemble the fixed header
|
||||||
copy(fixHdr[:], []byte(_Magic))
|
copy(fixHdr[:], []byte(_Magic))
|
||||||
|
@ -224,38 +241,45 @@ func (e *Encryptor) start(wr io.Writer) error {
|
||||||
return fmt.Errorf("encrypt: can't marshal header: %w", err)
|
return fmt.Errorf("encrypt: can't marshal header: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now calculate checksum of everything
|
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(buffer[:_FixedHdrLen+varSize])
|
h.Write(buffer[:_FixedHdrLen+varSize])
|
||||||
h.Sum(sumHdr[:0])
|
cksum := h.Sum(sumHdr[:0])
|
||||||
|
|
||||||
// Finally write it out
|
// now make the data encryption keys, nonces etc.
|
||||||
|
outbuf := make([]byte, sha256.Size+_AesKeySize+_AEADNonceSize)
|
||||||
|
|
||||||
|
// we mix the header checksum (and it captures the sigtool version, sender
|
||||||
|
// identity, etc.)
|
||||||
|
buf := expand(outbuf, e.key, cksum, []byte(_DataKeyExpansion))
|
||||||
|
|
||||||
|
var dkey, hmackey []byte
|
||||||
|
|
||||||
|
e.nonce, buf = buf[:_AEADNonceSize], buf[_AEADNonceSize:]
|
||||||
|
dkey, buf = buf[:_AesKeySize], buf[_AesKeySize:]
|
||||||
|
hmackey = buf
|
||||||
|
|
||||||
|
aes, err := aes.NewCipher(dkey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("encrypt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.ae, err = cipher.NewGCM(aes); err != nil {
|
||||||
|
return fmt.Errorf("encrypt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally write out the header
|
||||||
err = fullwrite(buffer, wr)
|
err = fullwrite(buffer, wr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("encrypt: %w", err)
|
return fmt.Errorf("encrypt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we mix the header checksum to create the encryption key
|
e.hmac = hmac.New(sha256.New, hmackey)
|
||||||
h = sha256.New()
|
e.buf = make([]byte, e.ChunkSize+4+uint32(e.ae.Overhead()))
|
||||||
h.Write([]byte(_EncryptNonce))
|
|
||||||
h.Write(e.key)
|
|
||||||
h.Write(sumHdr)
|
|
||||||
key := h.Sum(nil)
|
|
||||||
|
|
||||||
aes, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encrypt: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ae, err := cipher.NewGCMWithNonceSize(aes, _AEADNonceLen)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encrypt: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.buf = make([]byte, e.ChunkSize+4+uint32(ae.Overhead()))
|
|
||||||
e.ae = ae
|
|
||||||
|
|
||||||
e.started = true
|
e.started = true
|
||||||
|
|
||||||
|
debug("encrypt:\n\thdr-cksum: %x\n\taes-key: %x\n\tnonce: %x\n\thmac-key: %x\n",
|
||||||
|
cksum, dkey, e.nonce, hmackey)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,30 +288,70 @@ func (e *Encryptor) start(wr io.Writer) error {
|
||||||
// This protects the output stream from re-ordering attacks and length
|
// This protects the output stream from re-ordering attacks and length
|
||||||
// modification attacks. The encoded length & block number is used as
|
// modification attacks. The encoded length & block number is used as
|
||||||
// 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(pt []byte, wr io.Writer, i uint32, eof bool) error {
|
||||||
var z uint32 = uint32(len(buf))
|
var z uint32 = uint32(len(pt))
|
||||||
var nbuf [_AEADNonceLen]byte
|
var nonce [_AEADNonceSize]byte
|
||||||
|
|
||||||
// mark last block
|
// mark last block
|
||||||
if eof {
|
if eof {
|
||||||
z |= _EOF
|
z |= _EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
b := e.buf[:8]
|
copy(nonce[:], e.nonce)
|
||||||
binary.BigEndian.PutUint32(b[:4], z)
|
|
||||||
binary.BigEndian.PutUint32(b[4:], i)
|
|
||||||
|
|
||||||
nonce := makeNonceV2(nbuf[:], e.Salt, b)
|
// now change the upper bytes to track the block#; we use the len+eof as AD
|
||||||
|
binary.BigEndian.PutUint32(nonce[:4], i)
|
||||||
|
|
||||||
cbuf := e.buf[4:]
|
// put the encoded length+eof at the start of the output buf
|
||||||
c := e.ae.Seal(cbuf[:0], nonce, buf, b[:])
|
b := e.buf[:4]
|
||||||
|
ctbuf := e.buf[4:]
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(b, z)
|
||||||
|
ct := e.ae.Seal(ctbuf[:0], nonce[:], pt, b)
|
||||||
|
|
||||||
// total number of bytes written
|
// total number of bytes written
|
||||||
n := len(c) + 4
|
n := len(ct) + 4
|
||||||
err := fullwrite(e.buf[:n], wr)
|
err := fullwrite(e.buf[:n], wr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("encrypt: %w", err)
|
return fmt.Errorf("encrypt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.hmac.Write(b)
|
||||||
|
e.hmac.Write(pt)
|
||||||
|
|
||||||
|
if eof {
|
||||||
|
return e.writeTrailer(wr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a trailer:
|
||||||
|
// - if authenticating sender, sign the hmac and put the signature in the trailer
|
||||||
|
// - if not authenticating sender, write random bytes to the trailer
|
||||||
|
func (e *Encryptor) writeTrailer(wr io.Writer) error {
|
||||||
|
var tr []byte
|
||||||
|
|
||||||
|
switch e.auth {
|
||||||
|
case true:
|
||||||
|
var hmac [sha256.Size]byte
|
||||||
|
|
||||||
|
e.hmac.Sum(hmac[:0])
|
||||||
|
|
||||||
|
// We know sender is non null.
|
||||||
|
sig, err := e.sender.SignMessage(hmac[:], "")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("encrypt: trailer: %w", err)
|
||||||
|
}
|
||||||
|
tr = sig.Sig
|
||||||
|
|
||||||
|
case false:
|
||||||
|
tr = randBuf(ed25519.SignatureSize)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fullwrite(tr, wr); err != nil {
|
||||||
|
return fmt.Errorf("encrypt: trailer %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,15 +360,17 @@ type Decryptor struct {
|
||||||
pb.Header
|
pb.Header
|
||||||
|
|
||||||
ae cipher.AEAD
|
ae cipher.AEAD
|
||||||
|
hmac hash.Hash
|
||||||
|
|
||||||
|
sender *PublicKey
|
||||||
|
|
||||||
rd io.Reader
|
rd io.Reader
|
||||||
buf []byte
|
buf []byte
|
||||||
hdrsum []byte
|
nonce []byte // nonce for the data decrypting cipher
|
||||||
|
|
||||||
// flag set to true if sender signed the key
|
key []byte // Decrypted root key
|
||||||
auth bool
|
hdrsum []byte // cached header checksum
|
||||||
|
auth bool // flag set to true if sender signed the key
|
||||||
// Decrypted key
|
|
||||||
key []byte
|
|
||||||
eof bool
|
eof bool
|
||||||
stream bool
|
stream bool
|
||||||
}
|
}
|
||||||
|
@ -347,14 +413,18 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
|
||||||
return nil, fmt.Errorf("decrypt: error while reading header: %w", err)
|
return nil, fmt.Errorf("decrypt: error while reading header: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The checksum in the header
|
||||||
verify := varBuf[varSize:]
|
verify := varBuf[varSize:]
|
||||||
|
|
||||||
|
// the checksum we calculated
|
||||||
|
var csum [sha256.Size]byte
|
||||||
|
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(b[:])
|
h.Write(b[:])
|
||||||
h.Write(varBuf[:varSize])
|
h.Write(varBuf[:varSize])
|
||||||
cksum := h.Sum(nil)
|
cksum := h.Sum(csum[:0])
|
||||||
|
|
||||||
if subtle.ConstantTimeCompare(verify, cksum[:]) == 0 {
|
if subtle.ConstantTimeCompare(verify, cksum) == 0 {
|
||||||
return nil, ErrBadHeader
|
return nil, ErrBadHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +442,7 @@ func NewDecryptor(rd io.Reader) (*Decryptor, error) {
|
||||||
return nil, fmt.Errorf("decrypt: invalid chunkSize %d", d.ChunkSize)
|
return nil, fmt.Errorf("decrypt: invalid chunkSize %d", d.ChunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(d.Salt) != _AEADNonceLen {
|
if len(d.Salt) != _SaltSize {
|
||||||
return nil, fmt.Errorf("decrypt: invalid nonce length %d", len(d.Salt))
|
return nil, fmt.Errorf("decrypt: invalid nonce length %d", len(d.Salt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +452,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.DKey) <= 32 {
|
if len(w.DKey) <= _AesKeySize {
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,6 +472,8 @@ func (d *Decryptor) SetPrivateKey(sk *PrivateKey, senderPk *PublicKey) error {
|
||||||
return fmt.Errorf("decrypt: can't unwrap key %d: %w", i, err)
|
return fmt.Errorf("decrypt: can't unwrap key %d: %w", i, err)
|
||||||
}
|
}
|
||||||
if key != nil {
|
if key != nil {
|
||||||
|
d.key = key
|
||||||
|
d.sender = senderPk
|
||||||
goto havekey
|
goto havekey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,28 +481,37 @@ func (d *Decryptor) SetPrivateKey(sk *PrivateKey, senderPk *PublicKey) error {
|
||||||
return ErrBadKey
|
return ErrBadKey
|
||||||
|
|
||||||
havekey:
|
havekey:
|
||||||
if err := d.verifySender(key, sk, senderPk); err != nil {
|
if err := d.verifySender(key, senderPk); err != nil {
|
||||||
return fmt.Errorf("decrypt: %w", err)
|
return fmt.Errorf("decrypt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.key = key
|
outbuf := make([]byte, sha256.Size+_AesKeySize+_AEADNonceSize)
|
||||||
|
|
||||||
// we mix the header checksum into the key
|
buf := expand(outbuf, d.key, d.hdrsum, []byte(_DataKeyExpansion))
|
||||||
h := sha256.New()
|
|
||||||
h.Write([]byte(_EncryptNonce))
|
|
||||||
h.Write(d.key)
|
|
||||||
h.Write(d.hdrsum)
|
|
||||||
key = h.Sum(nil)
|
|
||||||
|
|
||||||
aes, err := aes.NewCipher(key)
|
var dkey, hmackey []byte
|
||||||
|
|
||||||
|
d.nonce, buf = buf[:_AEADNonceSize], buf[_AEADNonceSize:]
|
||||||
|
dkey, buf = buf[:_AesKeySize], buf[_AesKeySize:]
|
||||||
|
hmackey = buf
|
||||||
|
|
||||||
|
d.hmac = hmac.New(sha256.New, hmackey)
|
||||||
|
|
||||||
|
aes, err := aes.NewCipher(dkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("decrypt: %w", err)
|
return fmt.Errorf("decrypt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.ae, err = cipher.NewGCMWithNonceSize(aes, _AEADNonceLen)
|
d.ae, err = cipher.NewGCM(aes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("decrypt: %w", err)
|
return fmt.Errorf("decrypt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug("decrypt:\n\thdr-cksum: %x\n\taes-key: %x\n\tnonce: %x\n\thmac-key: %x\n",
|
||||||
|
d.hdrsum, dkey, d.nonce, hmackey)
|
||||||
|
|
||||||
|
// We have a separate on-stack buffer for reading the header (4 bytes).
|
||||||
|
// Thus, the actual I/O buf will never be larger than the chunksize + AEAD Overhead
|
||||||
d.buf = make([]byte, int(d.ChunkSize)+d.ae.Overhead())
|
d.buf = make([]byte, int(d.ChunkSize)+d.ae.Overhead())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -477,17 +558,16 @@ func (d *Decryptor) Decrypt(wr io.Writer) error {
|
||||||
|
|
||||||
// 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 nonceb [32]byte
|
|
||||||
var ovh uint32 = uint32(d.ae.Overhead())
|
var ovh uint32 = uint32(d.ae.Overhead())
|
||||||
var p []byte
|
var b [4]byte
|
||||||
|
var nonce [_AEADNonceSize]byte
|
||||||
|
|
||||||
n, err := io.ReadFull(d.rd, b[:4])
|
n, err := io.ReadFull(d.rd, b[:])
|
||||||
if err != nil || n == 0 {
|
if err != nil || n == 0 {
|
||||||
return nil, false, fmt.Errorf("decrypt: premature EOF while reading header block %d", i)
|
return nil, false, fmt.Errorf("decrypt: premature EOF while reading header block %d", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := binary.BigEndian.Uint32(b[:4])
|
m := binary.BigEndian.Uint32(b[:])
|
||||||
eof := (m & _EOF) > 0
|
eof := (m & _EOF) > 0
|
||||||
m &= (_EOF - 1)
|
m &= (_EOF - 1)
|
||||||
|
|
||||||
|
@ -500,13 +580,14 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
|
||||||
if !eof {
|
if !eof {
|
||||||
return nil, false, fmt.Errorf("decrypt: block %d: zero-sized chunk without EOF", i)
|
return nil, false, fmt.Errorf("decrypt: block %d: zero-sized chunk without EOF", i)
|
||||||
}
|
}
|
||||||
return p, eof, nil
|
return nil, eof, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(b[4:], i)
|
// make the nonce - top 4 bytes are the counter
|
||||||
nonce := makeNonceV2(nonceb[:], d.Salt, b[:])
|
copy(nonce[:], d.nonce)
|
||||||
|
binary.BigEndian.PutUint32(nonce[:4], i)
|
||||||
|
|
||||||
z := m + ovh
|
z := m + ovh
|
||||||
n, err = io.ReadFull(d.rd, d.buf[:z])
|
n, err = io.ReadFull(d.rd, d.buf[:z])
|
||||||
|
@ -514,57 +595,115 @@ func (d *Decryptor) decrypt(i uint32) ([]byte, bool, error) {
|
||||||
return nil, false, fmt.Errorf("decrypt: premature EOF while reading block %d: %w", i, err)
|
return nil, false, fmt.Errorf("decrypt: premature EOF while reading block %d: %w", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err = d.ae.Open(d.buf[:0], nonce, d.buf[:n], b[:])
|
pt, err := d.ae.Open(d.buf[:0], nonce[:], d.buf[:n], b[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("decrypt: can't decrypt chunk %d: %w", i, err)
|
return nil, false, fmt.Errorf("decrypt: can't decrypt chunk %d: %w", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p[:m], eof, nil
|
if uint32(len(pt)) != m {
|
||||||
|
return nil, false, fmt.Errorf("decrypt: partial unsealed bytes; exp %d, saw %d", m, len(pt))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap sender's signature of the encryption key
|
d.hmac.Write(b[:])
|
||||||
// if sender has provided their identity to authenticate, we sign the data-enc key
|
d.hmac.Write(pt)
|
||||||
// and encrypt the signature. At no point will we send the sender's identity.
|
|
||||||
func wrapSenderSig(sk *PrivateKey, key, salt []byte) ([]byte, error) {
|
if eof {
|
||||||
|
return d.processTrailer(pt, eof)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pt, eof, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decryptor) processTrailer(pt []byte, eof bool) ([]byte, bool, error) {
|
||||||
|
var rd [ed25519.SignatureSize]byte
|
||||||
|
|
||||||
|
_, err := io.ReadFull(d.rd, rd[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("decrypt: premature EOF while reading trailer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.auth {
|
||||||
|
// these are random bytes; ignore em
|
||||||
|
return pt, eof, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var hmac [sha256.Size]byte
|
||||||
|
|
||||||
|
cksum := d.hmac.Sum(hmac[:0])
|
||||||
|
ss := &Signature{
|
||||||
|
Sig: rd[:],
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := d.sender.VerifyMessage(cksum, ss); !ok {
|
||||||
|
return nil, eof, ErrBadTrailer
|
||||||
|
}
|
||||||
|
|
||||||
|
return pt, eof, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// optionally sign the checksum and encrypt everything
|
||||||
|
func (e *Encryptor) addSenderSig(sk *PrivateKey) error {
|
||||||
var zero [ed25519.SignatureSize]byte
|
var zero [ed25519.SignatureSize]byte
|
||||||
var sig []byte
|
var auth bool
|
||||||
|
sig := zero[:]
|
||||||
|
|
||||||
switch {
|
if e.sender != nil {
|
||||||
case sk == nil:
|
var csum [sha256.Size]byte
|
||||||
sig = zero[:]
|
|
||||||
|
|
||||||
default:
|
// We capture essential meta-data from the sender; viz:
|
||||||
xsig, err := sk.SignMessage(key, "")
|
// - Sender tool version
|
||||||
|
// - Sender generated curve25519 PK
|
||||||
|
// - session salt, root key
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte(_Magic))
|
||||||
|
h.Write([]byte{_SigtoolVersion})
|
||||||
|
h.Write(e.Pk)
|
||||||
|
h.Write(e.Salt)
|
||||||
|
h.Write(e.key)
|
||||||
|
cksum := h.Sum(csum[:0])
|
||||||
|
|
||||||
|
xsig, err := e.sender.SignMessage(cksum, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wrap: can't sign: %w", err)
|
return fmt.Errorf("wrap: can't sign: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = xsig.Sig
|
sig = xsig.Sig
|
||||||
|
auth = true
|
||||||
}
|
}
|
||||||
|
|
||||||
aes, err := aes.NewCipher(key)
|
buf := make([]byte, _AesKeySize+_AEADNonceSize)
|
||||||
|
buf = expand(buf, e.key, e.Salt, []byte(_WrapSender))
|
||||||
|
|
||||||
|
ekey, nonce := buf[:_AesKeySize], buf[_AesKeySize:]
|
||||||
|
|
||||||
|
aes, err := aes.NewCipher(ekey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wrap: %w", err)
|
return fmt.Errorf("senderId: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ae, err := cipher.NewGCM(aes)
|
ae, err := cipher.NewGCM(aes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wrap: %w", err)
|
return fmt.Errorf("senderId: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tagsize := ae.Overhead()
|
outbuf := make([]byte, ed25519.SignatureSize+ae.Overhead())
|
||||||
nonceSize := ae.NonceSize()
|
buf = ae.Seal(outbuf[:0], nonce, sig, nil)
|
||||||
|
|
||||||
nonce := sha256Slices([]byte(_WrapSenderNonce), salt)[:nonceSize]
|
e.auth = auth
|
||||||
esig := make([]byte, tagsize+len(sig))
|
e.Sender = buf
|
||||||
|
|
||||||
return ae.Seal(esig[:0], nonce, sig, nil), nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unwrap sender's signature using 'key' and extract the signature
|
// unwrap sender's signature using 'key' and extract the signature
|
||||||
// Optionally, verify the signature using the sender's PK (if provided).
|
// Optionally, verify the signature using the sender's PK (if provided).
|
||||||
func (d *Decryptor) verifySender(key []byte, sk *PrivateKey, senderPK *PublicKey) error {
|
func (d *Decryptor) verifySender(key []byte, senderPk *PublicKey) error {
|
||||||
aes, err := aes.NewCipher(key)
|
outbuf := make([]byte, _AEADNonceSize+_AesKeySize)
|
||||||
|
buf := expand(outbuf, key, d.Salt, []byte(_WrapSender))
|
||||||
|
|
||||||
|
ekey, nonce := buf[:_AesKeySize], buf[_AesKeySize:]
|
||||||
|
|
||||||
|
aes, err := aes.NewCipher(ekey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unwrap: %w", err)
|
return fmt.Errorf("unwrap: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -574,31 +713,42 @@ func (d *Decryptor) verifySender(key []byte, sk *PrivateKey, senderPK *PublicKey
|
||||||
return fmt.Errorf("unwrap: %w", err)
|
return fmt.Errorf("unwrap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nonceSize := ae.NonceSize()
|
var sigbuf [ed25519.SignatureSize]byte
|
||||||
nonce := sha256Slices([]byte(_WrapSenderNonce), d.Salt)[:nonceSize]
|
var zero [ed25519.SignatureSize]byte
|
||||||
sig := make([]byte, ed25519.SignatureSize)
|
|
||||||
sig, err = ae.Open(sig[:0], nonce, d.SenderSign, nil)
|
sig, err := ae.Open(sigbuf[:0], nonce, d.Sender, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unwrap: can't open sender info: %w", err)
|
return fmt.Errorf("unwrap: can't open sender info: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var zero [ed25519.SignatureSize]byte
|
|
||||||
|
|
||||||
// Did the sender actually sign anything?
|
// Did the sender actually sign anything?
|
||||||
if subtle.ConstantTimeCompare(zero[:], sig) == 0 {
|
if subtle.ConstantTimeCompare(zero[:], sig) == 0 {
|
||||||
// we set this to indicate that the sender authenticated themselves;
|
if senderPk == nil {
|
||||||
d.auth = true
|
return ErrNoSenderPK
|
||||||
|
}
|
||||||
|
|
||||||
|
var csum [sha256.Size]byte
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte(_Magic))
|
||||||
|
h.Write([]byte{_SigtoolVersion})
|
||||||
|
h.Write(d.Pk)
|
||||||
|
h.Write(d.Salt)
|
||||||
|
h.Write(key)
|
||||||
|
cksum := h.Sum(csum[:0])
|
||||||
|
|
||||||
if senderPK != nil {
|
|
||||||
ss := &Signature{
|
ss := &Signature{
|
||||||
Sig: sig,
|
Sig: sig,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok := senderPK.VerifyMessage(key, ss); !ok {
|
if ok := senderPk.VerifyMessage(cksum, ss); !ok {
|
||||||
return fmt.Errorf("unwrap: sender verification failed")
|
return ErrBadSender
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we set this to indicate that the sender authenticated themselves;
|
||||||
|
d.auth = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,12 +756,26 @@ func (d *Decryptor) verifySender(key []byte, sk *PrivateKey, senderPK *PublicKey
|
||||||
// basically, we do a scalarmult: Ephemeral encryption/decryption SK x receiver PK
|
// basically, we do a scalarmult: Ephemeral encryption/decryption SK x receiver PK
|
||||||
func (e *Encryptor) wrapKey(pk *PublicKey) (*pb.WrappedKey, error) {
|
func (e *Encryptor) wrapKey(pk *PublicKey) (*pb.WrappedKey, error) {
|
||||||
rxPK := pk.ToCurve25519PK()
|
rxPK := pk.ToCurve25519PK()
|
||||||
dkek, err := curve25519.X25519(e.encSK, rxPK)
|
sekrit, err := curve25519.X25519(e.encSK, rxPK)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wrap: %w", err)
|
return nil, fmt.Errorf("wrap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
aes, err := aes.NewCipher(dkek)
|
var shasum [sha256.Size]byte
|
||||||
|
|
||||||
|
rbuf := randBuf(_RxNonceSize)
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write(e.Salt)
|
||||||
|
h.Write(rbuf[:])
|
||||||
|
h.Sum(shasum[:0])
|
||||||
|
|
||||||
|
out := make([]byte, _AesKeySize+_RxNonceSize)
|
||||||
|
buf := expand(out[:], sekrit, shasum[:], []byte(_WrapReceiver))
|
||||||
|
|
||||||
|
kek, nonce := buf[:_AesKeySize], buf[_AesKeySize:]
|
||||||
|
|
||||||
|
aes, err := aes.NewCipher(kek)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wrap: %w", err)
|
return nil, fmt.Errorf("wrap: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -621,14 +785,10 @@ func (e *Encryptor) wrapKey(pk *PublicKey) (*pb.WrappedKey, error) {
|
||||||
return nil, fmt.Errorf("wrap: %w", err)
|
return nil, fmt.Errorf("wrap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tagsize := ae.Overhead()
|
ekey := make([]byte, ae.Overhead()+len(e.key))
|
||||||
nonceSize := ae.NonceSize()
|
|
||||||
|
|
||||||
nonceR := sha256Slices([]byte(_WrapReceiverNonce), e.Salt)[:nonceSize]
|
|
||||||
ekey := make([]byte, tagsize+len(e.key))
|
|
||||||
|
|
||||||
w := &pb.WrappedKey{
|
w := &pb.WrappedKey{
|
||||||
DKey: ae.Seal(ekey[:0], nonceR, e.key, pk.Pk),
|
DKey: ae.Seal(ekey[:0], nonce, e.key, pk.Pk),
|
||||||
|
Nonce: rbuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
return w, nil
|
return w, nil
|
||||||
|
@ -638,40 +798,48 @@ func (e *Encryptor) wrapKey(pk *PublicKey) (*pb.WrappedKey, error) {
|
||||||
// senders ephemeral PublicKey
|
// senders ephemeral PublicKey
|
||||||
func (d *Decryptor) unwrapKey(w *pb.WrappedKey, sk *PrivateKey) ([]byte, error) {
|
func (d *Decryptor) unwrapKey(w *pb.WrappedKey, sk *PrivateKey) ([]byte, error) {
|
||||||
ourSK := sk.ToCurve25519SK()
|
ourSK := sk.ToCurve25519SK()
|
||||||
dkek, err := curve25519.X25519(ourSK, d.Pk)
|
sekrit, err := curve25519.X25519(ourSK, d.Pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unwrap: %w", err)
|
return nil, fmt.Errorf("unwrap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
aes, err := aes.NewCipher(dkek)
|
var shasum [sha256.Size]byte
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write(d.Salt)
|
||||||
|
h.Write(w.Nonce)
|
||||||
|
h.Sum(shasum[:0])
|
||||||
|
|
||||||
|
out := make([]byte, _AesKeySize+_RxNonceSize)
|
||||||
|
buf := expand(out[:], sekrit, shasum[:], []byte(_WrapReceiver))
|
||||||
|
|
||||||
|
kek, nonce := buf[:_AesKeySize], buf[_AesKeySize:]
|
||||||
|
|
||||||
|
aes, err := aes.NewCipher(kek)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unwrap: %w", err)
|
return nil, fmt.Errorf("wrap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ae, err := cipher.NewGCM(aes)
|
ae, err := cipher.NewGCM(aes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unwrap: %w", err)
|
return nil, fmt.Errorf("wrap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 32 == AES-256 key size
|
want := _AesKeySize + ae.Overhead()
|
||||||
want := 32 + ae.Overhead()
|
|
||||||
if len(w.DKey) != want {
|
if len(w.DKey) != want {
|
||||||
return nil, fmt.Errorf("unwrap: incorrect decrypt bytes (need %d, saw %d)", want, len(w.DKey))
|
return nil, fmt.Errorf("unwrap: incorrect decrypt bytes (need %d, saw %d)", want, len(w.DKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
nonceSize := ae.NonceSize()
|
|
||||||
|
|
||||||
nonceR := sha256Slices([]byte(_WrapReceiverNonce), d.Salt)[:nonceSize]
|
|
||||||
pk := sk.PublicKey()
|
pk := sk.PublicKey()
|
||||||
|
dkey := make([]byte, _AesKeySize) // decrypted data decryption key
|
||||||
dkey := make([]byte, 32) // decrypted data decryption key
|
|
||||||
|
|
||||||
// we indicate incorrect receiver SK by returning a nil key
|
// we indicate incorrect receiver SK by returning a nil key
|
||||||
dkey, err = ae.Open(dkey[:0], nonceR, w.DKey, pk.Pk)
|
dkey, err = ae.Open(dkey[:0], nonce, w.DKey, pk.Pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we have successfully found the correct recipient
|
||||||
return dkey, nil
|
return dkey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,30 +859,14 @@ func fullwrite(buf []byte, wr io.Writer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// make aead nonce from salt, chunk-size and block#
|
|
||||||
// First 8 bytes are chunk-size and nonce (in 'ad')
|
|
||||||
func makeNonceV2(dest []byte, salt []byte, ad []byte) []byte {
|
|
||||||
n := len(ad)
|
|
||||||
copy(dest, ad)
|
|
||||||
copy(dest[n:], salt)
|
|
||||||
return dest
|
|
||||||
}
|
|
||||||
|
|
||||||
// make aead nonce from salt, chunk-size and block# for v1
|
|
||||||
// This is here for historical documentation purposes
|
|
||||||
func makeNonceV1(dest []byte, salt []byte, ad []byte) []byte {
|
|
||||||
h := sha256.New()
|
|
||||||
h.Write(salt)
|
|
||||||
h.Write(ad)
|
|
||||||
return h.Sum(dest[:0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a KEK from a shared DH key and a Pub Key
|
// generate a KEK from a shared DH key and a Pub Key
|
||||||
func expand(shared, pk []byte) ([]byte, error) {
|
func expand(out []byte, shared, salt, ad []byte) []byte {
|
||||||
kek := make([]byte, 32)
|
h := hkdf.New(sha512.New, shared, salt, ad)
|
||||||
h := hkdf.New(sha512.New, shared, pk, nil)
|
_, err := io.ReadFull(h, out)
|
||||||
_, err := io.ReadFull(h, kek)
|
if err != nil {
|
||||||
return kek, err
|
panic(fmt.Sprintf("hkdf: failed to generate %d bytes: %s", len(out), err))
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSender() (sk, pk []byte, err error) {
|
func newSender() (sk, pk []byte, err error) {
|
||||||
|
@ -736,4 +888,25 @@ func sha256Slices(v ...[]byte) []byte {
|
||||||
return h.Sum(nil)[:]
|
return h.Sum(nil)[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _debug int = 0
|
||||||
|
|
||||||
|
// Enable debugging of this module;
|
||||||
|
// level > 0 elicits debug messages on os.Stderr
|
||||||
|
func Debug(level int) {
|
||||||
|
_debug = level
|
||||||
|
}
|
||||||
|
|
||||||
|
func debug(s string, v ...interface{}) {
|
||||||
|
if _debug <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
z := fmt.Sprintf(s, v...)
|
||||||
|
if n := len(z); z[n-1] != '\n' {
|
||||||
|
z += "\n"
|
||||||
|
}
|
||||||
|
os.Stderr.WriteString(z)
|
||||||
|
os.Stderr.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
|
|
@ -20,17 +20,19 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrClosed = errors.New("encrypt: stream already closed")
|
ErrClosed = errors.New("encrypt: stream already closed")
|
||||||
ErrNoKey = errors.New("decrypt: No private key set for decryption")
|
ErrNoKey = errors.New("decrypt: no private key set for decryption")
|
||||||
ErrEncStarted = errors.New("encrypt: can't add new recipient after encryption has started")
|
ErrEncStarted = errors.New("encrypt: can't add new recipient after encryption has started")
|
||||||
ErrDecStarted = errors.New("decrypt: can't add new recipient after decryption has started")
|
ErrDecStarted = errors.New("decrypt: can't add new recipient after decryption has started")
|
||||||
ErrEncIsStream = errors.New("encrypt: can't use Encrypt() after using streaming I/O")
|
ErrEncIsStream = errors.New("encrypt: can't use Encrypt() after using streaming I/O")
|
||||||
ErrNotSigTool = errors.New("decrypt: Not a sigtool encrypted file?")
|
ErrNotSigTool = errors.New("decrypt: not a sigtool encrypted file?")
|
||||||
ErrHeaderTooBig = errors.New("decrypt: header too large (max 1048576)")
|
ErrHeaderTooBig = errors.New("decrypt: header too large (max 1048576)")
|
||||||
ErrHeaderTooSmall = errors.New("decrypt: header too small (min 32)")
|
ErrHeaderTooSmall = errors.New("decrypt: header too small (min 32)")
|
||||||
ErrBadHeader = errors.New("decrypt: header corrupted")
|
ErrBadHeader = errors.New("decrypt: header corrupted")
|
||||||
ErrNoWrappedKeys = errors.New("decrypt: No wrapped keys in encrypted file")
|
ErrNoWrappedKeys = errors.New("decrypt: no wrapped keys in encrypted file")
|
||||||
ErrBadKey = errors.New("decrypt: wrong key")
|
ErrBadKey = errors.New("decrypt: wrong key")
|
||||||
|
ErrBadTrailer = errors.New("decrypt: message integrity failed (bad trailer)")
|
||||||
ErrBadSender = errors.New("unwrap: sender verification failed")
|
ErrBadSender = errors.New("unwrap: sender verification failed")
|
||||||
|
ErrNoSenderPK = errors.New("unwrap: missing sender public key")
|
||||||
|
|
||||||
ErrIncorrectPassword = errors.New("ssh: invalid passphrase")
|
ErrIncorrectPassword = errors.New("ssh: invalid passphrase")
|
||||||
ErrNoPEMFound = errors.New("ssh: no PEM block found")
|
ErrNoPEMFound = errors.New("ssh: no PEM block found")
|
||||||
|
|
|
@ -38,3 +38,8 @@ func randRead(b []byte) []byte {
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randBuf(sz int) []byte {
|
||||||
|
b := make([]byte, sz)
|
||||||
|
return randRead(b)
|
||||||
|
}
|
||||||
|
|
10
sigtool.go
10
sigtool.go
|
@ -30,12 +30,13 @@ var Z string = path.Base(os.Args[0])
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var ver, help bool
|
var ver, help, debug bool
|
||||||
|
|
||||||
mf := flag.NewFlagSet(Z, flag.ExitOnError)
|
mf := flag.NewFlagSet(Z, flag.ExitOnError)
|
||||||
mf.SetInterspersed(false)
|
mf.SetInterspersed(false)
|
||||||
mf.BoolVarP(&ver, "version", "v", false, "Show version info and exit")
|
mf.BoolVarP(&ver, "version", "v", false, "Show version info and exit")
|
||||||
mf.BoolVarP(&help, "help", "h", false, "Show help info exit")
|
mf.BoolVarP(&help, "help", "h", false, "Show help info exit")
|
||||||
|
mf.BoolVarP(&debug, "debug", "", false, "Enable debug mode")
|
||||||
mf.Parse(os.Args[1:])
|
mf.Parse(os.Args[1:])
|
||||||
|
|
||||||
if ver {
|
if ver {
|
||||||
|
@ -80,6 +81,10 @@ func main() {
|
||||||
Die("can't map command %s", canon)
|
Die("can't map command %s", canon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
sign.Debug(1)
|
||||||
|
}
|
||||||
|
|
||||||
cmd(args[1:])
|
cmd(args[1:])
|
||||||
|
|
||||||
// always call Exit so that at-exit handlers are called.
|
// always call Exit so that at-exit handlers are called.
|
||||||
|
@ -323,7 +328,8 @@ Usage: %s [global-options] command [options] arg [args..]
|
||||||
|
|
||||||
Global options:
|
Global options:
|
||||||
-h, --help Show help and exit
|
-h, --help Show help and exit
|
||||||
-v, --version Show version info and exit.
|
-v, --version Show version info and exit
|
||||||
|
--debug Enable debug (DANGEROUS)
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
generate, g Generate a new Ed25519 keypair
|
generate, g Generate a new Ed25519 keypair
|
||||||
|
|
Loading…
Add table
Reference in a new issue