From 4aca3324c92ca5b7306394b245f5db1b2036ef74 Mon Sep 17 00:00:00 2001 From: "didier.amyot" Date: Tue, 14 Oct 2014 06:31:36 -0700 Subject: [PATCH] add all --- doc/fileformat.md | 33 ++++---- envelop/doc.go | 5 ++ envelop/envelop.go | 104 +++++++++++++++++++++++ envelop/envelop_test.go | 163 +++++++++++++++++++++++++++++++++++++ envelop/metadata.go | 8 ++ helper/test_helper.go | 10 +++ helper/test_helper_test.go | 5 ++ payload/doc.go | 6 ++ payload/payload.go | 57 +++++++++++++ payload/payload_test.go | 27 ++++++ 10 files changed, 401 insertions(+), 17 deletions(-) create mode 100644 envelop/doc.go create mode 100644 envelop/envelop.go create mode 100644 envelop/envelop_test.go create mode 100644 envelop/metadata.go create mode 100644 payload/doc.go create mode 100644 payload/payload.go create mode 100644 payload/payload_test.go diff --git a/doc/fileformat.md b/doc/fileformat.md index 5aaf899..09843ed 100644 --- a/doc/fileformat.md +++ b/doc/fileformat.md @@ -18,35 +18,34 @@ else than a zip file containing the following shit. * mode * encrypted symetric key * hash of the encrypted data (always use full for validation) -3. a 'signature' file with the signature of the `meta_data.yml` filefunc (this *RsaSha512) encrypt() ([]byte, error) { - -} +3. a 'signature' file with the signature of the `meta_data.yml` file + func (this *RsaSha512) encrypt() ([]byte, error) {} ~~~ - _____________________________________________________________________ + _____________________________________________________________________________ | | Envelop: zip file, no compression, no encryption - | _________________________________________________________________ - | | - | | Paiload key: cyphered by target pulic key - | |______________________________________________________________ - | - | _____________________________________________________________________ - | | - | | Envelop meta data: encryption type, mode, Paiload signature (yaml) - | |________________________________________________________________________ - | | ________________________________________________________________________ | | + | | Paiload key: cyphered by target pulic key + | |________________________________________________________________________ + | + | _________________________________________________________________________ + | | + | | Envelop meta data: encryption type, mode, Paiload signature (yaml) + | |__________________________________________________________________________ + | + | __________________________________________________________________________ + | | | | Paiload: Zip file, no compression, symetrcally encrypted - | | _________________________________________________________________ + | | _____________________________________________________________________ | | | | | | paiload intern metadata: Data signature, sender, message,... - | | |_________________________________________________________________ + | | |______________________________________________________________________ | | - | | _________________________________________________________________ + | | _______________________________________________________________________ | | | | | | Data: Zip file, high compression | | | diff --git a/envelop/doc.go b/envelop/doc.go new file mode 100644 index 0000000..6309901 --- /dev/null +++ b/envelop/doc.go @@ -0,0 +1,5 @@ +package envelop + +/* +This package containes everything to create an envelop around a payload +*/ diff --git a/envelop/envelop.go b/envelop/envelop.go new file mode 100644 index 0000000..cf7abb9 --- /dev/null +++ b/envelop/envelop.go @@ -0,0 +1,104 @@ +package envelop + +import ( + "archive/zip" + "encoding/json" + "fmt" + "io" +) + +type Payload interface { + GetHash() []byte + GetKey() []byte + GetAlgorithm() string + GetMode() string + GetHashMethod() string + GetPayloadData() io.Reader +} + +type Encryptor interface { + Encrypt(data []byte) ([]byte, error) +} + +type Signer interface { + Sign(data []byte) ([]byte, error) +} + +type Enveloper struct { + encryptor Encryptor + payload Payload + signer Signer + EncryptedKey []byte + Signature []byte + Metadata *Metadata +} + +func (this *Enveloper) EncryptKey() ([]byte, error) { + return this.encryptor.Encrypt(this.payload.GetKey()) +} + +func (this *Enveloper) SignPayload() ([]byte, error) { + return this.signer.Sign(this.payload.GetHash()) +} + +func (this *Enveloper) CreateMetadata() *Metadata { + md := new(Metadata) + md.BlockAlgorithm = this.payload.GetAlgorithm() + md.BlockMode = this.payload.GetMode() + md.SignatureAlgorithm = "RSAPSS" + md.HashMethod = this.payload.GetHashMethod() + return md +} + +func (this *Enveloper) CreateEnvelopCompletely() error { + var err error + if this.EncryptedKey, err = this.EncryptKey(); err != nil { + return err + } else if this.Signature, err = this.SignPayload(); err != nil { + return err + } + this.Metadata = this.CreateMetadata() + return nil +} + +func (this *Enveloper) WritePayload(z *zip.Writer) error { + dataWriter, err := z.Create("data") + if err != nil { + return err + } + _, err = io.Copy(dataWriter, this.payload.GetPayloadData()) + return err +} + +func (this *Enveloper) WriteMetadata(z *zip.Writer) error { + dataWriter, err := z.Create("metadata.json") + if err != nil { + return err + } + encoder := json.NewEncoder(dataWriter) + return encoder.Encode(this.Metadata) +} + +func (this *Enveloper) WriteEncryptedKey(z *zip.Writer) error { + dataWriter, err := z.Create("key.json") + if err != nil { + return err + } + encoder := json.NewEncoder(dataWriter) + return encoder.Encode(&struct { + Key string + }{fmt.Sprintf("%s", this.EncryptedKey)}) +} + +func (this *Enveloper) WriteToWriter(w io.Writer) error { + z := zip.NewWriter(w) + defer z.Close() + if err := this.WritePayload(z); err != nil { + return err + } else if err := this.WriteMetadata(z); err != nil { + return err + } else if err := this.WriteEncryptedKey(z); err != nil { + return err + } + return nil +} diff --git a/envelop/envelop_test.go b/envelop/envelop_test.go new file mode 100644 index 0000000..19b781a --- /dev/null +++ b/envelop/envelop_test.go @@ -0,0 +1,163 @@ +package envelop + +import ( + "archive/zip" + "bytes" + "github.com/didiercrunch/ecrypto/helper" + "io" + "reflect" + "testing" +) + +type mockPayload struct{} + +func (this *mockPayload) GetKey() []byte { + return []byte{1, 2, 3} +} + +func (this *mockPayload) GetHash() []byte { + return []byte{10, 9, 8} +} + +func (this *mockPayload) GetAlgorithm() string { + symetricEncryptionAlgorithm := "DES" + return symetricEncryptionAlgorithm +} + +func (this *mockPayload) GetMode() string { + symetricEncryptionMode := "OFB" + return symetricEncryptionMode +} + +func (this *mockPayload) GetHashMethod() string { + theHashMethodUsedInThePayLoad := "SHA1" + return theHashMethodUsedInThePayLoad +} + +func (this *mockPayload) GetPayloadData() io.Reader { + someData := `some data must be here. In reality, the data + would be the encrypted payload. This will be + the big chunk of the data. + ` + return bytes.NewReader([]byte(someData)) +} + +type mockEncryptor struct{} + +func (this *mockEncryptor) Encrypt(data []byte) ([]byte, error) { + // cesar encryption! + ret := make([]byte, len(data)) + for i, d := range data { + ret[i] = d + 3 // modulo is implicit + } + return ret, nil +} + +type mockSigner struct{} + +func (this *mockSigner) Sign(data []byte) ([]byte, error) { + // a slice with the sum of the data plus a "secret" value + var sum byte = 36 + for _, d := range data { + sum += d + } + return []byte{sum}, nil +} + +func TestEncryptKey(t *testing.T) { + env := &Enveloper{ + encryptor: &mockEncryptor{}, + payload: &mockPayload{}, + } + if enc, err := env.EncryptKey(); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(enc, []byte{1 + 3, 2 + 3, 3 + 3}) { + t.Error("bad signature") + } +} + +func TestCreateEnvelopCompletelyEncryptKey(t *testing.T) { + env := &Enveloper{ + encryptor: &mockEncryptor{}, + payload: &mockPayload{}, + signer: &mockSigner{}, + } + if err := env.CreateEnvelopCompletely(); err != nil { + t.Error(err) + } + if !reflect.DeepEqual(env.EncryptedKey, []byte{1 + 3, 2 + 3, 3 + 3}) { + t.Error("CreateEnvelopCompletely has not encrypted the key") + } +} + +func TestSignPayload(t *testing.T) { + env := &Enveloper{ + signer: &mockSigner{}, + payload: &mockPayload{}, + } + if sign, err := env.SignPayload(); err != nil { + t.Error(err) + } else if !reflect.DeepEqual([]byte{10 + 9 + 8 + 36}, sign) { + t.Error("bad signature") + } +} + +func TestCreateEnvelopCompletelySignHash(t *testing.T) { + env := &Enveloper{ + payload: &mockPayload{}, + encryptor: &mockEncryptor{}, + signer: &mockSigner{}, + } + if err := env.CreateEnvelopCompletely(); err != nil { + t.Error(err) + } + if !reflect.DeepEqual(env.Signature, []byte{10 + 9 + 8 + 36}) { + t.Error("CreateEnvelopCompletely has not signature") + } +} + +func TestCreateMetadata(t *testing.T) { + env := &Enveloper{payload: &mockPayload{}} + exptectedMetaData := &Metadata{ + BlockAlgorithm: "DES", + BlockMode: "OFB", + SignatureAlgorithm: "RSAPSS", + HashMethod: "SHA1", + } + + if m := env.CreateMetadata(); !reflect.DeepEqual(m, exptectedMetaData) { + t.Error("bad meta data") + } +} + +func TestCreateEnvelopCompletelyMetadata(t *testing.T) { + env := &Enveloper{ + encryptor: &mockEncryptor{}, + payload: &mockPayload{}, + signer: &mockSigner{}, + } + if err := env.CreateEnvelopCompletely(); err != nil { + t.Error(err) + } + if env.Metadata == nil || env.Metadata.HashMethod != "SHA1" { + t.Error("CreateEnvelopCompletely has not created metadata") + } +} + +func TestWriteToWriter(t *testing.T) { + env := &Enveloper{ + payload: &mockPayload{}, + } + w := helper.NewMockIoWriter() + if err := env.WriteToWriter(w); err != nil { + t.Error(err) + } + r, err := zip.NewReader(w.ReaderAt(), int64(w.Length())) + if err != nil { + t.Error(err) + return + } + if len(r.File) != 3 { + t.Error("wrong file number") + } +} diff --git a/envelop/metadata.go b/envelop/metadata.go new file mode 100644 index 0000000..decc951 --- /dev/null +++ b/envelop/metadata.go @@ -0,0 +1,8 @@ +package envelop + +type Metadata struct { + BlockMode string + BlockAlgorithm string + SignatureAlgorithm string + HashMethod string +} diff --git a/helper/test_helper.go b/helper/test_helper.go index f0a3bae..8dfff8e 100644 --- a/helper/test_helper.go +++ b/helper/test_helper.go @@ -1,8 +1,10 @@ package helper import ( + "bytes" "crypto/rsa" "encoding/binary" + "io" "math/big" "math/rand" ) @@ -64,6 +66,14 @@ func (this *MockIoWriter) Bytes() []byte { return this.state } +func (this *MockIoWriter) Reader() io.Reader { + return bytes.NewReader(this.state) +} + +func (this *MockIoWriter) ReaderAt() io.ReaderAt { + return bytes.NewReader(this.state) +} + func Range(length int) []byte { ret := make([]byte, length) for i, _ := range ret { diff --git a/helper/test_helper_test.go b/helper/test_helper_test.go index 8e378ea..8e0bdd9 100644 --- a/helper/test_helper_test.go +++ b/helper/test_helper_test.go @@ -2,6 +2,7 @@ package helper import ( "fmt" + "io/ioutil" "math/big" "math/rand" "reflect" @@ -35,6 +36,10 @@ func TestMockIoWriter(t *testing.T) { t.Fail() } else if w.Length() != len("something to write") { t.Fail() + } else if readerOutput, err := ioutil.ReadAll(w.Reader()); err != nil { + t.Error(err) + } else if "something to write" != string(readerOutput) { + t.Error("bad reader") } } diff --git a/payload/doc.go b/payload/doc.go new file mode 100644 index 0000000..a4d26e7 --- /dev/null +++ b/payload/doc.go @@ -0,0 +1,6 @@ +package payload + +/* +This package is contains everything to create the payload. + +*/ diff --git a/payload/payload.go b/payload/payload.go new file mode 100644 index 0000000..0fd1595 --- /dev/null +++ b/payload/payload.go @@ -0,0 +1,57 @@ +package payload + +import ( + "crypto" + "crypto/cipher" + "io" +) + +/* + + type Payload interface { + GetHash() []byte + GetKey() []byte + GetAlgorithm() string + GetMode() string + GetHashMethod() string + GetPayloadData() io.Reader + } + +*/ + +type Payload struct { + DataSource io.Reader + Hash crypto.Hash + Block cipher.Block + BlockMode cipher.BlockMode +} + +func (this *Payload) GetHashMethod() string { + switch this.Hash { + case crypto.MD5: + return "md5" + case crypto.MD4: + return "md4" + case crypto.SHA1: + return "sha1" + case crypto.MD5SHA1: + return "md5sha1" + case crypto.RIPEMD160: + return "ripemd160" + case crypto.SHA224: + return "sha224" + case crypto.SHA256: + return "sha256" + case crypto.SHA384: + return "sha384" + case crypto.SHA512: + return "sha512" + default: + panic("unknown hash method") + } +} + +func (this *Payload) GetPayloadData() io.Reader { + // this.BlockMode. + return nil +} diff --git a/payload/payload_test.go b/payload/payload_test.go new file mode 100644 index 0000000..0378e3d --- /dev/null +++ b/payload/payload_test.go @@ -0,0 +1,27 @@ +package payload + +import ( + "crypto" + "testing" +) + +func TestPayloadGetHashMethod(t *testing.T) { + p := new(Payload) + testCases := make(map[string]crypto.Hash) + testCases["md4"] = crypto.MD4 + testCases["md5"] = crypto.MD5 + testCases["sha1"] = crypto.SHA1 + testCases["sha224"] = crypto.SHA224 + testCases["sha256"] = crypto.SHA256 + testCases["sha384"] = crypto.SHA384 + testCases["sha512"] = crypto.SHA512 + testCases["md5sha1"] = crypto.MD5SHA1 + testCases["ripemd160"] = crypto.RIPEMD160 + for name, hashfunc := range testCases { + p.Hash = hashfunc + if returnedName := p.GetHashMethod(); returnedName != name { + t.Error("bad hash name. supposed to be ", name, " but received", returnedName) + } + } + +}