├── .github ├── FUNDING.yml └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .idea ├── .gitignore ├── filecrypt.iml ├── misc.xml ├── modules.xml ├── runConfigurations │ ├── build_all_x64.xml │ ├── go_build_filecrypt_src_production__linux__x64.xml │ ├── go_build_filecrypt_src_production__mac__x64.xml │ └── go_build_filecrypt_src_production__win__x64.xml └── vcs.xml ├── .travis.yml ├── LICENSE ├── README.md ├── _config.yml ├── cmd └── filecrypt │ └── main.go ├── docs ├── fcef_format.md └── res │ └── fcef vis.png ├── go.mod ├── go.sum └── v2 ├── app.go ├── encryption.go ├── encryption_test.go ├── file.go ├── passphrase.go └── passphrase_test.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: tarithj 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # ******** NOTE ******** 12 | 13 | name: "CodeQL" 14 | 15 | on: 16 | push: 17 | branches: [ main ] 18 | pull_request: 19 | # The branches below must be a subset of the branches above 20 | branches: [ main ] 21 | schedule: 22 | - cron: '26 20 * * 5' 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | language: [ 'go' ] 33 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 34 | # Learn more: 35 | # https://docs.github.com/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 36 | 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@v2 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@v1 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 50 | 51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 52 | # If this step fails, then you should remove it and run the build manually (see below) 53 | - name: Autobuild 54 | uses: github/codeql-action/autobuild@v1 55 | 56 | # ℹ️ Command-line programs to run using the OS shell. 57 | # 📚 https://git.io/JvXDl 58 | 59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 60 | # and modify them (or add more) to build your code if your project 61 | # uses a compiled language 62 | 63 | #- run: | 64 | # make bootstrap 65 | # make release 66 | 67 | - name: Perform CodeQL Analysis 68 | uses: github/codeql-action/analyze@v1 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /src/vendor/ 3 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | /dictionaries/ 10 | -------------------------------------------------------------------------------- /.idea/filecrypt.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/build_all_x64.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_build_filecrypt_src_production__linux__x64.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_build_filecrypt_src_production__mac__x64.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /.idea/runConfigurations/go_build_filecrypt_src_production__win__x64.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 flew-software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # filecrypt 2 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/12610588667845698ee710628e313c0b)](https://www.codacy.com/gh/flew-software/filecrypt/dashboard?utm_source=github.com&utm_medium=referral&utm_content=flew-software/filecrypt&utm_campaign=Badge_Grade) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/flew-software/filecrypt)](https://goreportcard.com/report/github.com/flew-software/filecrypt) 4 | [![](https://tokei.rs/b1/github/flew-software/filecrypt?category=lines)](https://github.com/flew-software/filecrypt) 5 | ![](https://img.shields.io/badge/Binaries%20under-800kb-succes) 6 | ![GitHub repo size](https://img.shields.io/github/repo-size/flew-software/filecrypt) 7 | [![Written in](https://img.shields.io/badge/Written%20in-golang-blue)](https://golang.org) 8 | A super easy to use file encryption utility written in golang 9 | [![FILECRYPT](https://repository-images.githubusercontent.com/312549577/6eb8d100-2851-11eb-9c77-3a2a197b95ee)](https://github.com/flew-software/filecrypt) 10 | 11 | ⚠ **Filecrypt is nolonger maintained** 12 | **NOTE:** if you are planning to use filecrypt for your go-lang project please clone this repo and build your software from there 13 | 14 | ## What's the difference 15 | * Speed 16 | * Simple 17 | * Works on all operating systems 18 | 19 | ## How to use 20 | #### Windows 21 | 1. Download the latest release from [here](https://github.com/flew-software/filecrypt/releases/latest) 22 | 2. Create a folder named "filecrypt" in any drive 23 | 3. Move the downloaded executable(.exe) to the folder created in step 2 24 | 4. Add the path of the created folder created in step 2 into [windows path variable](https://docs.alfresco.com/4.2/tasks/fot-addpath.html) 25 | 5. Open the command prompt and enter `filecrypt --help` for detailed info about using flags 26 | 27 | #### flags 28 | * `--force` - Force write even if a file exists with that name (overwrite) DESTRUCTIVE 29 | * `--location` - The location of the file to encrypt/decrypt(determined by the mode flag) 30 | * `--mode` - FileCrypt mode (encrypt/decrypt These can be replaced with their first letters) Example: `filecrypt --mode=e --location="super secret file.txt" --password="test"` 31 | * `--password` - (removed in release v2 & later) Password to be used while encrypting/decrypting 32 | **Note: due to security issues with commandline flags the password will be prompted at runtime.** 33 | 34 | 35 | ## Q&A 36 | ### How can I help? 37 | * Staring this project on github 38 | * Creating issues 39 | * Contributing with code or documentation 40 | * Sharing this project with your friends 41 | 42 | ### What is a .fcef file 43 | fcef (File. Crypt. Encrypted. File) is an encrypted file that is generated by FileCrypt, 44 | If you know the password that it was encrypted with it can be decrypted with FileCrypt using the `--mode=d`or `--mode=decrypt` flag. 45 | 46 | ### How is FileCrypt so small 47 | Because Filecrypt 48 | * Only uses the standard library 49 | * Has no unwanted code 50 | * Is built with `-ldflags "-s -w"` flag 51 | * Executable is compressed with [upx](https://upx.github.io/) (Only extra small binaries) 52 | 53 | ## Docs 54 | * [FCEF format](./docs/fcef_format.md) 55 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /cmd/filecrypt/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "github.com/flew-software/filecrypt/v2" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | // file crypt encrypted file 13 | const fileCryptExtension string = ".fcef" 14 | 15 | func main() { 16 | reader := bufio.NewReader(os.Stdin) 17 | // flag vars 18 | var ( 19 | fileLocation string 20 | password string 21 | mode string 22 | force bool 23 | ) 24 | 25 | // flags 26 | flag.StringVar(&fileLocation, "location", "./", "Location of the file") 27 | flag.StringVar(&mode, "mode", "undefined", "FileCrypt mode (encrypt/decrypt)") 28 | flag.BoolVar(&force, "force", false, "Force write even if a file exists with that name (overwrite)") 29 | flag.Parse() 30 | 31 | app := v2.App{ 32 | FileCryptExtension: fileCryptExtension, 33 | Overwrite: force, 34 | } 35 | // asks for password 36 | print("Enter a password> ") 37 | password, _ = reader.ReadString('\n') 38 | password = strings.Replace(password, "\n", "", -1) 39 | 40 | switch strings.ToLower(mode) { 41 | case "encrypt", "e": 42 | newFile, err := app.Encrypt(fileLocation, v2.Passphrase(password)) 43 | if err != nil { 44 | fmt.Printf("could not encrypt file: %s\n", err) 45 | os.Exit(1) 46 | } 47 | fmt.Printf("file encrypted:\n%s\n", newFile) 48 | 49 | case "decrypt", "d": 50 | newFile, err := app.Decrypt(fileLocation, v2.Passphrase(password)) 51 | if err != nil { 52 | fmt.Printf("could not decrypt file: %s\n", err) 53 | os.Exit(1) 54 | } 55 | fmt.Printf("file decrypted:\n%s\n", newFile) 56 | default: 57 | fmt.Printf("unhandled mode: %s\n", mode) 58 | os.Exit(1) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/fcef_format.md: -------------------------------------------------------------------------------- 1 | # FCEF Format specification 2 | 3 | ## Ver 1 (v0001) (DEPRECATED) 4 | This document explains the specification of the FCEF file format Ver 1 5 | **DEPRECATED: due to the vulnerability in the Ver 1 encryption algorithm it's strongly recommended to use Ver 2** 6 | 7 | The FCEF format contain(s) 8 | 1. nonce & Encrypted Data 9 | 10 | **These will be called tags hereafter** 11 | 12 | ### Encrypted data 13 | A FCEF file generated with FileCrypt writes the nonce and encrypted data **after 0x0**. 14 | This tag contains the actual data that is used to decrypt. 15 | 16 | 17 | ## Ver 2 (v0002) 18 | This document explains the specification of the FCEF file format Ver 2 19 | 20 | 21 | ![FCEF visual representation](./res/fcef%20vis.png) 22 | 23 | 24 | The FCEF format contain(s) 25 | 1. Version number 26 | 2. Salt 27 | 3. Nonce & Encrypted data 28 | 29 | **These will be called tags hereafter** 30 | **The tag size(excluding Encrypted data) of FCEF Ver2 is 34 bytes** 31 | 32 | ### Version number 33 | A FCEF file generated with FileCrypt writes the encryption version that it used to encrypt the file **from 0x0 to 0x5**. 34 | It's used to check whether the FCEF file can be decrypted using the current version of the software. 35 | Every version after Ver 1 will contain this tag. 36 | **Note: the last byte(0x5) of the version tag contains a line brake(`\n`)** 37 | **Note: the version number ranges from v0001 to v9999** 38 | 39 | ### Salt 40 | A FCEF file generated with FileCrypt writes the salt generated using `rand.Read(salt)` **from 0x6 to 0x15** 41 | It's used while decrypting to regenerate the argon2 hash from the password. 42 | 43 | ### Nonce & Encrypted data 44 | A FCEF file generated with FileCrypt writes the nonce **from 0x16-0x21** and encrypted data **after 0x21**. 45 | This tag contains the actual data that is used to decrypt. 46 | 47 | -------------------------------------------------------------------------------- /docs/res/fcef vis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flewsoftware/filecrypt/b5bca23a04271a6c36d426f3fe6afe73e007b57e/docs/res/fcef vis.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/flew-software/filecrypt 2 | 3 | go 1.15 4 | 5 | require golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 2 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k= 3 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 4 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 5 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 6 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 7 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 8 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 9 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 10 | -------------------------------------------------------------------------------- /v2/app.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // App contains the actual application logic. 11 | type App struct { 12 | // FileCryptExtension is the file extension of encrypted files. 13 | FileCryptExtension string 14 | // Overwrite defines whether or not to overwrite existing files. 15 | Overwrite bool 16 | } 17 | 18 | // Encrypt encrypts the given file and returns the path the an encrypted file. 19 | func (a *App) Encrypt(fileLocation string, password Passphrase) (string, error) { 20 | if err := password.validate(); err != nil { 21 | return "", err 22 | } 23 | 24 | outputFilepath := fmt.Sprintf("%s%s", fileLocation, a.FileCryptExtension) 25 | 26 | if fileExists(outputFilepath) && !a.Overwrite { 27 | return "", fmt.Errorf("file already exists [%s]. use --force flag to overwrite", outputFilepath) 28 | } 29 | 30 | fileData, err := ioutil.ReadFile(fileLocation) 31 | if err != nil { 32 | return "", fmt.Errorf("could not read file [%s]: %w", fileLocation, err) 33 | } 34 | 35 | encryptedFileData, err := EncryptSHA256(fileData, password) 36 | if err != nil { 37 | return "", fmt.Errorf("could not encrypt data: %w", err) 38 | } 39 | 40 | // creates a file 41 | nf, err := os.Create(outputFilepath) 42 | if err != nil { 43 | return "", fmt.Errorf("could not create output file [%s]: %w", outputFilepath, err) 44 | } 45 | defer nf.Close() 46 | 47 | // writes the encrypted data slice to the file 48 | _, writeErr := nf.Write(encryptedFileData) 49 | if writeErr != nil { 50 | return "", fmt.Errorf("could not write encrypted data to file: %w", err) 51 | } 52 | 53 | return outputFilepath, nil 54 | } 55 | 56 | // Decrypt decrypts the given file and returns the path to an unencrypted file. 57 | func (a *App) Decrypt(fileLocation string, password Passphrase) (string, error) { 58 | realFile := strings.Replace(fileLocation, a.FileCryptExtension, "", -1) 59 | if fileLocation == realFile { 60 | return "", fmt.Errorf("input file does not contain a valid extension: expected %s", a.FileCryptExtension) 61 | } 62 | 63 | // Checks if file exists 64 | if fileExists(realFile) && !a.Overwrite { 65 | return "", fmt.Errorf("file already exists [%s]. use --force flag to overwrite", realFile) 66 | } 67 | 68 | // reads the file 69 | b, readErr := ioutil.ReadFile(fileLocation) 70 | if readErr != nil { 71 | return "", fmt.Errorf("could not read input file [%s]: %w", fileLocation, readErr) 72 | } 73 | 74 | // decrypted byte slice 75 | clearText, err := DecryptSHA256(b, password) 76 | if err != nil { 77 | return "", fmt.Errorf("could not decrypt file: %w", err) 78 | } 79 | 80 | // creates a file 81 | nf, err := os.Create(realFile) 82 | if err != nil { 83 | return "", fmt.Errorf("could not create unencrypted file [%s]: %w", realFile, err) 84 | } 85 | defer nf.Close() 86 | 87 | // writes decrypted buffer to the file 88 | _, writeErr := nf.Write(clearText) 89 | if writeErr != nil { 90 | return "", fmt.Errorf("could not write to unencrypted file: %w", err) 91 | } 92 | 93 | return realFile, nil 94 | } 95 | -------------------------------------------------------------------------------- /v2/encryption.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/rand" 7 | "errors" 8 | "golang.org/x/crypto/argon2" 9 | "io" 10 | "strings" 11 | ) 12 | 13 | type EncryptedData []byte 14 | type DecryptedData []byte 15 | type Hash string 16 | 17 | // default size of a salt(bytes) 18 | const saltSize = 16 19 | 20 | // current encryption version(fcef format) 21 | const encryptionVer string = "v0002" + "\n" 22 | 23 | // contains all supported fcef format versions 24 | var supportedVersions = []string{"v0002"} 25 | 26 | // creates a argon2 Hash from the key 27 | func CreateHashArgon(key string, salt []byte) (Hash, error) { 28 | return Hash(argon2.IDKey([]byte(key), salt, 1, 60*1024, 4, 32)), nil 29 | } 30 | 31 | // encrypts byte slice using the passphrase 32 | func EncryptSHA256(data []byte, p Passphrase) (EncryptedData, error) { 33 | salt := make([]byte, saltSize) 34 | 35 | // Generate a Salt 36 | if _, err := rand.Read(salt); err != nil { 37 | return EncryptedData(""), err 38 | } 39 | 40 | // convert string to bytes 41 | key, err := CreateHashArgon(string(p), salt) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | // create a new cipher block from the key 47 | block, err := aes.NewCipher([]byte(key)) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | // create a new GCM 53 | aesGCM, err := cipher.NewGCM(block) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | // create a nonce (12 bytes) 59 | nonce := make([]byte, aesGCM.NonceSize()) 60 | if _, err = io.ReadFull(rand.Reader, nonce); err != nil { 61 | return nil, err 62 | } 63 | 64 | // encrypt the data using aesGCM.Seal 65 | cipherData := append([]byte(encryptionVer), salt...) 66 | cipherData = append(cipherData, aesGCM.Seal(nonce, nonce, data, nil)...) 67 | return cipherData, nil 68 | 69 | } 70 | 71 | // decrypts byte slice using the passphrase 72 | func DecryptSHA256(data []byte, p Passphrase) (DecryptedData, error) { 73 | // extracts the version number of fcef format from byte slice 74 | ver, SaltAndMix := data[:len(encryptionVer)], data[len(encryptionVer):] 75 | if !CheckSupport(string(ver)) { 76 | return nil, errors.New("version not supported") 77 | } 78 | 79 | // gets the salt form the byte slice 80 | salt, mix := SaltAndMix[:saltSize], SaltAndMix[saltSize:] 81 | key, err := CreateHashArgon(string(p), salt) 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | // create a new cipher block from the key 87 | block, err := aes.NewCipher([]byte(key)) 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | // create a new GCM 93 | aesGCM, err := cipher.NewGCM(block) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | // get the nonce size 99 | nonceSize := aesGCM.NonceSize() 100 | 101 | // extract the nonce from the encrypted data 102 | nonce, cipherText := mix[:nonceSize], mix[nonceSize:] 103 | 104 | // decrypt the data 105 | plainData, err := aesGCM.Open(nil, nonce, cipherText, nil) 106 | if err != nil { 107 | return nil, err 108 | } 109 | return plainData, nil 110 | } 111 | 112 | // checks if current version of FileCrypt supports the fcef format 113 | func CheckSupport(ver string) bool { 114 | for i := 0; i < len(supportedVersions); i++ { 115 | if supportedVersions[i] == strings.Replace(ver, "\n", "", -1) { 116 | return true 117 | } 118 | } 119 | return false 120 | } 121 | -------------------------------------------------------------------------------- /v2/encryption_test.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestEncryptDecrypt(t *testing.T) { 9 | var tests = []struct { 10 | input string 11 | passphrase Passphrase 12 | }{ 13 | {"log", Passphrase("popcorn")}, 14 | {"log", Passphrase("pa$$word")}, 15 | {"log", Passphrase("pdsasdas")}, 16 | {"log", Passphrase("[asdsdasdmasn]")}, 17 | {"log", Passphrase("popwas")}, 18 | {"log", Passphrase("oi0isas")}, 19 | {"log", Passphrase("0=2oasdaj")}, 20 | {"test", Passphrase("testPass")}, 21 | {"test", Passphrase("asdasdasd")}, 22 | {"test", Passphrase("testP[ss")}, 23 | {"test", Passphrase("195s2f5")}, 24 | {"test", Passphrase("%%%%%%%s")}, 25 | {"test", Passphrase("#491k2@")}, 26 | {"test", Passphrase("[]'fd;fo;hkaf")}, 27 | {"golang", Passphrase("[]'foplekdk")}, 28 | {"filecrypt", Passphrase("[][]-fgioamfifo;hkaf")}, 29 | {"95k20kd-2j4", Passphrase("[]'fd;fo;hkaf")}, 30 | } 31 | for k, test := range tests { 32 | testCase := test 33 | t.Run(fmt.Sprint(k), func(t *testing.T) { 34 | encryptedResults, err := EncryptSHA256([]byte(testCase.input), testCase.passphrase) 35 | if err != nil { 36 | t.Errorf("unexpected error: %s", err) 37 | return 38 | } 39 | 40 | decryptedResults, err := DecryptSHA256(encryptedResults, testCase.passphrase) 41 | if err != nil { 42 | t.Errorf("unexpected error: %s", err) 43 | return 44 | } 45 | 46 | if exp, got := string(decryptedResults), testCase.input; exp != got { 47 | t.Errorf("expected result %s, got %s", exp, got) 48 | } 49 | }) 50 | } 51 | } 52 | 53 | func BenchmarkEncryptDecrypt(t *testing.B) { 54 | 55 | var tests = []struct { 56 | input string 57 | passphrase Passphrase 58 | }{ 59 | {"log", Passphrase("popcorn")}, 60 | {"log", Passphrase("pa$$word")}, 61 | {"log", Passphrase("pdsasdas")}, 62 | {"log", Passphrase("[asdsdasdmasn]")}, 63 | {"log", Passphrase("popwas")}, 64 | {"log", Passphrase("oi0isas")}, 65 | {"log", Passphrase("0=2oasdaj")}, 66 | {"test", Passphrase("testPass")}, 67 | {"test", Passphrase("asdasdasd")}, 68 | {"test", Passphrase("testP[ss")}, 69 | {"test", Passphrase("195s2f5")}, 70 | {"test", Passphrase("%%%%%%%s")}, 71 | {"test", Passphrase("#491k2@")}, 72 | {"test", Passphrase("[]'fd;fo;hkaf")}, 73 | {"golang", Passphrase("[]'foplekdk")}, 74 | {"filecrypt", Passphrase("[][]-fgioamfifo;hkaf")}, 75 | {"95k20kd-2j4", Passphrase("[]'fd;fo;hkaf")}, 76 | } 77 | for i := 0; i < 10; i++ { 78 | for k, test := range tests { 79 | testCase := test 80 | t.Run(fmt.Sprint(k), func(t *testing.B) { 81 | encryptedResults, err := EncryptSHA256([]byte(testCase.input), testCase.passphrase) 82 | if err != nil { 83 | t.Errorf("unexpected error: %s", err) 84 | return 85 | } 86 | 87 | decryptedResults, err := DecryptSHA256(encryptedResults, testCase.passphrase) 88 | if err != nil { 89 | t.Errorf("unexpected error: %s", err) 90 | return 91 | } 92 | 93 | if exp, got := string(decryptedResults), testCase.input; exp != got { 94 | t.Errorf("expected result %s, got %s", exp, got) 95 | } 96 | }) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /v2/file.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func fileExists(filename string) bool { 8 | info, err := os.Stat(filename) 9 | if os.IsNotExist(err) { 10 | return false 11 | } 12 | return !info.IsDir() 13 | } 14 | -------------------------------------------------------------------------------- /v2/passphrase.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import "fmt" 4 | 5 | // Passphrase is a password. 6 | type Passphrase string 7 | 8 | const minPasswordLength = 4 9 | 10 | // String returns the Passphrase as a string. 11 | func (p Passphrase) String() string { 12 | return string(p) 13 | } 14 | 15 | func (p *Passphrase) validate() error { 16 | pString := string(*p) 17 | if len(p.String()) < minPasswordLength { 18 | return fmt.Errorf("password must be at least %d characters in length", minPasswordLength-1) 19 | } else if pString == "default" { 20 | return fmt.Errorf("non-default password required") 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /v2/passphrase_test.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestPassphraseValidate(t *testing.T) { 9 | var tests = []struct { 10 | passphrase Passphrase 11 | valid bool 12 | }{ 13 | // valid tests 14 | {Passphrase("popcorn"), true}, 15 | {Passphrase("pa$$word"), true}, 16 | {Passphrase("pdsasdas"), true}, 17 | {Passphrase("[asdsdasdmasn]"), true}, 18 | {Passphrase("popwas"), true}, 19 | {Passphrase("oi0isas"), true}, 20 | {Passphrase("0=2oasdaj"), true}, 21 | {Passphrase("testPass"), true}, 22 | {Passphrase("asdasdasd"), true}, 23 | {Passphrase("testP[ss"), true}, 24 | {Passphrase("195s2f5"), true}, 25 | {Passphrase("%%%%%%%s"), true}, 26 | {Passphrase("#491k2@"), true}, 27 | 28 | // invalid tests 29 | {Passphrase("#49"), false}, 30 | {Passphrase("#f9"), false}, 31 | {Passphrase("as9"), false}, 32 | {Passphrase("bn9"), false}, 33 | {Passphrase("pop"), false}, 34 | {Passphrase("lol"), false}, 35 | {Passphrase("123"), false}, 36 | {Passphrase("default"), false}, 37 | {Passphrase("de"), false}, 38 | {Passphrase("\n"), false}, 39 | } 40 | for k, test := range tests { 41 | testCase := test 42 | t.Run(fmt.Sprint(k), func(t *testing.T) { 43 | if exp, got := testCase.valid, testCase.passphrase.validate() == nil; exp != got { 44 | t.Errorf("expected result %v, got %v", exp, got) 45 | } 46 | }) 47 | } 48 | } 49 | --------------------------------------------------------------------------------