├── .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 |
5 |
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 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_build_filecrypt_src_production__mac__x64.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/go_build_filecrypt_src_production__win__x64.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
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 | [](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 | [](https://goreportcard.com/report/github.com/flew-software/filecrypt)
4 | [](https://github.com/flew-software/filecrypt)
5 | 
6 | 
7 | [](https://golang.org)
8 | A super easy to use file encryption utility written in golang
9 | [](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 | 
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 |
--------------------------------------------------------------------------------