From 8654c45e35add74430c927ce838bb1199de1800f Mon Sep 17 00:00:00 2001 From: didier amyot Date: Sat, 15 Nov 2014 12:07:11 -0800 Subject: [PATCH] I hate commit messages --- contract/contract_generators.go | 1 + contract_getter.go | 41 ++++++++++++++++++++++ contract_getter_test.go | 22 ++++++++++++ files/encrypt_file_test.go | 36 +++++++++++++++++++ files/files.go | 61 ++++++++++++++++++++++++++++++--- files/files_test.go | 57 ++++++++++++++++++++++++------ helper/test_helper.go | 29 ++++++++++++++++ helper/test_helper_test.go | 33 ++++++++++++++++++ integration/contract_test.go | 9 ----- main.go | 22 ++++++------ main_test.go | 2 +- 11 files changed, 278 insertions(+), 35 deletions(-) create mode 100644 contract_getter.go create mode 100644 contract_getter_test.go delete mode 100644 integration/contract_test.go diff --git a/contract/contract_generators.go b/contract/contract_generators.go index 3d57d09..a7b2c68 100644 --- a/contract/contract_generators.go +++ b/contract/contract_generators.go @@ -78,6 +78,7 @@ func (this *ContractsGenerator) createPublicContractWithGoodDefaultValues(key *k this.privateContract.AcceptedSignatureScheme = []string{"rsa_pss"} this.privateContract.AcceptedBlockCypherModes = []string{"ofb"} this.privateContract.AcceptedBlockCyphers = []string{"aes"} + this.privateContract.AcceptedHashes = []string{"sha384", "sha512", "sha256"} this.privateContract.RSAPublicKey = []*keys.RSAPublicKey{key} } diff --git a/contract_getter.go b/contract_getter.go new file mode 100644 index 0000000..9125d50 --- /dev/null +++ b/contract_getter.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/json" + "errors" + "io/ioutil" + "os" + + "github.com/didiercrunch/filou/contract" +) + +func fileExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} + +func getFileContract(path string) (*contract.PublicContract, error) { + ret := new(contract.PublicContract) + if data, err := ioutil.ReadFile(path); err != nil { + return nil, err + } else if err = json.Unmarshal(data, ret); err != nil { + return nil, err + } else { + return ret, nil + } +} + +func GetContract(location string) (*contract.PublicContract, error) { + switch { + case fileExists(location): + return getFileContract(location) + default: + return nil, errors.New("could not find public contract at " + location) + } +} diff --git a/contract_getter_test.go b/contract_getter_test.go new file mode 100644 index 0000000..d316855 --- /dev/null +++ b/contract_getter_test.go @@ -0,0 +1,22 @@ +package main + +import ( + "testing" + + "github.com/didiercrunch/filou/contract" +) + +func getMockContract() *contract.PublicContract { + c := new(contract.PublicContract) + c.AcceptedHashes = []string{"hash_function_1", "hash_function_2"} + return c +} + +func getMockContractAndFile() *contract.PublicContract { + + return nil + +} + +func TestGetFileContract(t *testing.T) { +} diff --git a/files/encrypt_file_test.go b/files/encrypt_file_test.go index c21c873..c0143fa 100644 --- a/files/encrypt_file_test.go +++ b/files/encrypt_file_test.go @@ -1,3 +1,39 @@ package files +import ( + "bytes" + "crypto/rsa" + "fmt" + "testing" + "github.com/didiercrunch/filou/contract" + "github.com/didiercrunch/filou/helper" +) + +func getRSAPrivateKey() contract.GetRSAPrivateKey { + return func() (*rsa.PrivateKey, error) { + return helper.GetRSAPrivateKey(2048), nil + } +} + +func getSensibleAcceptedContract() *contract.AcceptedContract { + ret := new(contract.AcceptedContract) + ret.Hash = "sha512" + ret.BlockCipher = "aes256" + ret.BlockCipherMode = "ofb" + ret.AsynchronousEncryptionScheme = "rsa_oaep" + ret.SignatureScheme = "rsa_pss" + ret.RsaPrivateKey = getRSAPrivateKey() + ret.RsaPublicKey = helper.GetRSAPublicKey(2048) + return ret +} + +func TestEncryptFile(t *testing.T) { + buffer := bytes.NewBufferString("some secret text") + w := helper.NewMockIoWriter() + c := getSensibleAcceptedContract() + if err := EncryptFile(buffer, w, c); err != nil { + t.Error(err) + } + fmt.Println(w.String()) +} diff --git a/files/files.go b/files/files.go index 6997388..b3bf12e 100644 --- a/files/files.go +++ b/files/files.go @@ -1,15 +1,14 @@ package files import ( - "fmt" + "errors" "io" "os" + "regexp" "github.com/didiercrunch/filou/contract" ) -var _ = fmt.Print - type encryptor func(reader io.Reader, writer io.Writer, acceptedContract *contract.AcceptedContract) error type PathEncryptor struct { @@ -17,7 +16,61 @@ type PathEncryptor struct { EncryptDirectory encryptor } -func (this *PathEncryptor) EncryptPath(path string, writer io.Writer, acceptedContract *contract.AcceptedContract) error { +func (this *PathEncryptor) fileExists(path string) bool { + _, err := os.Stat(path) + return !os.IsNotExist(err) +} + +func (this *PathEncryptor) getDefaultPathToEncryptFile(originalFileName string) string { + isFileWithSingleExtension := regexp.MustCompile(`\.\w+$`) + isFileWithDoubleExtension := regexp.MustCompile(`\.\w+\.\w+$`) + isHiddenDirectory := regexp.MustCompile(`\/.\w+$`) + isHiddenFile := regexp.MustCompile(`\/\.\w+\.\w+$`) + + if isHiddenFile.MatchString(originalFileName) { + return isFileWithSingleExtension.ReplaceAllString(originalFileName, ".filou") + } + if isHiddenDirectory.MatchString(originalFileName) { + return originalFileName + ".filou" + } + if isFileWithDoubleExtension.MatchString(originalFileName) { + return isFileWithDoubleExtension.ReplaceAllString(originalFileName, ".filou") + } + if isFileWithSingleExtension.MatchString(originalFileName) { + return isFileWithSingleExtension.ReplaceAllString(originalFileName, ".filou") + } + return originalFileName + ".filou" +} + +// create a file with permission set to 600 +func (this *PathEncryptor) createPrivateFile(name string) (*os.File, error) { + return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) +} + +func (this *PathEncryptor) EncryptPathToDefaultFile(path string, acceptedContract *contract.AcceptedContract) error { + outputPathName := this.getDefaultPathToEncryptFile(path) + if _, err := os.Stat(outputPathName); !os.IsNotExist(err) { + return errors.New("the default path to encrypt already exists.") + } + return nil +} + +func (this *PathEncryptor) EncryptPathToOtherPath(fromPath, toPath string, acceptedContract *contract.AcceptedContract) error { + if this.fileExists(toPath) { + return errors.New("output file already exists") + } + if f, err := this.createPrivateFile(toPath); err != nil { + return err + } else { + defer f.Close() + return this.EncryptPathToWriter(fromPath, f, acceptedContract) + } +} + +func (this *PathEncryptor) EncryptPathToWriter(path string, writer io.Writer, acceptedContract *contract.AcceptedContract) error { + if !this.fileExists(path) { + return errors.New("input file does not exists") + } f, err := os.Open(path) if err != nil { return err diff --git a/files/files_test.go b/files/files_test.go index 3bda913..d6ba33c 100644 --- a/files/files_test.go +++ b/files/files_test.go @@ -3,6 +3,7 @@ package files import ( "fmt" "io" + "os" "testing" "github.com/didiercrunch/filou/contract" @@ -20,11 +21,16 @@ func (this *mockEncryptor) Encrypt(reader io.Reader, writer io.Writer, acceptedC return nil } +func removeFile(name string, t *testing.T) { + if err := os.Remove(name); err != nil { + t.Error(err) + } +} func TestEncryptPathWrongPath(t *testing.T) { pe := new(PathEncryptor) filePath := "/something_that_does_not_exists" - if err := pe.EncryptPath(filePath, nil, nil); err == nil { + if err := pe.EncryptPathToWriter(filePath, nil, nil); err == nil { t.Error("should say the file does not exists") } } @@ -34,7 +40,7 @@ func TestEncryptPathForDirectory(t *testing.T) { fileEncryptor, dirEncryptor := new(mockEncryptor), new(mockEncryptor) pe := &PathEncryptor{fileEncryptor.Encrypt, dirEncryptor.Encrypt} - if err := pe.EncryptPath(dirName, nil, nil); err != nil { + if err := pe.EncryptPathToWriter(dirName, nil, nil); err != nil { t.Error(err) } else if fileEncryptor.N != 0 { t.Error("should have 0", fileEncryptor.N) @@ -43,17 +49,48 @@ func TestEncryptPathForDirectory(t *testing.T) { } } -func TestEncryptPathForFile(t *testing.T) { - file := helper.GetTmpEmptyFile() - defer func() { - if err := file.Close(); err != nil { - t.Error(err) - } - }() +func TestEncryptPathToWriter(t *testing.T) { + file := helper.GetTmpFileWithText("foo bar") + defer removeFile(file.Name(), t) fileEncryptor, dirEncryptor := new(mockEncryptor), new(mockEncryptor) pe := &PathEncryptor{fileEncryptor.Encrypt, dirEncryptor.Encrypt} - if err := pe.EncryptPath(file.Name(), nil, nil); err != nil { + if err := pe.EncryptPathToWriter(file.Name(), nil, nil); err != nil { + t.Error(err) + } else if fileEncryptor.N != 1 { + t.Error("should have 1", fileEncryptor.N) + } else if dirEncryptor.N != 0 { + t.Error("should have 0", dirEncryptor.N) + } +} + +func TestGetDefaultPathToEncryptFile(t *testing.T) { + cases := map[string]string{ + "/foo/bar.txt": "/foo/bar.filou", // regular file + "/foo/bar.tar.gz": "/foo/bar.filou", // file with two extensions + "/foo/bar": "/foo/bar.filou", // directory + "/foo/.bar": "/foo/.bar.filou", // hidden directory + "/foo/.bar.txt": "/foo/.bar.filou", // hidden file + } + pe := new(PathEncryptor) + for input, expectedOutput := range cases { + if output := pe.getDefaultPathToEncryptFile(input); output != expectedOutput { + t.Error("expected", expectedOutput, "but received", output) + } + } +} + +func TestEncryptPathToOtherPath(t *testing.T) { + fromFile := helper.GetTmpFileWithText("foo bar") + defer removeFile(fromFile.Name(), t) + + toFile := helper.GetTmpFileName() + defer removeFile(toFile, t) + + fileEncryptor, dirEncryptor := new(mockEncryptor), new(mockEncryptor) + pe := &PathEncryptor{fileEncryptor.Encrypt, dirEncryptor.Encrypt} + + if err := pe.EncryptPathToOtherPath(fromFile.Name(), toFile, nil); err != nil { t.Error(err) } else if fileEncryptor.N != 1 { t.Error("should have 1", fileEncryptor.N) diff --git a/helper/test_helper.go b/helper/test_helper.go index 6506092..3ad11ee 100644 --- a/helper/test_helper.go +++ b/helper/test_helper.go @@ -5,6 +5,7 @@ import ( "crypto/rsa" "encoding/binary" "encoding/json" + "fmt" "io" "io/ioutil" "math/big" @@ -123,6 +124,8 @@ func GetRSAPrivateKey(size int) *rsa.PrivateKey { } +// return a tmp file in the default tmp directory. If fails, the function +// will panic func GetTmpEmptyFile() *os.File { if f, err := ioutil.TempFile("", ""); err != nil { panic(err) @@ -131,6 +134,32 @@ func GetTmpEmptyFile() *os.File { } } +// returned a closed tmp file with text in it. +func GetTmpFileWithText(text string) *os.File { + f := GetTmpEmptyFile() + defer func() { + if err := f.Close(); err != nil { + panic(err) + } + }() + + fmt.Fprint(f, text) + return f +} + +// returns a path that can be safely use as tmp file +func GetTmpFileName() string { + f := GetTmpEmptyFile() + if err := f.Close(); err != nil { + panic(err) + } + if err := os.Remove(f.Name()); err != nil { + panic(err) + } + return f.Name() +} + +// create a tmp dir and return its path. If failing, the method will panic func GetTmpEmptyDir() string { if dirName, err := ioutil.TempDir("", ""); err != nil { panic(err) diff --git a/helper/test_helper_test.go b/helper/test_helper_test.go index 09fa18a..7affb43 100644 --- a/helper/test_helper_test.go +++ b/helper/test_helper_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "math/big" "math/rand" + "os" "reflect" "testing" ) @@ -135,3 +136,35 @@ func TestGetReadLength(t *testing.T) { t.Error("bad read length") } } + +func TestGetTmpFileWithText(t *testing.T) { + + f := GetTmpFileWithText("some text") + defer func() { + if err := os.Remove(f.Name()); err != nil { + t.Error(err) + } + }() + if _, err := os.Stat(f.Name()); os.IsNotExist(err) { + t.Error("tmp file does not exists") + } +} + +func TestGetTmpEmptyFile(t *testing.T) { + f := GetTmpEmptyFile() + defer func() { + if err := os.Remove(f.Name()); err != nil { + t.Error(err) + } + }() + if _, err := os.Stat(f.Name()); os.IsNotExist(err) { + t.Error("tmp file does not exists") + } +} + +func TestGetTmpFileName(t *testing.T) { + f := GetTmpFileName() + if _, err := os.Stat(f); !os.IsNotExist(err) { + t.Error("tmp file exists") + } +} diff --git a/integration/contract_test.go b/integration/contract_test.go deleted file mode 100644 index bd6a8af..0000000 --- a/integration/contract_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package integration - -import ( - "testing" -) - -func TestEnvelop(t *testing.T) { - -} diff --git a/main.go b/main.go index e2c5f49..c7e7e72 100644 --- a/main.go +++ b/main.go @@ -5,19 +5,19 @@ import ( "os" "github.com/codegangsta/cli" - "github.com/didiercrunch/filou/keygenerator" + "github.com/didiercrunch/filou/contract" ) var createKeyCommand = cli.Command{ - Name: "createkey", - Usage: "create a pair of public/private key", + Name: "create-contracts", + Usage: "create a pair of public/private key encapsulated in contracts", Flags: []cli.Flag{ cli.IntFlag{Name: "size, s", Value: 2048, Usage: "key size in bits"}, }, Action: func(c *cli.Context) { - keyGenerator := new(keygenerator.KeyGenerator) - fmt.Println("creating key") - if err := keyGenerator.CreateNewKey(c.Int("size")); err != nil { + keyGenerator := new(contract.ContractsGenerator) + fmt.Println("creating key ...") + if err := keyGenerator.CreateContracts(c.Int("size")); err != nil { fmt.Println("error while creating the key\n", err) } }, @@ -28,14 +28,14 @@ var encryptFileCommand = cli.Command{ Usage: "encrypt file or directory", Flags: []cli.Flag{ cli.StringFlag{Name: "file, s", Value: "", Usage: "the file or directory to encrypt."}, - cli.StringFlag{Name: "publicKey, k", Value: "", Usage: "the public key to use to encrypt"}, + cli.StringFlag{Name: "publicContract, c", Value: "", Usage: "the public contract to use to encrypt"}, }, Action: func(c *cli.Context) { - keyGenerator := new(keygenerator.KeyGenerator) - fmt.Println("creating key") - if err := keyGenerator.CreateNewKey(c.Int("size")); err != nil { - fmt.Println("error while creating the key\n", err) + if _, err := GetContract(c.String("publicContract")); err != nil { + fmt.Println("error getting the public contract", err) + return } + }, } diff --git a/main_test.go b/main_test.go index db68c0c..51cbae0 100644 --- a/main_test.go +++ b/main_test.go @@ -17,7 +17,7 @@ func TestCreateKeyCommand(t *testing.T) { } app := cli.NewApp() app.Commands = []cli.Command{c} - args := []string{"file", "createkey", "-size", "22"} + args := []string{"file", "create-contracts", "-size", "22"} app.Run(args) if !hasBeenCalled { t.Error("sub command *createkey* has not been called")