aboutsummaryrefslogtreecommitdiff
path: root/cryptfs
diff options
context:
space:
mode:
authorJakob Unterwurzacher2015-12-19 14:41:39 +0100
committerJakob Unterwurzacher2015-12-19 15:02:29 +0100
commit1caa9258685fa5fad8935d3bfcd0eac7d7f84f1e (patch)
treeabc1e46f269f9ef8f05d812e13fcdf2bae68d298 /cryptfs
parent88826dc51d7919ef8b190c079955230e653323e2 (diff)
Increase GCM IV size from 96 to 128 bits
This pushes back the birthday bound for collisions to make it virtually irrelevant.
Diffstat (limited to 'cryptfs')
-rw-r--r--cryptfs/address_translation.go4
-rw-r--r--cryptfs/config_file.go11
-rw-r--r--cryptfs/content_test.go6
-rw-r--r--cryptfs/cryptfs.go26
-rw-r--r--cryptfs/cryptfs_content.go8
-rw-r--r--cryptfs/names_test.go4
-rw-r--r--cryptfs/nonce.go11
-rw-r--r--cryptfs/openssl_aead.go7
8 files changed, 47 insertions, 30 deletions
diff --git a/cryptfs/address_translation.go b/cryptfs/address_translation.go
index 147040c..b21cfc7 100644
--- a/cryptfs/address_translation.go
+++ b/cryptfs/address_translation.go
@@ -44,7 +44,7 @@ func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 {
blockNo := be.CipherOffToBlockNo(cipherSize - 1)
blockCount := blockNo + 1
- overhead := BLOCK_OVERHEAD*blockCount + HEADER_LEN
+ overhead := be.BlockOverhead()*blockCount + HEADER_LEN
return cipherSize - overhead
}
@@ -56,7 +56,7 @@ func (be *CryptFS) PlainSizeToCipherSize(plainSize uint64) uint64 {
blockNo := be.PlainOffToBlockNo(plainSize - 1)
blockCount := blockNo + 1
- overhead := BLOCK_OVERHEAD*blockCount + HEADER_LEN
+ overhead := be.BlockOverhead()*blockCount + HEADER_LEN
return plainSize + overhead
}
diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go
index 48e5474..138426a 100644
--- a/cryptfs/config_file.go
+++ b/cryptfs/config_file.go
@@ -46,6 +46,7 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
cf.EncryptKey(key, password, logN)
// Set feature flags
+ cf.FeatureFlags = append(cf.FeatureFlags, FlagGCMIV128)
if plaintextNames {
cf.FeatureFlags = append(cf.FeatureFlags, FlagPlaintextNames)
} else {
@@ -94,7 +95,7 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
// 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)
+ 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\n", err.Error())
@@ -115,7 +116,7 @@ func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
scryptHash := cf.ScryptObject.DeriveKey(password)
// Lock master key using password-based key
- cfs := NewCryptFS(scryptHash, false, false)
+ cfs := NewCryptFS(scryptHash, false, false, false)
cf.EncryptedKey = cfs.EncryptBlock(key, 0, nil)
}
@@ -155,16 +156,18 @@ func (cf *ConfFile) WriteFile() error {
const (
// Understood Feature Flags.
- // Also teach isFeatureFlagKnown() about any additions
+ // 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:
+ case FlagPlaintextNames, FlagDirIV, FlagEMENames, FlagGCMIV128:
return true
default:
return false
diff --git a/cryptfs/content_test.go b/cryptfs/content_test.go
index 1b269bd..3efa959 100644
--- a/cryptfs/content_test.go
+++ b/cryptfs/content_test.go
@@ -21,7 +21,7 @@ func TestSplitRange(t *testing.T) {
testRange{6654, 8945})
key := make([]byte, KEY_LEN)
- f := NewCryptFS(key, true, false)
+ f := NewCryptFS(key, true, false, true)
for _, r := range ranges {
parts := f.ExplodePlainRange(r.offset, r.length)
@@ -48,7 +48,7 @@ func TestCiphertextRange(t *testing.T) {
testRange{6654, 8945})
key := make([]byte, KEY_LEN)
- f := NewCryptFS(key, true, false)
+ f := NewCryptFS(key, true, false, true)
for _, r := range ranges {
@@ -70,7 +70,7 @@ func TestCiphertextRange(t *testing.T) {
func TestBlockNo(t *testing.T) {
key := make([]byte, KEY_LEN)
- f := NewCryptFS(key, true, false)
+ f := NewCryptFS(key, true, false, true)
b := f.CipherOffToBlockNo(788)
if b != 0 {
diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go
index 58cca74..ae62045 100644
--- a/cryptfs/cryptfs.go
+++ b/cryptfs/cryptfs.go
@@ -11,9 +11,7 @@ import (
const (
DEFAULT_PLAINBS = 4096
KEY_LEN = 32 // AES-256
- NONCE_LEN = 12
AUTH_TAG_LEN = 16
- BLOCK_OVERHEAD = NONCE_LEN + AUTH_TAG_LEN
DIRIV_LEN = 16 // identical to AES block size
DIRIV_FILENAME = "gocryptfs.diriv"
)
@@ -21,6 +19,8 @@ const (
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
@@ -29,7 +29,7 @@ type CryptFS struct {
DirIVCacheEnc DirIVCache
}
-func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS {
+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)))
@@ -40,22 +40,31 @@ func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS {
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 = cipher.NewGCM(b)
+ gcm, err = cipher.NewGCMWithNonceSize(b, gcmIV)
if err != nil {
panic(err)
}
}
- cipherBS := DEFAULT_PLAINBS + NONCE_LEN + AUTH_TAG_LEN
+ plainBS := DEFAULT_PLAINBS
+ cipherBS := plainBS + gcmIV + AUTH_TAG_LEN
return &CryptFS{
blockCipher: b,
gcm: gcm,
- plainBS: DEFAULT_PLAINBS,
+ gcmIVLen: gcmIV,
+ gcmIVGen: nonceGenerator{nonceLen: gcmIV},
+ plainBS: uint64(plainBS),
cipherBS: uint64(cipherBS),
allZeroBlock: make([]byte, cipherBS),
}
@@ -65,3 +74,8 @@ func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS {
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
index 25293a7..9a79db4 100644
--- a/cryptfs/cryptfs_content.go
+++ b/cryptfs/cryptfs_content.go
@@ -59,15 +59,15 @@ func (be *CryptFS) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte
return make([]byte, be.plainBS), nil
}
- if len(ciphertext) < NONCE_LEN {
+ if len(ciphertext) < be.gcmIVLen {
Warn.Printf("DecryptBlock: Block is too short: %d bytes\n", len(ciphertext))
return nil, errors.New("Block is too short")
}
// Extract nonce
- nonce := ciphertext[:NONCE_LEN]
+ nonce := ciphertext[:be.gcmIVLen]
ciphertextOrig := ciphertext
- ciphertext = ciphertext[NONCE_LEN:]
+ ciphertext = ciphertext[be.gcmIVLen:]
// Decrypt
var plaintext []byte
@@ -94,7 +94,7 @@ func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte)
}
// Get fresh nonce
- nonce := gcmNonce.Get()
+ nonce := be.gcmIVGen.Get()
// Authenticate block with block number and file ID
aData := make([]byte, 8)
diff --git a/cryptfs/names_test.go b/cryptfs/names_test.go
index 6dffae3..0207f0a 100644
--- a/cryptfs/names_test.go
+++ b/cryptfs/names_test.go
@@ -12,7 +12,7 @@ func TestEncryptPathNoIV(t *testing.T) {
s = append(s, "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890")
key := make([]byte, KEY_LEN)
- fs := NewCryptFS(key, true, false)
+ fs := NewCryptFS(key, true, false, true)
for _, n := range s {
c := fs.EncryptPathNoIV(n)
@@ -33,7 +33,7 @@ func TestPad16(t *testing.T) {
s = append(s, []byte("12345678901234567abcdefg"))
key := make([]byte, KEY_LEN)
- fs := NewCryptFS(key, true, false)
+ fs := NewCryptFS(key, true, false, true)
for i := range s {
orig := s[i]
diff --git a/cryptfs/nonce.go b/cryptfs/nonce.go
index 3abfefa..be777fc 100644
--- a/cryptfs/nonce.go
+++ b/cryptfs/nonce.go
@@ -24,16 +24,15 @@ func RandUint64() uint64 {
return binary.BigEndian.Uint64(b)
}
-var gcmNonce nonce96
-
-type nonce96 struct {
+type nonceGenerator struct {
lastNonce []byte
+ nonceLen int // bytes
}
// Get a random 96 bit nonce
-func (n *nonce96) Get() []byte {
- nonce := RandBytes(12)
- Debug.Printf("nonce96.Get(): %s\n", hex.EncodeToString(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)
diff --git a/cryptfs/openssl_aead.go b/cryptfs/openssl_aead.go
index c70bd1f..5d38d38 100644
--- a/cryptfs/openssl_aead.go
+++ b/cryptfs/openssl_aead.go
@@ -7,6 +7,7 @@ import (
"github.com/spacemonkeygo/openssl"
)
+// Supports all nonce sizes
type opensslGCM struct {
key []byte
}
@@ -16,13 +17,13 @@ func (be opensslGCM) Overhead() int {
}
func (be opensslGCM) NonceSize() int {
- return NONCE_LEN
+ // 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. The nonce must be NonceSize() bytes long and unique for all
-// time, for a given key.
+// slice. opensslGCM supports any nonce size.
func (be opensslGCM) Seal(dst, nonce, plaintext, data []byte) []byte {
// Preallocate output buffer