From 698e92c3763fed737250728c80d940e420d9f144 Mon Sep 17 00:00:00 2001 From: Ben Burkert Date: Tue, 25 Aug 2015 22:14:52 -0700 Subject: [PATCH] material: add Material type Material holds non-secret encryption data; usually ciphertext. It is used to store non sensitive data in the vault used for input to unlock a vault. --- material/db.go | 10 + material/material.go | 43 +++++ material/material.pb.go | 389 ++++++++++++++++++++++++++++++++++++++ material/material.proto | 18 ++ material/material_test.go | 31 +++ vcrypt.go | 1 + 6 files changed, 492 insertions(+) create mode 100644 material/db.go create mode 100644 material/material.go create mode 100644 material/material.pb.go create mode 100644 material/material.proto create mode 100644 material/material_test.go diff --git a/material/db.go b/material/db.go new file mode 100644 index 0000000..57f6040 --- /dev/null +++ b/material/db.go @@ -0,0 +1,10 @@ +package material + +// DB interface for loading and storing material. +type DB interface { + // LoadMaterial retrieves Material data from a backing store. + LoadMaterial([]byte) (*Material, error) + + // StoreMaterial saves Material data to a backing store. + StoreMaterial(*Material) error +} diff --git a/material/material.go b/material/material.go new file mode 100644 index 0000000..45730cb --- /dev/null +++ b/material/material.go @@ -0,0 +1,43 @@ +package material + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "io" +) + +// New constructs a new Material for an id & data. +func New(id []byte, data [][]byte) (*Material, error) { + nonce := make([]byte, 24) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + return &Material{ + Data: data, + ID: id, + Nonce: nonce, + }, nil +} + +// Comment string +func (m *Material) Comment() string { return m.comment } + +// Digest returns a unique series of bytes that identify the Material. +func (m *Material) Digest() ([]byte, error) { + // SHA256(Nonce,ID|Data[*]) + hash := hmac.New(sha256.New, m.Nonce) + + if _, err := hash.Write(m.ID); err != nil { + return nil, err + } + + for _, chunk := range m.Data { + if _, err := hash.Write(chunk); err != nil { + return nil, err + } + } + + return hash.Sum(nil), nil +} diff --git a/material/material.pb.go b/material/material.pb.go new file mode 100644 index 0000000..31b23d3 --- /dev/null +++ b/material/material.pb.go @@ -0,0 +1,389 @@ +// Code generated by protoc-gen-gogo. +// source: material/material.proto +// DO NOT EDIT! + +/* + Package material is a generated protocol buffer package. + + It is generated from these files: + material/material.proto + + It has these top-level messages: + Material +*/ +package material + +import proto "github.com/gogo/protobuf/proto" + +// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto" + +import io "io" +import fmt "fmt" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal + +type Material struct { + Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"` + comment string `protobuf:"bytes,2,opt,name=comment,proto3" json:"comment,omitempty"` + ID []byte `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + Data [][]byte `protobuf:"bytes,4,rep,name=data" json:"data,omitempty"` +} + +func (m *Material) Reset() { *m = Material{} } +func (m *Material) String() string { return proto.CompactTextString(m) } +func (*Material) ProtoMessage() {} + +func (m *Material) 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 *Material) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Nonce != nil { + if len(m.Nonce) > 0 { + data[i] = 0xa + i++ + i = encodeVarintMaterial(data, i, uint64(len(m.Nonce))) + i += copy(data[i:], m.Nonce) + } + } + if len(m.comment) > 0 { + data[i] = 0x12 + i++ + i = encodeVarintMaterial(data, i, uint64(len(m.comment))) + i += copy(data[i:], m.comment) + } + if m.ID != nil { + if len(m.ID) > 0 { + data[i] = 0x1a + i++ + i = encodeVarintMaterial(data, i, uint64(len(m.ID))) + i += copy(data[i:], m.ID) + } + } + if len(m.Data) > 0 { + for _, b := range m.Data { + data[i] = 0x22 + i++ + i = encodeVarintMaterial(data, i, uint64(len(b))) + i += copy(data[i:], b) + } + } + return i, nil +} + +func encodeFixed64Material(data []byte, offset int, v uint64) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + data[offset+4] = uint8(v >> 32) + data[offset+5] = uint8(v >> 40) + data[offset+6] = uint8(v >> 48) + data[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Material(data []byte, offset int, v uint32) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintMaterial(data []byte, offset int, v uint64) int { + for v >= 1<<7 { + data[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + data[offset] = uint8(v) + return offset + 1 +} +func (m *Material) Size() (n int) { + var l int + _ = l + if m.Nonce != nil { + l = len(m.Nonce) + if l > 0 { + n += 1 + l + sovMaterial(uint64(l)) + } + } + l = len(m.comment) + if l > 0 { + n += 1 + l + sovMaterial(uint64(l)) + } + if m.ID != nil { + l = len(m.ID) + if l > 0 { + n += 1 + l + sovMaterial(uint64(l)) + } + } + if len(m.Data) > 0 { + for _, b := range m.Data { + l = len(b) + n += 1 + l + sovMaterial(uint64(l)) + } + } + return n +} + +func sovMaterial(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozMaterial(x uint64) (n int) { + return sovMaterial(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Material) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + 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) + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMaterial + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nonce = append([]byte{}, data[iNdEx:postIndex]...) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field comment", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + postIndex := iNdEx + int(stringLen) + if stringLen < 0 { + return ErrInvalidLengthMaterial + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.comment = string(data[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMaterial + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = append([]byte{}, data[iNdEx:postIndex]...) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMaterial + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data, make([]byte, postIndex-iNdEx)) + copy(m.Data[len(m.Data)-1], data[iNdEx:postIndex]) + iNdEx = postIndex + default: + var sizeOfWire int + for { + sizeOfWire++ + wire >>= 7 + if wire == 0 { + break + } + } + iNdEx -= sizeOfWire + skippy, err := skipMaterial(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMaterial + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + return nil +} +func skipMaterial(data []byte) (n int, err error) { + l := len(data) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for { + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if data[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthMaterial + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipMaterial(data[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthMaterial = fmt.Errorf("proto: negative length found during unmarshaling") +) diff --git a/material/material.proto b/material/material.proto new file mode 100644 index 0000000..966907a --- /dev/null +++ b/material/material.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package material; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.goproto_enum_prefix_all) = false; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.sizer_all) = true; + +message Material { + bytes nonce = 1; + string comment = 2 [(gogoproto.customname) = "comment"]; + bytes id = 3 [(gogoproto.customname) = "ID"]; + repeated bytes data = 4; +} diff --git a/material/material_test.go b/material/material_test.go new file mode 100644 index 0000000..9ddfa99 --- /dev/null +++ b/material/material_test.go @@ -0,0 +1,31 @@ +package material + +import ( + "reflect" + "testing" +) + +func TestMaterialRoundTrip(t *testing.T) { + id, want := []byte(`test id`), [][]byte{[]byte(`test value`)} + mtrl, err := New(id, want) + if err != nil { + t.Fatal(err) + } + + data, err := mtrl.Marshal() + if err != nil { + t.Fatal(err) + } + + mtrl = &Material{} + if err := mtrl.Unmarshal(data); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(id, mtrl.ID) { + t.Errorf("want id %+v, got %+v", id, mtrl.ID) + } + if !reflect.DeepEqual(want, mtrl.Data) { + t.Errorf("want data %+v, got %+v", want, mtrl.Data) + } +} diff --git a/vcrypt.go b/vcrypt.go index 557ace9..d4412ac 100644 --- a/vcrypt.go +++ b/vcrypt.go @@ -4,6 +4,7 @@ import "github.com/benburkert/vcrypt/seal" //go:generate -command protoc protoc --proto_path=$GOPATH/src:$GOPATH/src/github.com/gogo/protobuf/protobuf:. --gogo_out=. //go:generate protoc cryptex/cryptex.proto cryptex/sss.proto cryptex/xor.proto cryptex/secretbox.proto cryptex/box.proto cryptex/rsa.proto cryptex/openpgp.proto cryptex/mux.proto cryptex/demux.proto +//go:generate protoc material/material.proto //go:generate protoc seal/seal.proto seal/openpgp.proto //go:generate protoc secret/secret.proto secret/password.proto secret/openpgpkey.proto //go:generate protoc marker.proto node.proto