diff --git a/encrypter/aes_ofb_encrypter.go b/encrypter/aes_ofb_encrypter.go new file mode 100644 index 0000000..0c2a1f4 --- /dev/null +++ b/encrypter/aes_ofb_encrypter.go @@ -0,0 +1,67 @@ +package encrypter + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" +) + +type AESOFBEncrypter struct { + randomReader io.Reader + iv []byte + key []byte +} + +func NewAESOFBEncrypter() *AESOFBEncrypter { + ret := new(AESOFBEncrypter) + ret.randomReader = rand.Reader + return ret +} + +func (this *AESOFBEncrypter) createSymetricKey(size int) ([]byte, error) { + key := make([]byte, size) + _, err := io.ReadFull(this.randomReader, key) + return key, err +} + +func (this *AESOFBEncrypter) getCypherBlock() (cipher.Block, error) { + var err error + this.key, err = this.createSymetricKey(32) + if err != nil { + return nil, err + } + return aes.NewCipher(this.key) +} + +func (this *AESOFBEncrypter) createIV(block cipher.Block) (iv []byte, err error) { + return this.createSymetricKey(block.BlockSize()) +} + +func (this *AESOFBEncrypter) createStreamMode(block cipher.Block) (stream cipher.Stream, err error) { + this.iv, err = this.createIV(block) + if err != nil { + return nil, err + } + return cipher.NewOFB(block, this.iv), err +} + +func (this *AESOFBEncrypter) createFileHandlerAndCypherBlock() (stream cipher.Stream, err error) { + cypherBlock, err := this.getCypherBlock() + if err != nil { + return nil, err + } + return this.createStreamMode(cypherBlock) +} + +func (this *AESOFBEncrypter) EncryptReaderToWriter(clearInput io.Reader, encryptedOutput io.Writer) error { + stream, err := this.createFileHandlerAndCypherBlock() + if err != nil { + return err + } + writer := &cipher.StreamWriter{S: stream, W: encryptedOutput} + if _, err := io.Copy(writer, clearInput); err != nil { + return err + } + return nil +} diff --git a/encrypter/aes_ofb_encrypter_test.go b/encrypter/aes_ofb_encrypter_test.go new file mode 100644 index 0000000..d58e76f --- /dev/null +++ b/encrypter/aes_ofb_encrypter_test.go @@ -0,0 +1,155 @@ +package encrypter + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + . "github.com/didiercrunch/ecrypto/helper" + "reflect" + "strings" + "testing" +) + +func NewMockAESOFBEncrypter() *AESOFBEncrypter { + ret := new(AESOFBEncrypter) + ret.randomReader = NewMockRandomReader() + return ret +} + +func TestCreateSymetricKey(t *testing.T) { + c := NewMockAESOFBEncrypter() + key, e := c.createSymetricKey(512) + if e != nil { + t.Error(e) + } else if len(key) != 512 { + t.Error("bad key lenght") + } else if !reflect.DeepEqual(key, NewMockRandomReader().GetRandomBytes(512)) { + t.Fail() + } +} + +func TestGetCypherBlock(t *testing.T) { + c := NewMockAESOFBEncrypter() + if c.key != nil { + t.Error("expected nil key at this point") + } + block, err := c.getCypherBlock() + if err != nil { + t.Error(err) + } + if block.BlockSize() != 16 { + t.Fail() + } + src := Range(16) + dst := make([]byte, 16) + expected := []byte{30, 248, 157, 9, 161, 60, 109, 82, 221, 152, 171, 167, 137, + 239, 177, 191} + expectedKey := []byte{73, 233, 188, 33, 31, 118, 98, 81, 66, 120, 254, 85, 138, 36, + 118, 80, 38, 83, 169, 57, 27, 61, 103, 122, 37, 164, 185, 40, 112, 66, 1, 50} + + block.Encrypt(dst, src) + if !reflect.DeepEqual(dst, expected) { + t.Error("bad aes encryption", dst) + } + if !reflect.DeepEqual(c.key, expectedKey) { + t.Error("bad key", c.key) + } +} + +func TestNewEncrypter(t *testing.T) { + c := NewAESOFBEncrypter() + if c.randomReader != rand.Reader { + t.Fail() + } +} + +func TestCreateIV(t *testing.T) { + m := NewMockAESOFBEncrypter() + b, err := aes.NewCipher([]byte("example key 1234")) + if err != nil { + t.Error(err) + return + } + iv, err := m.createIV(b) + if err != nil { + t.Error(err) + return + } + expt := NewMockRandomReader().GetRandomBytes(16) + if !reflect.DeepEqual(iv, expt) { + t.Error("bad iv", iv) + } +} + +func TestCreateStreamMode(t *testing.T) { + m := NewMockAESOFBEncrypter() + if m.iv != nil { + t.Error("expected nil iv at first") + } + b, err := aes.NewCipher([]byte("example key 1234")) + if err != nil { + t.Error(err) + return + } + _, err = m.createStreamMode(b) + if err != nil { + t.Error(err) + return + } + exptIv := NewMockRandomReader().GetRandomBytes(16) + if !reflect.DeepEqual(m.iv, exptIv) { + t.Error("bad iv", m.iv, " is not ", exptIv) + } +} + +func TestCreateFileHandlerAndCypherBlock(t *testing.T) { + m := NewMockAESOFBEncrypter() + if m.iv != nil { + t.Error("expected nil iv at first") + } + + _, err := m.createFileHandlerAndCypherBlock() + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(m.key, NewMockRandomReader().GetRandomBytes(32)) { + t.Error("bad key", m.key) + } + if !reflect.DeepEqual(m.iv, NewMockRandomReader().GetRandomBytes(32 + 16)[32:32+16]) { + t.Error("bad iv", m.iv) + } +} + +func TestSimpleEncryptReaderToWriter(t *testing.T) { + reader := strings.NewReader("some plain text") + writer := NewMockIoWriter() + m := NewMockAESOFBEncrypter() + if err := m.EncryptReaderToWriter(reader, writer); err != nil { + t.Error(err) + } +} + +func decryptEncryptedTextWithAESAlgorithmAndOFBMode(key, iv, encryptedText []byte) (string, error) { + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + stream := cipher.NewOFB(block, iv) + ret := make([]byte, len(encryptedText)) + stream.XORKeyStream(ret, encryptedText) + return string(ret), nil +} + +func TestEncryptDecrypt(t *testing.T) { + reader := strings.NewReader("some plain text") + writer := NewMockIoWriter() + m := NewMockAESOFBEncrypter() + if err := m.EncryptReaderToWriter(reader, writer); err != nil { + t.Error(err) + } + if plaintext2, err := decryptEncryptedTextWithAESAlgorithmAndOFBMode(m.key, m.iv, writer.Bytes()); err != nil { + t.Error(err) + } else if string(plaintext2) != "some plain text" { + t.Error("bad encryption/decryption process") + } +} diff --git a/encrypter/rsa_encryptor.go b/encrypter/rsa_encryptor.go new file mode 100644 index 0000000..6a4b6a7 --- /dev/null +++ b/encrypter/rsa_encryptor.go @@ -0,0 +1,30 @@ +package encrypter + +import ( + "crypto" + "crypto/rsa" + "crypto/sha512" + "io" +) + +var _ = sha512.New + +type RsaSha512 struct { + PrivateKey *rsa.PrivateKey +} + +func (this *RsaSha512) hashIOReader(input io.Reader) ([]byte, error) { + hashFunction := crypto.SHA512.New() + if _, err := io.Copy(hashFunction, input); err != nil { + return nil, err + } + return hashFunction.Sum(nil), nil +} + +func (this *RsaSha512) SignStream(input io.Reader, rand io.Reader) ([]byte, error) { + hashed, err := this.hashIOReader(input) + if err != nil { + return nil, err + } + return rsa.SignPSS(rand, this.PrivateKey, crypto.SHA512, hashed, nil) +} diff --git a/encrypter/rsa_encryptor_test.go b/encrypter/rsa_encryptor_test.go new file mode 100644 index 0000000..876d4b3 --- /dev/null +++ b/encrypter/rsa_encryptor_test.go @@ -0,0 +1,46 @@ +package encrypter + +import ( + "bytes" + "crypto" + "crypto/rsa" + "crypto/sha512" + "fmt" + "github.com/didiercrunch/ecrypto/helper" + "testing" +) + +func TestHashIOReader(t *testing.T) { + rs := new(RsaSha512) + expt := "fabbbe22180f1f137cfdc9556d2570e775d1ae02a597ded43a72a40f9b485d500043b7b" + + "e128fb9fcd982b83159a0d99aa855a9e7cc4240c00dc01a9bdf8218d7" + input := bytes.NewBufferString("C is as portable as Stonehedge!!") + if hashValue, err := rs.hashIOReader(input); err != nil { + t.Error(err) + } else if fmt.Sprintf("%x", hashValue) != expt { + t.Errorf("Hash is not the same \n expected : %s\n"+ + "results : %x ", expt, hashValue) + } +} + +func TestSignStream(t *testing.T) { + toSlice := func(data [sha512.Size]byte) []byte { + ret := make([]byte, sha512.Size) + for i, datum := range data { + ret[i] = datum + } + return ret + } + privateKey := helper.GetRSAPrivateKey(1024) + rs := &RsaSha512{privateKey} + input := bytes.NewBufferString("something to sign") + signature, err := rs.SignStream(input, helper.NewMockRandomReader()) + if err != nil { + t.Error(err) + return + } + hashed := toSlice(sha512.Sum512([]byte("something to sign"))) + if err := rsa.VerifyPSS(&privateKey.PublicKey, crypto.SHA512, hashed, signature, nil); err != nil { + t.Error(err) + } +} diff --git a/helper/test_helper.go b/helper/test_helper.go new file mode 100644 index 0000000..f0a3bae --- /dev/null +++ b/helper/test_helper.go @@ -0,0 +1,94 @@ +package helper + +import ( + "crypto/rsa" + "encoding/binary" + "math/big" + "math/rand" +) + +func B(x int64) *big.Int { + return big.NewInt(x) +} + +type MockRandomReader struct { + random *rand.Rand +} + +func NewMockRandomReader() *MockRandomReader { + return &MockRandomReader{rand.New(rand.NewSource(99))} +} + +func (this *MockRandomReader) getNextFourRandomBytes() []byte { + ret := make([]byte, 4) + binary.LittleEndian.PutUint32(ret, uint32(this.random.Int31())) + return ret +} + +func (this *MockRandomReader) Read(p []byte) (n int, err error) { + for i := 0; i < len(p); i += 4 { + copy(p[i:], this.getNextFourRandomBytes()) + } + return len(p), nil +} + +func (this *MockRandomReader) GetRandomBytes(size int) []byte { + ret := make([]byte, size) + this.Read(ret) + return ret + +} + +type MockIoWriter struct { + state []byte +} + +func NewMockIoWriter() *MockIoWriter { + return &MockIoWriter{make([]byte, 0, 1024)} +} + +func (this *MockIoWriter) Write(p []byte) (n int, err error) { + this.state = append(this.state, p...) + return len(p), nil +} + +func (this *MockIoWriter) String() string { + return string(this.state) +} + +func (this *MockIoWriter) Length() int { + return len(this.state) +} + +func (this *MockIoWriter) Bytes() []byte { + return this.state +} + +func Range(length int) []byte { + ret := make([]byte, length) + for i, _ := range ret { + ret[i] = byte(i) + } + return ret +} + +func FromRange(from, to int) []byte { + ret := make([]byte, to-from) + for i, _ := range ret { + ret[i] = byte(i + from) + } + return ret +} + +func GetRSAPublicKey(size int) *rsa.PublicKey { + return &GetRSAPrivateKey(size).PublicKey +} + +func GetRSAPrivateKey(size int) *rsa.PrivateKey { + key, err := rsa.GenerateKey(NewMockRandomReader(), size) + if err != nil { + panic(err) + } + return key + +} diff --git a/helper/test_helper_test.go b/helper/test_helper_test.go new file mode 100644 index 0000000..8e378ea --- /dev/null +++ b/helper/test_helper_test.go @@ -0,0 +1,100 @@ +package helper + +import ( + "fmt" + "math/big" + "math/rand" + "reflect" + "testing" +) + +func TestRange(t *testing.T) { + if !reflect.DeepEqual(Range(3), []byte{0, 1, 2}) { + t.Fail() + } +} + +func TestFromRange(t *testing.T) { + if !reflect.DeepEqual(FromRange(3, 5), []byte{3, 4}) { + t.Fail() + } +} + +func TestB(t *testing.T) { + if !reflect.DeepEqual(B(3), big.NewInt(3)) { + t.Fail() + } +} + +func TestMockIoWriter(t *testing.T) { + w := NewMockIoWriter() + fmt.Fprint(w, "something to write") + if !reflect.DeepEqual(w.Bytes(), []byte("something to write")) { + t.Fail() + } else if "something to write" != w.String() { + t.Fail() + } else if w.Length() != len("something to write") { + t.Fail() + } +} + +func TestGetRSAPublicKeyAndGetRSAPrivateKey(t *testing.T) { + privateKey := GetRSAPrivateKey(50) + if err := privateKey.Validate(); err != nil { + t.Error(err) + return + } + publicKey := GetRSAPublicKey(50) + if publicKey.E != privateKey.E || publicKey.N.Cmp(privateKey.N) != 0 { + t.Error("private and public keys do not match", publicKey.N, privateKey.N) + } + + if publicKey.N.BitLen() != 50 { + t.Error("modulo has bad bit length") + } +} + +func TestGetNextFourRandomBytes(t *testing.T) { + m := &MockRandomReader{rand.New(rand.NewSource(22))} + rb := m.getNextFourRandomBytes() + var expt []byte = []byte{233, 237, 107, 32} + if len(rb) != 4 { + t.Error("bad random length") + return + } else if !reflect.DeepEqual(expt, rb) { + t.Error("unexpect randome data") + } + + rb = m.getNextFourRandomBytes() + expt = []byte{27, 131, 191, 20} + if len(rb) != 4 { + t.Error("bad random length") + return + } else if !reflect.DeepEqual(expt, rb) { + t.Error("unexpect randome data") + } +} + +func TestMockRandomReader(t *testing.T) { + m := NewMockRandomReader() + dump := make([]byte, 4*10+3) + if l, err := m.Read(dump); l != len(dump) || err != nil { + t.Error("that is very bad :(") + return + } + var expt []byte = []byte{73, 233, 188, 33, 31, 118, 98, 81, 66, 120, 254, 85, 138, + 36, 118, 80, 38, 83, 169, 57, 27, 61, 103, 122, 37, 164, 185, 40, 112, 66, 1, + 50, 185, 128, 156, 20, 8, 164, 37, 13, 182, 50, 175} + if !reflect.DeepEqual(dump, expt) { + t.Error("unexpected returned data\n", dump) + } +} + +func TestGetRandomBytes(t *testing.T) { + rdBytes := NewMockRandomReader().GetRandomBytes(10) + expt := make([]byte, 10) + NewMockRandomReader().Read(expt) + if !reflect.DeepEqual(expt, rdBytes) { + t.Fail() + } +} diff --git a/something.go b/something.go new file mode 100644 index 0000000..95a12d9 --- /dev/null +++ b/something.go @@ -0,0 +1,27 @@ +package main + +import ( + "io" +) + +type Params struct { +} + +type Envelop struct { + err error + params *Params +} + +func ZipIoReader(reader io.Reader) { + +} + +func createEnvelop(reader io.Reader, writer io.WriteCloser, params *Params) error { + env := &Envelop{nil, params} + env.ZipDataForPayload() + env.CreatePayloadMetadata() + env.EncryptPayload() + env.CreateEnvelopMetadata() + env.EncryptPayloadKeyAndAddItToEncelop() + return env.getError() +}