diff options
Diffstat (limited to 'cryptfs')
28 files changed, 0 insertions, 1678 deletions
diff --git a/cryptfs/address_translation.go b/cryptfs/address_translation.go deleted file mode 100644 index b21cfc7..0000000 --- a/cryptfs/address_translation.go +++ /dev/null @@ -1,89 +0,0 @@ -package cryptfs - -// CryptFS methods that translate offsets between ciphertext and plaintext - -// get the block number at plain-text offset -func (be *CryptFS) PlainOffToBlockNo(plainOffset uint64) uint64 { - return plainOffset / be.plainBS -} - -// get the block number at ciphter-text offset -func (be *CryptFS) CipherOffToBlockNo(cipherOffset uint64) uint64 { - return (cipherOffset - HEADER_LEN) / be.cipherBS -} - -// get ciphertext offset of block "blockNo" -func (be *CryptFS) BlockNoToCipherOff(blockNo uint64) uint64 { - return HEADER_LEN + blockNo*be.cipherBS -} - -// get plaintext offset of block "blockNo" -func (be *CryptFS) BlockNoToPlainOff(blockNo uint64) uint64 { - return blockNo * be.plainBS -} - -// PlainSize - calculate plaintext size from ciphertext size -func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 { - - // Zero sized files stay zero-sized - if cipherSize == 0 { - return 0 - } - - if cipherSize == HEADER_LEN { - Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize) - return 0 - } - - if cipherSize < HEADER_LEN { - Warn.Printf("cipherSize %d < header size: corrupt file\n", cipherSize) - return 0 - } - - // Block number at last byte - blockNo := be.CipherOffToBlockNo(cipherSize - 1) - blockCount := blockNo + 1 - - overhead := be.BlockOverhead()*blockCount + HEADER_LEN - - return cipherSize - overhead -} - -// CipherSize - calculate ciphertext size from plaintext size -func (be *CryptFS) PlainSizeToCipherSize(plainSize uint64) uint64 { - - // Block number at last byte - blockNo := be.PlainOffToBlockNo(plainSize - 1) - blockCount := blockNo + 1 - - overhead := be.BlockOverhead()*blockCount + HEADER_LEN - - return plainSize + overhead -} - -// Split a plaintext byte range into (possibly partial) blocks -func (be *CryptFS) ExplodePlainRange(offset uint64, length uint64) []intraBlock { - var blocks []intraBlock - var nextBlock intraBlock - nextBlock.fs = be - - for length > 0 { - nextBlock.BlockNo = be.PlainOffToBlockNo(offset) - nextBlock.Skip = offset - be.BlockNoToPlainOff(nextBlock.BlockNo) - - // Minimum of remaining data and remaining space in the block - nextBlock.Length = MinUint64(length, be.plainBS-nextBlock.Skip) - - blocks = append(blocks, nextBlock) - offset += nextBlock.Length - length -= nextBlock.Length - } - return blocks -} - -func MinUint64(x uint64, y uint64) uint64 { - if x < y { - return x - } - return y -} diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go deleted file mode 100644 index 013b82d..0000000 --- a/cryptfs/config_file.go +++ /dev/null @@ -1,188 +0,0 @@ -package cryptfs - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" -) -import "os" - -const ( - // The dot "." is not used in base64url (RFC4648), hence - // we can never clash with an encrypted file. - ConfDefaultName = "gocryptfs.conf" -) - -type ConfFile struct { - // File the config is saved to. Not exported to JSON. - filename string - // Encrypted AES key, unlocked using a password hashed with scrypt - EncryptedKey []byte - // Stores parameters for scrypt hashing (key derivation) - ScryptObject scryptKdf - // The On-Disk-Format version this filesystem uses - Version uint16 - // List of feature flags this filesystem has enabled. - // If gocryptfs encounters a feature flag it does not support, it will refuse - // mounting. This mechanism is analogous to the ext4 feature flags that are - // stored in the superblock. - FeatureFlags []string -} - -// CreateConfFile - create a new config with a random key encrypted with -// "password" and write it to "filename". -// Uses scrypt with cost parameter logN. -func CreateConfFile(filename string, password string, plaintextNames bool, logN int) error { - var cf ConfFile - cf.filename = filename - cf.Version = HEADER_CURRENT_VERSION - - // Generate new random master key - key := RandBytes(KEY_LEN) - - // Encrypt it using the password - // This sets ScryptObject and EncryptedKey - cf.EncryptKey(key, password, logN) - - // Set feature flags - cf.FeatureFlags = append(cf.FeatureFlags, FlagGCMIV128) - if plaintextNames { - cf.FeatureFlags = append(cf.FeatureFlags, FlagPlaintextNames) - } else { - cf.FeatureFlags = append(cf.FeatureFlags, FlagDirIV) - cf.FeatureFlags = append(cf.FeatureFlags, FlagEMENames) - } - - // Write file to disk - return cf.WriteFile() -} - -// LoadConfFile - read config file from disk and decrypt the -// contained key using password. -// -// Returns the decrypted key and the ConfFile object -func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) { - var cf ConfFile - cf.filename = filename - - // Read from disk - js, err := ioutil.ReadFile(filename) - if err != nil { - return nil, nil, err - } - - // Unmarshal - err = json.Unmarshal(js, &cf) - if err != nil { - Warn.Printf("Failed to unmarshal config file") - return nil, nil, err - } - - if cf.Version != HEADER_CURRENT_VERSION { - return nil, nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version) - } - - for _, flag := range cf.FeatureFlags { - if cf.isFeatureFlagKnown(flag) == false { - return nil, nil, fmt.Errorf("Unsupported feature flag %s", flag) - } - } - - // Generate derived key from password - scryptHash := cf.ScryptObject.DeriveKey(password) - - // Unlock master key using password-based key - // We use stock go GCM instead of OpenSSL here as speed is not important - // and we get better error messages - cfs := NewCryptFS(scryptHash, false, false, false) - key, err := cfs.DecryptBlock(cf.EncryptedKey, 0, nil) - if err != nil { - Warn.Printf("failed to unlock master key: %s", err.Error()) - Warn.Printf("Password incorrect.") - return nil, nil, err - } - - return key, &cf, nil -} - -// EncryptKey - encrypt "key" using an scrypt hash generated from "password" -// and store it in cf.EncryptedKey. -// Uses scrypt with cost parameter logN and stores the scrypt parameters in -// cf.ScryptObject. -func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) { - // Generate derived key from password - cf.ScryptObject = NewScryptKdf(logN) - scryptHash := cf.ScryptObject.DeriveKey(password) - - // Lock master key using password-based key - cfs := NewCryptFS(scryptHash, false, false, false) - cf.EncryptedKey = cfs.EncryptBlock(key, 0, nil) -} - -// WriteFile - write out config in JSON format to file "filename.tmp" -// then rename over "filename". -// This way a password change atomically replaces the file. -func (cf *ConfFile) WriteFile() error { - tmp := cf.filename + ".tmp" - // 0400 permissions: gocryptfs.conf should be kept secret and never be written to. - fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400) - if err != nil { - return err - } - js, err := json.MarshalIndent(cf, "", "\t") - if err != nil { - return err - } - _, err = fd.Write(js) - if err != nil { - return err - } - err = fd.Sync() - if err != nil { - return err - } - err = fd.Close() - if err != nil { - return err - } - err = os.Rename(tmp, cf.filename) - if err != nil { - return err - } - - return nil -} - -const ( - // Understood Feature Flags. - // Also teach isFeatureFlagKnown() about any additions and - // add it to CreateConfFile() if you want to have it enabled by default. - FlagPlaintextNames = "PlaintextNames" - FlagDirIV = "DirIV" - FlagEMENames = "EMENames" - FlagGCMIV128 = "GCMIV128" -) - -// Verify that we understand a feature flag -func (cf *ConfFile) isFeatureFlagKnown(flag string) bool { - switch flag { - case FlagPlaintextNames, FlagDirIV, FlagEMENames, FlagGCMIV128: - return true - default: - return false - } -} - -// isFeatureFlagSet - is the feature flag "flagWant" enabled? -func (cf *ConfFile) IsFeatureFlagSet(flagWant string) bool { - if !cf.isFeatureFlagKnown(flagWant) { - log.Panicf("BUG: Tried to use unsupported feature flag %s", flagWant) - } - for _, flag := range cf.FeatureFlags { - if flag == flagWant { - return true - } - } - return false -} diff --git a/cryptfs/config_test.go b/cryptfs/config_test.go deleted file mode 100644 index 11599c0..0000000 --- a/cryptfs/config_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package cryptfs - -import ( - "fmt" - "testing" - "time" -) - -func TestLoadV1(t *testing.T) { - _, _, err := LoadConfFile("config_test/v1.conf", "test") - if err == nil { - t.Errorf("Outdated v1 config file must fail to load but it didn't") - } else if testing.Verbose() { - fmt.Print(err) - } -} - -// Load a known-good config file and verify that it takes at least 100ms -// (brute-force protection) -func TestLoadV2(t *testing.T) { - t1 := time.Now() - - _, _, err := LoadConfFile("config_test/v2.conf", "foo") - if err != nil { - t.Errorf("Could not load v2 config file: %v", err) - } - - elapsed := time.Since(t1) - if elapsed < 100*time.Millisecond { - t.Errorf("scrypt calculation runs too fast: %d ms", elapsed/time.Millisecond) - } -} - -func TestLoadV2PwdError(t *testing.T) { - if !testing.Verbose() { - Warn.Enabled = false - } - _, _, err := LoadConfFile("config_test/v2.conf", "wrongpassword") - if err == nil { - t.Errorf("Loading with wrong password must fail but it didn't") - } -} - -func TestLoadV2Feature(t *testing.T) { - _, _, err := LoadConfFile("config_test/PlaintextNames.conf", "test") - if err != nil { - t.Errorf("Could not load v2 PlaintextNames config file: %v", err) - } -} - -func TestLoadV2StrangeFeature(t *testing.T) { - _, _, err := LoadConfFile("config_test/StrangeFeature.conf", "test") - if err == nil { - t.Errorf("Loading unknown feature must fail but it didn't") - } else if testing.Verbose() { - fmt.Print(err) - } -} - -func TestCreateConfFile(t *testing.T) { - err := CreateConfFile("config_test/tmp.conf", "test", false, 10) - if err != nil { - t.Fatal(err) - } - _, _, err = LoadConfFile("config_test/tmp.conf", "test") - if err != nil { - t.Fatal(err) - } - -} - -func TestIsFeatureFlagKnown(t *testing.T) { - var cf ConfFile - if !cf.isFeatureFlagKnown(FlagDirIV) { - t.Errorf("This flag should be known") - } - if !cf.isFeatureFlagKnown(FlagPlaintextNames) { - t.Errorf("This flag should be known") - } - if cf.isFeatureFlagKnown("StrangeFeatureFlag") { - t.Errorf("This flag should be NOT known") - } -} diff --git a/cryptfs/config_test/.gitignore b/cryptfs/config_test/.gitignore deleted file mode 100644 index 0720169..0000000 --- a/cryptfs/config_test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tmp.conf diff --git a/cryptfs/config_test/PlaintextNames.conf b/cryptfs/config_test/PlaintextNames.conf deleted file mode 100644 index c1ff8cc..0000000 --- a/cryptfs/config_test/PlaintextNames.conf +++ /dev/null @@ -1,14 +0,0 @@ -{ - "EncryptedKey": "rG4u0argMq02V5G9Fa+gAaaHtNrj3wn7OZjP44hWOzO4yBFtn+Qn3PW4V6LMuKmGLEhyktCyWOI3K8lj", - "ScryptObject": { - "Salt": "bRjq1V63u5ML3FoTWx/GBXUhUVpTunOX3DPxS+yPjg0=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "PlaintextNames" - ] -} diff --git a/cryptfs/config_test/StrangeFeature.conf b/cryptfs/config_test/StrangeFeature.conf deleted file mode 100644 index 6a97781..0000000 --- a/cryptfs/config_test/StrangeFeature.conf +++ /dev/null @@ -1,14 +0,0 @@ -{ - "EncryptedKey": "rG4u0argMq02V5G9Fa+gAaaHtNrj3wn7OZjP44hWOzO4yBFtn+Qn3PW4V6LMuKmGLEhyktCyWOI3K8lj", - "ScryptObject": { - "Salt": "bRjq1V63u5ML3FoTWx/GBXUhUVpTunOX3DPxS+yPjg0=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "StrangeFeatureFlag" - ] -} diff --git a/cryptfs/config_test/v1.conf b/cryptfs/config_test/v1.conf deleted file mode 100644 index 588a25a..0000000 --- a/cryptfs/config_test/v1.conf +++ /dev/null @@ -1,11 +0,0 @@ -{ - "EncryptedKey": "t6YAvFQJvbv46c93bHQ5IZnvNz80DA9cohGoSPL/2M257LuIigow6jbr8b9HhnbDqHTCcz7aKkMDzneF", - "ScryptObject": { - "Salt": "yT4yQmmRmVNx2P0tJrUswk5SQzZaL6Z8kUteAoNJkXM=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 1 -} diff --git a/cryptfs/config_test/v2.conf b/cryptfs/config_test/v2.conf deleted file mode 100644 index 8ef3dcf..0000000 --- a/cryptfs/config_test/v2.conf +++ /dev/null @@ -1,11 +0,0 @@ -{ - "EncryptedKey": "RvxJnZWKTBSU21+7xbl08xlZyNyUCkpIqlK8Z51TUrRiBhqqNPxbdk1WXMvmOf/YzZ85Xbyz+DGM+SDf", - "ScryptObject": { - "Salt": "2OrFRfdW/5SanbMXM3TMINmfMO6oYU9awG+NZ77V8E8=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2 -} diff --git a/cryptfs/content_test.go b/cryptfs/content_test.go deleted file mode 100644 index 3efa959..0000000 --- a/cryptfs/content_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package cryptfs - -import ( - "testing" -) - -type testRange struct { - offset uint64 - length uint64 -} - -func TestSplitRange(t *testing.T) { - var ranges []testRange - - ranges = append(ranges, testRange{0, 70000}, - testRange{0, 10}, - testRange{234, 6511}, - testRange{65444, 54}, - testRange{0, 1024 * 1024}, - testRange{0, 65536}, - testRange{6654, 8945}) - - key := make([]byte, KEY_LEN) - f := NewCryptFS(key, true, false, true) - - for _, r := range ranges { - parts := f.ExplodePlainRange(r.offset, r.length) - var lastBlockNo uint64 = 1 << 63 - for _, p := range parts { - if p.BlockNo == lastBlockNo { - t.Errorf("Duplicate block number %d", p.BlockNo) - } - lastBlockNo = p.BlockNo - if p.Length > DEFAULT_PLAINBS || p.Skip >= DEFAULT_PLAINBS { - t.Errorf("Test fail: n=%d, length=%d, offset=%d\n", p.BlockNo, p.Length, p.Skip) - } - } - } -} - -func TestCiphertextRange(t *testing.T) { - var ranges []testRange - - ranges = append(ranges, testRange{0, 70000}, - testRange{0, 10}, - testRange{234, 6511}, - testRange{65444, 54}, - testRange{6654, 8945}) - - key := make([]byte, KEY_LEN) - f := NewCryptFS(key, true, false, true) - - for _, r := range ranges { - - blocks := f.ExplodePlainRange(r.offset, r.length) - alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks) - skipBytes := blocks[0].Skip - - if alignedLength < r.length { - t.Errorf("alignedLength=%d is smaller than length=%d", alignedLength, r.length) - } - if (alignedOffset-HEADER_LEN)%f.cipherBS != 0 { - t.Errorf("alignedOffset=%d is not aligned", alignedOffset) - } - if r.offset%f.plainBS != 0 && skipBytes == 0 { - t.Errorf("skipBytes=0") - } - } -} - -func TestBlockNo(t *testing.T) { - key := make([]byte, KEY_LEN) - f := NewCryptFS(key, true, false, true) - - b := f.CipherOffToBlockNo(788) - if b != 0 { - t.Errorf("actual: %d", b) - } - b = f.CipherOffToBlockNo(HEADER_LEN + f.cipherBS) - if b != 1 { - t.Errorf("actual: %d", b) - } - b = f.PlainOffToBlockNo(788) - if b != 0 { - t.Errorf("actual: %d", b) - } - b = f.PlainOffToBlockNo(f.plainBS) - if b != 1 { - t.Errorf("actual: %d", b) - } -} diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go deleted file mode 100644 index 3a40e29..0000000 --- a/cryptfs/cryptfs.go +++ /dev/null @@ -1,83 +0,0 @@ -package cryptfs - -// CryptFS is the crypto backend of GoCryptFS - -import ( - "crypto/aes" - "crypto/cipher" - "fmt" -) - -const ( - PROGRAM_NAME = "gocryptfs" - - DEFAULT_PLAINBS = 4096 - KEY_LEN = 32 // AES-256 - AUTH_TAG_LEN = 16 - DIRIV_LEN = 16 // identical to AES block size - DIRIV_FILENAME = "gocryptfs.diriv" -) - -type CryptFS struct { - blockCipher cipher.Block - gcm cipher.AEAD - gcmIVLen int - gcmIVGen nonceGenerator - plainBS uint64 - cipherBS uint64 - // Stores an all-zero block of size cipherBS - allZeroBlock []byte - // DirIV cache for filename encryption - DirIVCache dirIVCache -} - -func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool, GCMIV128 bool) *CryptFS { - - if len(key) != KEY_LEN { - panic(fmt.Sprintf("Unsupported key length %d", len(key))) - } - - b, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - - // We want the IV size in bytes - gcmIV := 96 / 8 - if GCMIV128 { - gcmIV = 128 / 8 - } - - var gcm cipher.AEAD - if useOpenssl { - gcm = opensslGCM{key} - } else { - gcm, err = goGCMWrapper(b, gcmIV) - if err != nil { - panic(err) - } - } - - plainBS := DEFAULT_PLAINBS - cipherBS := plainBS + gcmIV + AUTH_TAG_LEN - - return &CryptFS{ - blockCipher: b, - gcm: gcm, - gcmIVLen: gcmIV, - gcmIVGen: nonceGenerator{nonceLen: gcmIV}, - plainBS: uint64(plainBS), - cipherBS: uint64(cipherBS), - allZeroBlock: make([]byte, cipherBS), - } -} - -// Get plaintext block size -func (be *CryptFS) PlainBS() uint64 { - return be.plainBS -} - -// Per-block storage overhead -func (be *CryptFS) BlockOverhead() uint64 { - return be.cipherBS - be.plainBS -} diff --git a/cryptfs/cryptfs_content.go b/cryptfs/cryptfs_content.go deleted file mode 100644 index 2036e58..0000000 --- a/cryptfs/cryptfs_content.go +++ /dev/null @@ -1,129 +0,0 @@ -package cryptfs - -// File content encryption / decryption - -import ( - "bytes" - "crypto/cipher" - "crypto/md5" - "encoding/binary" - "encoding/hex" - "errors" - "os" -) - -// md5sum - debug helper, return md5 hex string -func md5sum(buf []byte) string { - rawHash := md5.Sum(buf) - hash := hex.EncodeToString(rawHash[:]) - return hash -} - -type CryptFile struct { - file *os.File - gcm cipher.AEAD -} - -// DecryptBlocks - Decrypt a number of blocks -func (be *CryptFS) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId []byte) ([]byte, error) { - cBuf := bytes.NewBuffer(ciphertext) - var err error - var pBuf bytes.Buffer - for cBuf.Len() > 0 { - cBlock := cBuf.Next(int(be.cipherBS)) - var pBlock []byte - pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileId) - if err != nil { - break - } - pBuf.Write(pBlock) - firstBlockNo++ - } - return pBuf.Bytes(), err -} - -// DecryptBlock - Verify and decrypt GCM block -// -// Corner case: A full-sized block of all-zero ciphertext bytes is translated -// to an all-zero plaintext block, i.e. file hole passtrough. -func (be *CryptFS) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte) ([]byte, error) { - - // Empty block? - if len(ciphertext) == 0 { - return ciphertext, nil - } - - // All-zero block? - if bytes.Equal(ciphertext, be.allZeroBlock) { - Debug.Printf("DecryptBlock: file hole encountered") - return make([]byte, be.plainBS), nil - } - - if len(ciphertext) < be.gcmIVLen { - Warn.Printf("DecryptBlock: Block is too short: %d bytes", len(ciphertext)) - return nil, errors.New("Block is too short") - } - - // Extract nonce - nonce := ciphertext[:be.gcmIVLen] - ciphertextOrig := ciphertext - ciphertext = ciphertext[be.gcmIVLen:] - - // Decrypt - var plaintext []byte - aData := make([]byte, 8) - aData = append(aData, fileId...) - binary.BigEndian.PutUint64(aData, blockNo) - plaintext, err := be.gcm.Open(plaintext, nonce, ciphertext, aData) - - if err != nil { - Warn.Printf("DecryptBlock: %s, len=%d, md5=%s", err.Error(), len(ciphertextOrig), md5sum(ciphertextOrig)) - Debug.Println(hex.Dump(ciphertextOrig)) - return nil, err - } - - return plaintext, nil -} - -// encryptBlock - Encrypt and add IV and MAC -func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte) []byte { - - // Empty block? - if len(plaintext) == 0 { - return plaintext - } - - // Get fresh nonce - nonce := be.gcmIVGen.Get() - - // Authenticate block with block number and file ID - aData := make([]byte, 8) - binary.BigEndian.PutUint64(aData, blockNo) - aData = append(aData, fileID...) - - // Encrypt plaintext and append to nonce - ciphertext := be.gcm.Seal(nonce, nonce, plaintext, aData) - - return ciphertext -} - -// MergeBlocks - Merge newData into oldData at offset -// New block may be bigger than both newData and oldData -func (be *CryptFS) MergeBlocks(oldData []byte, newData []byte, offset int) []byte { - - // Make block of maximum size - out := make([]byte, be.plainBS) - - // Copy old and new data into it - copy(out, oldData) - l := len(newData) - copy(out[offset:offset+l], newData) - - // Crop to length - outLen := len(oldData) - newLen := offset + len(newData) - if outLen < newLen { - outLen = newLen - } - return out[0:outLen] -} diff --git a/cryptfs/file_header.go b/cryptfs/file_header.go deleted file mode 100644 index fe4ac56..0000000 --- a/cryptfs/file_header.go +++ /dev/null @@ -1,56 +0,0 @@ -package cryptfs - -// Per-file header -// -// Format: [ "Version" uint16 big endian ] [ "Id" 16 random bytes ] - -import ( - "encoding/binary" - "fmt" -) - -const ( - HEADER_CURRENT_VERSION = 2 // Current on-disk-format version - HEADER_VERSION_LEN = 2 // uint16 - HEADER_ID_LEN = 16 // 128 bit random file id - HEADER_LEN = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length -) - -type FileHeader struct { - Version uint16 - Id []byte -} - -// Pack - serialize fileHeader object -func (h *FileHeader) Pack() []byte { - if len(h.Id) != HEADER_ID_LEN || h.Version != HEADER_CURRENT_VERSION { - panic("FileHeader object not properly initialized") - } - buf := make([]byte, HEADER_LEN) - binary.BigEndian.PutUint16(buf[0:HEADER_VERSION_LEN], h.Version) - copy(buf[HEADER_VERSION_LEN:], h.Id) - return buf - -} - -// ParseHeader - parse "buf" into fileHeader object -func ParseHeader(buf []byte) (*FileHeader, error) { - if len(buf) != HEADER_LEN { - return nil, fmt.Errorf("ParseHeader: invalid length: got %d, want %d", len(buf), HEADER_LEN) - } - var h FileHeader - h.Version = binary.BigEndian.Uint16(buf[0:HEADER_VERSION_LEN]) - if h.Version != HEADER_CURRENT_VERSION { - return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, HEADER_CURRENT_VERSION) - } - h.Id = buf[HEADER_VERSION_LEN:] - return &h, nil -} - -// RandomHeader - create new fileHeader object with random Id -func RandomHeader() *FileHeader { - var h FileHeader - h.Version = HEADER_CURRENT_VERSION - h.Id = RandBytes(HEADER_ID_LEN) - return &h -} diff --git a/cryptfs/gcm_go1.4.go b/cryptfs/gcm_go1.4.go deleted file mode 100644 index 0a2ff49..0000000 --- a/cryptfs/gcm_go1.4.go +++ /dev/null @@ -1,22 +0,0 @@ -// +build !go1.5 -// = go 1.4 or lower - -package cryptfs - -import ( - "crypto/cipher" - "fmt" -) - -// goGCMWrapper - This wrapper makes sure gocryptfs can be compiled on Go -// versions 1.4 and lower that lack NewGCMWithNonceSize(). -// 128 bit GCM IVs will not work when using built-in Go crypto, obviously, when -// compiled on 1.4. -func goGCMWrapper(bc cipher.Block, nonceSize int) (cipher.AEAD, error) { - if nonceSize != 12 { - Warn.Printf("128 bit GCM IVs are not supported by Go 1.4 and lower.") - Warn.Printf("Please use openssl crypto or recompile using a newer Go runtime.") - return nil, fmt.Errorf("128 bit GCM IVs are not supported by Go 1.4 and lower") - } - return cipher.NewGCM(bc) -} diff --git a/cryptfs/gcm_go1.5.go b/cryptfs/gcm_go1.5.go deleted file mode 100644 index c469357..0000000 --- a/cryptfs/gcm_go1.5.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build go1.5 -// = go 1.5 or higher - -package cryptfs - -import ( - "crypto/cipher" -) - -// goGCMWrapper - This wrapper makes sure gocryptfs can be compiled on Go -// versions 1.4 and lower that lack NewGCMWithNonceSize(). -// 128 bit GCM IVs will not work when using built-in Go crypto, obviously, when -// compiled on 1.4. -func goGCMWrapper(bc cipher.Block, nonceSize int) (cipher.AEAD, error) { - return cipher.NewGCMWithNonceSize(bc, nonceSize) -} diff --git a/cryptfs/intrablock.go b/cryptfs/intrablock.go deleted file mode 100644 index faff471..0000000 --- a/cryptfs/intrablock.go +++ /dev/null @@ -1,51 +0,0 @@ -package cryptfs - -// intraBlock identifies a part of a file block -type intraBlock struct { - BlockNo uint64 // Block number in file - Skip uint64 // Offset into block plaintext - Length uint64 // Length of data from this block - fs *CryptFS -} - -// isPartial - is the block partial? This means we have to do read-modify-write. -func (ib *intraBlock) IsPartial() bool { - if ib.Skip > 0 || ib.Length < ib.fs.plainBS { - return true - } - return false -} - -// CiphertextRange - get byte range in ciphertext file corresponding to BlockNo -// (complete block) -func (ib *intraBlock) CiphertextRange() (offset uint64, length uint64) { - return ib.fs.BlockNoToCipherOff(ib.BlockNo), ib.fs.cipherBS -} - -// PlaintextRange - get byte range in plaintext corresponding to BlockNo -// (complete block) -func (ib *intraBlock) PlaintextRange() (offset uint64, length uint64) { - return ib.fs.BlockNoToPlainOff(ib.BlockNo), ib.fs.plainBS -} - -// CropBlock - crop a potentially larger plaintext block down to the relevant part -func (ib *intraBlock) CropBlock(d []byte) []byte { - lenHave := len(d) - lenWant := int(ib.Skip + ib.Length) - if lenHave < lenWant { - return d[ib.Skip:lenHave] - } - return d[ib.Skip:lenWant] -} - -// Ciphertext range corresponding to the sum of all "blocks" (complete blocks) -func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64, length uint64) { - firstBlock := blocks[0] - lastBlock := blocks[len(blocks)-1] - - offset = ib.fs.BlockNoToCipherOff(firstBlock.BlockNo) - offsetLast := ib.fs.BlockNoToCipherOff(lastBlock.BlockNo) - length = offsetLast + ib.fs.cipherBS - offset - - return offset, length -} diff --git a/cryptfs/kdf.go b/cryptfs/kdf.go deleted file mode 100644 index e958413..0000000 --- a/cryptfs/kdf.go +++ /dev/null @@ -1,54 +0,0 @@ -package cryptfs - -import ( - "fmt" - "golang.org/x/crypto/scrypt" - "math" - "os" -) - -const ( - // 1 << 16 uses 64MB of memory, - // takes 4 seconds on my Atom Z3735F netbook - SCRYPT_DEFAULT_LOGN = 16 -) - -type scryptKdf struct { - Salt []byte - N int - R int - P int - KeyLen int -} - -func NewScryptKdf(logN int) scryptKdf { - var s scryptKdf - s.Salt = RandBytes(KEY_LEN) - if logN <= 0 { - s.N = 1 << SCRYPT_DEFAULT_LOGN - } else { - if logN < 10 { - fmt.Println("Error: scryptn below 10 is too low to make sense. Aborting.") - os.Exit(1) - } - s.N = 1 << uint32(logN) - } - s.R = 8 // Always 8 - s.P = 1 // Always 1 - s.KeyLen = KEY_LEN - return s -} - -func (s *scryptKdf) DeriveKey(pw string) []byte { - k, err := scrypt.Key([]byte(pw), s.Salt, s.N, s.R, s.P, s.KeyLen) - if err != nil { - panic(fmt.Sprintf("DeriveKey failed: %s", err.Error())) - } - return k -} - -// LogN - N is saved as 2^LogN, but LogN is much easier to work with. -// This function gives you LogN = Log2(N). -func (s *scryptKdf) LogN() int { - return int(math.Log2(float64(s.N)) + 0.5) -} diff --git a/cryptfs/kdf_test.go b/cryptfs/kdf_test.go deleted file mode 100644 index 4d909ea..0000000 --- a/cryptfs/kdf_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package cryptfs - -import ( - "testing" -) - -/* -Results on a 2.7GHz Pentium G630: - -gocryptfs/cryptfs$ go test -bench=. -PASS -BenchmarkScrypt10-2 300 6021435 ns/op ... 6ms -BenchmarkScrypt11-2 100 11861460 ns/op -BenchmarkScrypt12-2 100 23420822 ns/op -BenchmarkScrypt13-2 30 47666518 ns/op -BenchmarkScrypt14-2 20 92561590 ns/op ... 92ms -BenchmarkScrypt15-2 10 183971593 ns/op -BenchmarkScrypt16-2 3 368506365 ns/op -BenchmarkScrypt17-2 2 755502608 ns/op ... 755ms -ok github.com/rfjakob/gocryptfs/cryptfs 18.772s -*/ - -func benchmarkScryptN(n int, b *testing.B) { - kdf := NewScryptKdf(n) - for i := 0; i < b.N; i++ { - kdf.DeriveKey("test") - } -} - -func BenchmarkScrypt10(b *testing.B) { - benchmarkScryptN(10, b) -} - -func BenchmarkScrypt11(b *testing.B) { - benchmarkScryptN(11, b) -} - -func BenchmarkScrypt12(b *testing.B) { - benchmarkScryptN(12, b) -} - -func BenchmarkScrypt13(b *testing.B) { - benchmarkScryptN(13, b) -} - -func BenchmarkScrypt14(b *testing.B) { - benchmarkScryptN(14, b) -} - -func BenchmarkScrypt15(b *testing.B) { - benchmarkScryptN(15, b) -} - -func BenchmarkScrypt16(b *testing.B) { - benchmarkScryptN(16, b) -} - -func BenchmarkScrypt17(b *testing.B) { - benchmarkScryptN(17, b) -} diff --git a/cryptfs/log.go b/cryptfs/log.go deleted file mode 100644 index 19d8b51..0000000 --- a/cryptfs/log.go +++ /dev/null @@ -1,61 +0,0 @@ -package cryptfs - -import ( - "encoding/json" - "fmt" - "log" - "os" -) - -func JSONDump(obj interface{}) string { - b, err := json.MarshalIndent(obj, "", "\t") - if err != nil { - return err.Error() - } else { - return string(b) - } -} - -// toggledLogger - a Logger than can be enabled and disabled -type toggledLogger struct { - // Enable or disable output - Enabled bool - // Panic after logging a message, useful in regression tests - PanicAfter bool - *log.Logger -} - -func (l *toggledLogger) Printf(format string, v ...interface{}) { - if !l.Enabled { - return - } - l.Logger.Printf(format, v...) - if l.PanicAfter { - panic("PanicAfter: " + fmt.Sprintf(format, v...)) - } -} -func (l *toggledLogger) Println(v ...interface{}) { - if !l.Enabled { - return - } - l.Logger.Println(v...) - if l.PanicAfter { - panic("PanicAfter: " + fmt.Sprintln(v...)) - } -} - -// As defined by http://elinux.org/Debugging_by_printing#Log_Levels -// Debug messages -var Debug *toggledLogger - -// Informational message e.g. startup information -var Info *toggledLogger - -// A warning, meaning nothing serious by itself but might indicate problems -var Warn *toggledLogger - -func init() { - Debug = &toggledLogger{false, false, log.New(os.Stdout, "", 0)} - Info = &toggledLogger{true, false, log.New(os.Stdout, "", 0)} - Warn = &toggledLogger{true, false, log.New(os.Stderr, "", 0)} -} diff --git a/cryptfs/log_go1.4.go b/cryptfs/log_go1.4.go deleted file mode 100644 index 4b91bad..0000000 --- a/cryptfs/log_go1.4.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build !go1.5 -// = go 1.4 or lower - -package cryptfs - -import ( - "log/syslog" -) - -func (l *toggledLogger) SwitchToSyslog(p syslog.Priority) { - Debug.Printf("Cannot switch to syslog - need Go 1.5 or higher") -} diff --git a/cryptfs/log_go1.5.go b/cryptfs/log_go1.5.go deleted file mode 100644 index 8daae9c..0000000 --- a/cryptfs/log_go1.5.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build go1.5 -// = go 1.5 or higher - -package cryptfs - -import ( - "log/syslog" -) - -func (l *toggledLogger) SwitchToSyslog(p syslog.Priority) { - w, err := syslog.New(p, PROGRAM_NAME) - if err != nil { - Warn.Printf("Cannot switch 0x%02x to syslog: %v", p, err) - } else { - l.SetOutput(w) - } -} diff --git a/cryptfs/names_core.go b/cryptfs/names_core.go deleted file mode 100644 index 0f2e5b3..0000000 --- a/cryptfs/names_core.go +++ /dev/null @@ -1,134 +0,0 @@ -package cryptfs - -// Filename encryption / decryption functions - -import ( - "crypto/aes" - "crypto/cipher" - "encoding/base64" - "errors" - "fmt" - - "github.com/rfjakob/eme" -) - -// DecryptName - decrypt base64-encoded encrypted filename "cipherName" -// The used encryption is either CBC or EME, depending on the "EMENames" argument. -// -// This function is exported because it allows for a very efficient readdir -// implementation (read IV once, decrypt all names using this function). -func (be *CryptFS) DecryptName(cipherName string, iv []byte, EMENames bool) (string, error) { - return be.decryptName(cipherName, iv, EMENames) -} - -// decryptName - decrypt base64-encoded encrypted filename "cipherName". -// The used encryption is either CBC or EME, depending on the "EMENames" argument. -func (be *CryptFS) decryptName(cipherName string, iv []byte, EMENames bool) (string, error) { - - // Make sure relative symlinks still work after encryption - // by passing these through unchanged - if cipherName == "." || cipherName == ".." { - return cipherName, nil - } - - bin, err := base64.URLEncoding.DecodeString(cipherName) - if err != nil { - return "", err - } - - if len(bin)%aes.BlockSize != 0 { - return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin)) - } - - if EMENames { - bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionDecrypt) - } else { - cbc := cipher.NewCBCDecrypter(be.blockCipher, iv) - cbc.CryptBlocks(bin, bin) - } - - bin, err = be.unPad16(bin) - if err != nil { - return "", err - } - - plain := string(bin) - return plain, err -} - -// encryptName - encrypt "plainName", return base64-encoded "cipherName64" -// The used encryption is either CBC or EME, depending on the "EMENames" argument. -func (be *CryptFS) encryptName(plainName string, iv []byte, EMENames bool) (cipherName64 string) { - - // Make sure relative symlinks still work after encryption - // by passing these trough unchanged - if plainName == "." || plainName == ".." { - return plainName - } - - bin := []byte(plainName) - bin = be.pad16(bin) - - if EMENames { - bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionEncrypt) - } else { - cbc := cipher.NewCBCEncrypter(be.blockCipher, iv) - cbc.CryptBlocks(bin, bin) - } - - cipherName64 = base64.URLEncoding.EncodeToString(bin) - return cipherName64 -} - -// pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding -// https://tools.ietf.org/html/rfc5652#section-6.3 -func (be *CryptFS) pad16(orig []byte) (padded []byte) { - oldLen := len(orig) - if oldLen == 0 { - panic("Padding zero-length string makes no sense") - } - padLen := aes.BlockSize - oldLen%aes.BlockSize - if padLen == 0 { - padLen = aes.BlockSize - } - newLen := oldLen + padLen - padded = make([]byte, newLen) - copy(padded, orig) - padByte := byte(padLen) - for i := oldLen; i < newLen; i++ { - padded[i] = padByte - } - return padded -} - -// unPad16 - remove padding -func (be *CryptFS) unPad16(orig []byte) ([]byte, error) { - oldLen := len(orig) - if oldLen%aes.BlockSize != 0 { - return nil, errors.New("Unaligned size") - } - // The last byte is always a padding byte - padByte := orig[oldLen-1] - // The padding byte's value is the padding length - padLen := int(padByte) - // Padding must be at least 1 byte - if padLen <= 0 { - return nil, errors.New("Padding cannot be zero-length") - } - // Larger paddings make no sense - if padLen > aes.BlockSize { - return nil, errors.New("Padding cannot be larger than 16") - } - // All padding bytes must be identical - for i := oldLen - padLen; i < oldLen; i++ { - if orig[i] != padByte { - return nil, errors.New(fmt.Sprintf("Padding byte at i=%d is invalid", i)) - } - } - newLen := oldLen - padLen - // Padding an empty string makes no sense - if newLen == 0 { - return nil, errors.New("Unpadded length is zero") - } - return orig[0:newLen], nil -} diff --git a/cryptfs/names_diriv.go b/cryptfs/names_diriv.go deleted file mode 100644 index 276316c..0000000 --- a/cryptfs/names_diriv.go +++ /dev/null @@ -1,140 +0,0 @@ -package cryptfs - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "sync" -) - -// A simple one-entry DirIV cache -type dirIVCache struct { - // Invalidated? - cleared bool - // The DirIV - iv []byte - // Directory the DirIV belongs to - dir string - // Ecrypted version of "dir" - translatedDir string - // Synchronisation - lock sync.RWMutex -} - -// lookup - fetch entry for "dir" from the cache -func (c *dirIVCache) lookup(dir string) (bool, []byte, string) { - c.lock.RLock() - defer c.lock.RUnlock() - if !c.cleared && c.dir == dir { - return true, c.iv, c.translatedDir - } - return false, nil, "" -} - -// store - write entry for "dir" into the caches -func (c *dirIVCache) store(dir string, iv []byte, translatedDir string) { - c.lock.Lock() - defer c.lock.Unlock() - c.cleared = false - c.iv = iv - c.dir = dir - c.translatedDir = translatedDir -} - -func (c *dirIVCache) Clear() { - c.lock.Lock() - defer c.lock.Unlock() - c.cleared = true -} - -// readDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path) -func (be *CryptFS) ReadDirIV(dir string) (iv []byte, readErr error) { - ivfile := filepath.Join(dir, DIRIV_FILENAME) - Debug.Printf("ReadDirIV: reading %s\n", ivfile) - iv, readErr = ioutil.ReadFile(ivfile) - if readErr != nil { - // The directory may have been concurrently deleted or moved. Failure to - // read the diriv is not an error in that case. - _, statErr := os.Stat(dir) - if os.IsNotExist(statErr) { - Debug.Printf("ReadDirIV: Dir %s was deleted under our feet", dir) - } else { - // This should not happen - Warn.Printf("ReadDirIV: Dir exists but diriv does not: %v\n", readErr) - } - return nil, readErr - } - if len(iv) != DIRIV_LEN { - return nil, fmt.Errorf("ReadDirIV: Invalid length %d\n", len(iv)) - } - return iv, nil -} - -// WriteDirIV - create diriv file inside "dir" (absolute ciphertext path) -// This function is exported because it is used from pathfs_frontend, main, -// and also the automated tests. -func WriteDirIV(dir string) error { - iv := RandBytes(DIRIV_LEN) - file := filepath.Join(dir, DIRIV_FILENAME) - // 0444 permissions: the file is not secret but should not be written to - return ioutil.WriteFile(file, iv, 0444) -} - -// EncryptPathDirIV - encrypt path using EME with DirIV -func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool) (cipherPath string, err error) { - // Empty string means root directory - if plainPath == "" { - return plainPath, nil - } - // Check if the DirIV is cached - parentDir := filepath.Dir(plainPath) - found, iv, cParentDir := be.DirIVCache.lookup(parentDir) - if found { - //fmt.Print("h") - baseName := filepath.Base(plainPath) - cBaseName := be.encryptName(baseName, iv, eme) - cipherPath = cParentDir + "/" + cBaseName - return cipherPath, nil - } - // Walk the directory tree - var wd = rootDir - var encryptedNames []string - plainNames := strings.Split(plainPath, "/") - for _, plainName := range plainNames { - iv, err = be.ReadDirIV(wd) - if err != nil { - return "", err - } - encryptedName := be.encryptName(plainName, iv, eme) - encryptedNames = append(encryptedNames, encryptedName) - wd = filepath.Join(wd, encryptedName) - } - // Cache the final DirIV - cipherPath = strings.Join(encryptedNames, "/") - cParentDir = filepath.Dir(cipherPath) - be.DirIVCache.store(parentDir, iv, cParentDir) - return cipherPath, nil -} - -// DecryptPathDirIV - decrypt path using EME with DirIV -func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) { - var wd = rootDir - var plainNames []string - encryptedNames := strings.Split(encryptedPath, "/") - Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames) - for _, encryptedName := range encryptedNames { - iv, err := be.ReadDirIV(wd) - if err != nil { - return "", err - } - plainName, err := be.decryptName(encryptedName, iv, eme) - if err != nil { - return "", err - } - plainNames = append(plainNames, plainName) - wd = filepath.Join(wd, encryptedName) - } - return filepath.Join(plainNames...), nil -} diff --git a/cryptfs/names_noiv.go b/cryptfs/names_noiv.go deleted file mode 100644 index 7eed4b8..0000000 --- a/cryptfs/names_noiv.go +++ /dev/null @@ -1,63 +0,0 @@ -package cryptfs - -import ( - "strings" -) - -const ( - OpEncrypt = iota - OpDecrypt -) - -// DecryptPathNoIV - decrypt path using CBC without any IV. -// This function is deprecated by the the more secure DirIV variant and only retained -// for compatability with old filesystems. -func (be *CryptFS) DecryptPathNoIV(cipherPath string) (plainPath string, err error) { - plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt) - return plainPath, err -} - -// EncryptPathNoIV - decrypt path using CBC without any IV. -// This function is deprecated by the the more secure DirIV variant and only retained -// for compatability with old filesystems. -func (be *CryptFS) EncryptPathNoIV(plainPath string) (cipherPath string) { - cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt) - return cipherPath -} - -// translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV. -// Just splits the string on "/" and hands the parts to encryptName() / decryptName() -func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) { - var err error - - // Empty string means root directory - if path == "" { - return path, err - } - - zeroIV := make([]byte, DIRIV_LEN) - - // Run operation on each path component - var translatedParts []string - parts := strings.Split(path, "/") - for _, part := range parts { - if part == "" { - // This happens on "/foo/bar/" on the front and on the end. - // Don't panic. - translatedParts = append(translatedParts, "") - continue - } - var newPart string - if op == OpEncrypt { - newPart = be.encryptName(part, zeroIV, false) - } else { - newPart, err = be.decryptName(part, zeroIV, false) - if err != nil { - return "", err - } - } - translatedParts = append(translatedParts, newPart) - } - - return strings.Join(translatedParts, "/"), err -} diff --git a/cryptfs/names_test.go b/cryptfs/names_test.go deleted file mode 100644 index 0207f0a..0000000 --- a/cryptfs/names_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package cryptfs - -import ( - "bytes" - "testing" -) - -func TestEncryptPathNoIV(t *testing.T) { - var s []string - s = append(s, "foo") - s = append(s, "foo12312312312312312313123123123") - s = append(s, "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890") - - key := make([]byte, KEY_LEN) - fs := NewCryptFS(key, true, false, true) - - for _, n := range s { - c := fs.EncryptPathNoIV(n) - d, err := fs.DecryptPathNoIV(c) - if err != nil { - t.Errorf("Got error from DecryptPathNoIV: %s", err) - } - if d != n { - t.Errorf("Content mismatch, n != d: n=%s c=%s d=%s", n, c, d) - } - } -} - -func TestPad16(t *testing.T) { - var s [][]byte - s = append(s, []byte("foo")) - s = append(s, []byte("12345678901234567")) - s = append(s, []byte("12345678901234567abcdefg")) - - key := make([]byte, KEY_LEN) - fs := NewCryptFS(key, true, false, true) - - for i := range s { - orig := s[i] - padded := fs.pad16(orig) - if len(padded) <= len(orig) { - t.Errorf("Padded length not bigger than orig: %d", len(padded)) - } - if len(padded)%16 != 0 { - t.Errorf("Length is not aligend: %d", len(padded)) - } - unpadded, err := fs.unPad16(padded) - if err != nil { - t.Error("unPad16 returned error:", err) - } - if len(unpadded) != len(orig) { - t.Errorf("Size mismatch: orig=%d unpadded=%d", len(s[i]), len(unpadded)) - } - if !bytes.Equal(orig, unpadded) { - t.Error("Content mismatch orig vs unpadded") - } - } -} diff --git a/cryptfs/nonce.go b/cryptfs/nonce.go deleted file mode 100644 index be777fc..0000000 --- a/cryptfs/nonce.go +++ /dev/null @@ -1,42 +0,0 @@ -package cryptfs - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "encoding/hex" - "fmt" -) - -// Get "n" random bytes from /dev/urandom or panic -func RandBytes(n int) []byte { - b := make([]byte, n) - _, err := rand.Read(b) - if err != nil { - panic("Failed to read random bytes: " + err.Error()) - } - return b -} - -// Return a secure random uint64 -func RandUint64() uint64 { - b := RandBytes(8) - return binary.BigEndian.Uint64(b) -} - -type nonceGenerator struct { - lastNonce []byte - nonceLen int // bytes -} - -// Get a random 96 bit nonce -func (n *nonceGenerator) Get() []byte { - nonce := RandBytes(n.nonceLen) - Debug.Printf("nonceGenerator.Get(): %s\n", hex.EncodeToString(nonce)) - if bytes.Equal(nonce, n.lastNonce) { - m := fmt.Sprintf("Got the same nonce twice: %s. This should never happen!", hex.EncodeToString(nonce)) - panic(m) - } - n.lastNonce = nonce - return nonce -} diff --git a/cryptfs/openssl_aead.go b/cryptfs/openssl_aead.go deleted file mode 100644 index 5d38d38..0000000 --- a/cryptfs/openssl_aead.go +++ /dev/null @@ -1,100 +0,0 @@ -package cryptfs - -// Implements cipher.AEAD with OpenSSL backend - -import ( - "bytes" - "github.com/spacemonkeygo/openssl" -) - -// Supports all nonce sizes -type opensslGCM struct { - key []byte -} - -func (be opensslGCM) Overhead() int { - return AUTH_TAG_LEN -} - -func (be opensslGCM) NonceSize() int { - // We support any nonce size - return -1 -} - -// Seal encrypts and authenticates plaintext, authenticates the -// additional data and appends the result to dst, returning the updated -// slice. opensslGCM supports any nonce size. -func (be opensslGCM) Seal(dst, nonce, plaintext, data []byte) []byte { - - // Preallocate output buffer - var cipherBuf bytes.Buffer - cipherBuf.Grow(len(dst) + len(plaintext) + AUTH_TAG_LEN) - // Output will be appended to dst - cipherBuf.Write(dst) - - ectx, err := openssl.NewGCMEncryptionCipherCtx(KEY_LEN*8, nil, be.key, nonce) - if err != nil { - panic(err) - } - err = ectx.ExtraData(data) - if err != nil { - panic(err) - } - part, err := ectx.EncryptUpdate(plaintext) - if err != nil { - panic(err) - } - cipherBuf.Write(part) - part, err = ectx.EncryptFinal() - if err != nil { - panic(err) - } - cipherBuf.Write(part) - part, err = ectx.GetTag() - if err != nil { - panic(err) - } - cipherBuf.Write(part) - - return cipherBuf.Bytes() -} - -// Open decrypts and authenticates ciphertext, authenticates the -// additional data and, if successful, appends the resulting plaintext -// to dst, returning the updated slice. The nonce must be NonceSize() -// bytes long and both it and the additional data must match the -// value passed to Seal. -// -// The ciphertext and dst may alias exactly or not at all. -func (be opensslGCM) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { - - l := len(ciphertext) - tag := ciphertext[l-AUTH_TAG_LEN : l] - ciphertext = ciphertext[0 : l-AUTH_TAG_LEN] - plainBuf := bytes.NewBuffer(dst) - - dctx, err := openssl.NewGCMDecryptionCipherCtx(KEY_LEN*8, nil, be.key, nonce) - if err != nil { - return nil, err - } - err = dctx.ExtraData(data) - if err != nil { - return nil, err - } - part, err := dctx.DecryptUpdate(ciphertext) - if err != nil { - return nil, err - } - plainBuf.Write(part) - err = dctx.SetTag(tag) - if err != nil { - return nil, err - } - part, err = dctx.DecryptFinal() - if err != nil { - return nil, err - } - plainBuf.Write(part) - - return plainBuf.Bytes(), nil -} diff --git a/cryptfs/openssl_benchmark.bash b/cryptfs/openssl_benchmark.bash deleted file mode 100755 index df29628..0000000 --- a/cryptfs/openssl_benchmark.bash +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -go test -run NONE -bench BenchmarkEnc diff --git a/cryptfs/openssl_test.go b/cryptfs/openssl_test.go deleted file mode 100644 index aecee94..0000000 --- a/cryptfs/openssl_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package cryptfs - -// Benchmark go built-int GCM against spacemonkey openssl bindings -// -// Note: The benchmarks in this file supersede the ones in the openssl_benchmark -// directory as they use the same code paths that gocryptfs actually uses. -// -// Run benchmark: -// go test -bench Enc - -import ( - "crypto/aes" - "testing" -) - -func benchmarkGoEnc(b *testing.B, plaintext []byte, key []byte, nonce []byte) (ciphertext []byte) { - b.SetBytes(int64(len(plaintext))) - aes, err := aes.NewCipher(key[:]) - if err != nil { - b.Fatal(err) - } - aesgcm, err := goGCMWrapper(aes, len(nonce)) - if err != nil { - b.Fatal(err) - } - // This would be fileID + blockNo - aData := make([]byte, 24) - b.ResetTimer() - for i := 0; i < b.N; i++ { - // Encrypt plaintext and append to nonce - ciphertext = aesgcm.Seal(nonce, nonce, plaintext, aData) - } - return ciphertext -} - -func benchmarkOpensslEnc(b *testing.B, plaintext []byte, key []byte, nonce []byte) (ciphertext []byte) { - b.SetBytes(int64(len(plaintext))) - var aesgcm opensslGCM - aesgcm.key = key - // This would be fileID + blockNo - aData := make([]byte, 24) - for i := 0; i < b.N; i++ { - // Encrypt plaintext and append to nonce - ciphertext = aesgcm.Seal(nonce, nonce, plaintext, aData) - } - return ciphertext -} - -func BenchmarkEnc_Go_4k_AES256_nonce96(b *testing.B) { - plaintext := make([]byte, 4048) - key := make([]byte, 256/8) - nonce := make([]byte, 96/8) - benchmarkGoEnc(b, plaintext, key, nonce) -} - -func BenchmarkEnc_Go_4k_AES256_nonce128(b *testing.B) { - plaintext := make([]byte, 4048) - key := make([]byte, 256/8) - nonce := make([]byte, 128/8) - benchmarkGoEnc(b, plaintext, key, nonce) -} - -func BenchmarkEnc_OpenSSL_4k_AES256_nonce96(b *testing.B) { - plaintext := make([]byte, 4048) - key := make([]byte, 256/8) - nonce := make([]byte, 96/8) - benchmarkOpensslEnc(b, plaintext, key, nonce) -} - -func BenchmarkEnc_OpenSSL_4k_AES256_nonce128(b *testing.B) { - plaintext := make([]byte, 4048) - key := make([]byte, 256/8) - nonce := make([]byte, 96/8) - benchmarkOpensslEnc(b, plaintext, key, nonce) -} |