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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
package cryptfs
// File content encryption / decryption
import (
"bytes"
"os"
"errors"
"crypto/cipher"
)
type CryptFile struct {
file *os.File
gcm cipher.AEAD
}
// DecryptBlocks - Decrypt a number of blocks
func (be *CryptFS) DecryptBlocks(ciphertext []byte) ([]byte, error) {
cBuf := bytes.NewBuffer(ciphertext)
var err error
var pBuf bytes.Buffer
for cBuf.Len() > 0 {
cBlock := cBuf.Next(int(be.cipherBS))
pBlock, err := be.DecryptBlock(cBlock)
if err != nil {
break
}
pBuf.Write(pBlock)
}
return pBuf.Bytes(), err
}
// DecryptBlock - Verify and decrypt GCM block
func (be *CryptFS) DecryptBlock(ciphertext []byte) ([]byte, error) {
// Empty block?
if len(ciphertext) == 0 {
return ciphertext, nil
}
if len(ciphertext) < NONCE_LEN {
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]
ciphertext = ciphertext[NONCE_LEN:]
// Decrypt
var plaintext []byte
plaintext, err := be.gcm.Open(plaintext, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
// encryptBlock - Encrypt and add MAC using GCM
func (be *CryptFS) EncryptBlock(plaintext []byte) []byte {
// Empty block?
if len(plaintext) == 0 {
return plaintext
}
// Get fresh nonce
nonce := gcmNonce.Get()
// Encrypt plaintext and append to nonce
ciphertext := be.gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext
}
// Split a plaintext byte range into (possible partial) blocks
func (be *CryptFS) SplitRange(offset uint64, length uint64) []intraBlock {
var b intraBlock
var parts []intraBlock
b.fs = be
for length > 0 {
b.BlockNo = offset / be.plainBS
b.Offset = offset % be.plainBS
b.Length = be.minu64(length, be.plainBS - b.Offset)
parts = append(parts, b)
offset += b.Length
length -= b.Length
}
return parts
}
// PlainSize - calculate plaintext size from ciphertext size
func (be *CryptFS) PlainSize(size uint64) uint64 {
// Zero sized files stay zero-sized
if size > 0 {
overhead := be.cipherBS - be.plainBS
nBlocks := (size + be.cipherBS - 1) / be.cipherBS
size -= nBlocks * overhead
}
return size
}
func (be *CryptFS) minu64(x uint64, y uint64) uint64 {
if x < y {
return x
}
return y
}
// Get the byte range in the ciphertext corresponding to blocks
// (full blocks!)
func (be *CryptFS) JoinCiphertextRange(blocks []intraBlock) (uint64, uint64) {
offset, _ := blocks[0].CiphertextRange()
last := blocks[len(blocks)-1]
length := (last.BlockNo - blocks[0].BlockNo + 1) * be.cipherBS
return offset, length
}
// Crop plaintext that correspons to complete cipher blocks down to what is
// requested according to "iblocks"
func (be *CryptFS) CropPlaintext(plaintext []byte, blocks []intraBlock) []byte {
offset := blocks[0].Offset
last := blocks[len(blocks)-1]
length := (last.BlockNo - blocks[0].BlockNo + 1) * be.plainBS
var cropped []byte
if offset + length > uint64(len(plaintext)) {
cropped = plaintext[offset:len(plaintext)]
} else {
cropped = plaintext[offset:offset+length]
}
return cropped
}
|