filecrypt

package
v0.0.0-...-8cf541d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 18, 2020 License: GPL-3.0 Imports: 15 Imported by: 0

README

Filecrypt

Package filecrypt implements filecrypt encryption and decryption protocol

Filecrypt procotol is used to encrypt a set of different data structures into a file. It adds enough information in a header to be able to decrypt it at a later time.

A filecrypt object includes a Digest Header, Key Header, a Encryption header, and a sequence of payload blocks. There may be multiple filecrypt blocks with different encryption options. Typically, a filecrypt block includes a single data structure. Multiple filecrypt blocks can be appended toguether to encrypt several data structures with different Encryption Headers.

All headers are always unencrypted. Payload blocks may or may not be encryted.

NOTE Before using filecrypt, you should call gob.Register() function to register the data strcutre you want to backup

Filecrypt currently supports two encryption schemes:

  • GCM : Symmetric Encryption
  • RSA : Asymmetric Encryption

Digest Header Format

Field Length Description
Version 1 Byte Version 0
nBlocks 8 Bytes Number of FileCrypt blocks

For every block:

Field Length Description
Offset 8 Bytes Startig location of FileCrypt block (in bytes)
Tag 9 Bytes Tag to query the block during decryption. First byte includes tag size in bytes

Key Header Format

It includes information to generate a key from a master key

Field Length Description
Version 1 Byte Version 0
Key Type 1 Byte Different key types : no key (FC_KEY_T_NOKEY), direct key (FC_KEY_T_DIRECT) or PBKDF2 (FC_KEY_T_PBKDF2)
VAR Variable size variable fields

There is only a single key header per file. All blocks use same key derivation mechanism:

  • No Key : Payload Not encrypted
  • Direct Key : Key is used as is to encrypt payload
  • PBKDF2 : Uses PBKDF2 as Key Derivation function

If mechanism selected is PBKDF2, vAR fields include:

Field Length Description
Length 1 Byte Variable field length
Hash 1 Byte Hash function used
Iter 4 Byte Number of iterations
KeyLen 1 Byte Resulting key length in bytes
SaltLen 1 Byte Salt Length

Currently No Hash (FC_NOHASH) or SHA256 (FC_HASH_SHA256) are implemented as hash functions

Encryption Header Format

Encryption Header is 16 bytes long. Contents include

Field Length Description
Version 1 Byte Version 0
FC type 1 byte encryption type. Not encrypted (FC_CLEAR), GCM (FC_GCM) , RSA (FC_RSA)
Blocksize 1 byte Encryption block size. Currently 128 (FC_BSIZE_BYTES_128) and 256 (FC_BSIZE_BYTES_256) for GCM or 2048 (FC_BSIZE_BYTES_2048) and 4096 (FC_BSIZE_BYTES_4096) for RSA
Noncesize 1 byte Size in bytes of nonce. Can be 0
Last_blocksize 1 byte Size in bytes of last cleartext block
Nblocks 8 byte Number of blocks

Examples

GCM with Direct Key

In this example we will encrypt three different blocks with GCM-256


type FCTest struct{
   SecretText string
}

// init tests data
testData1 := initFCTest1(12)
testData2 := initFCTest1(2335)
testData3 := initFCTest1(123232)
testData := []*FCTest1{testData1, testData2, testData3}

// register struct
gob.Register(&FCTest1{})

// init key
key, err := genRandomBytes(FC_BSIZE_BYTES_256)

tags := [3]string{"BLOCK1", "BLOCK2", "BLOCK3"}
fc, err := New(3, "./testdata/sample1.dat", nil, key, FC_KEY_T_DIRECT)
if err != nil {
	fmt.Errorf("Error creating FileCrypt Object")
}

// encrypt first block
err = fc.AddBlock([]byte(tags[0]), FC_GCM, testData1)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// encrypt second block
err = fc.AddBlock([]byte(tags[1]), FC_GCM, testData2)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// encrypt last block
err = fc.AddBlock([]byte(tags[2]), FC_GCM, testData3)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// Decode filecrypt
newFC, err := NewFromFile("./testdata/sample1.dat")
if err != nil {
	fmt.Errorf("Error recovering FileCrypt Header") 
}

newTags := newFC.ListTags()
// Check Tags
for idx := 0; idx < len(tags); idx += 1 {
	if !bytes.Equal(newTags[idx], []byte(tags[idx])) {
               fmt.Errorf("Tags not equal")
	}
}
result, err := newFC.DecryptAll(key)
if err != nil {
	fmt.Errorf("Error Decrypting") 
}
if len(result) != 3 {
	fmt.Errorf("Unexpected result length")
}
r1 := *result[0].(*FCTest1)
r2 := *result[1].(*FCTest1)
r3 := *result[2].(*FCTest1)

if r1 != *testData1 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}
if r2 != *testData2 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}
if r3 != *testData3 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}

for idx := 0; idx < len(tags); idx += 1 {
	result, err := newFC.DecryptSingle([]byte(tags[idx]), key)
	if err != nil {
	      fmt.Errorf("Error Decrypting") 
	}
	r := *result.(*FCTest1)
	if r != *testData[idx] {
	   fmt.Errorf("Encrypted and decrypted values not equal")
	}

}

RSA with Direct Key

In this example we will encrypt three different blocks with GCM-256

type FCTest struct{
   SecretText string
}

// init tests data
testData1 := initFCTest1(12)
testData2 := initFCTest1(2335)
testData3 := initFCTest1(123232)
testData := []*FCTest1{testData1, testData2, testData3}

// register struct
gob.Register(&FCTest1{})

// init key
privKey, _ := rsa.GenerateKey(cr.Reader, FC_BSIZE_BYTES_2048*8)
publicKeyB, _ := json.Marshal(privKey.PublicKey)
privateKeyB, _ := json.Marshal(privKey)

tags := [3]string{"BLOCK1", "BLOCK2", "BLOCK3"}
fc, err := New(3, "./testdata/sample1.dat", nil, publicKeyB, FC_KEY_T_DIRECT)
if err != nil {
	fmt.Errorf("Error creating FileCrypt Object")
}

// encrypt first block
err = fc.AddBlock([]byte(tags[0]), FC_RSA, testData1)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// encrypt second block
err = fc.AddBlock([]byte(tags[1]), FC_RSA, testData2)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// encrypt last block
err = fc.AddBlock([]byte(tags[2]), FC_RSA, testData3)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// Decode filecrypt
newFC, err := NewFromFile("./testdata/sample1.dat")
if err != nil {
	fmt.Errorf("Error recovering FileCrypt Header") 
}

newTags := newFC.ListTags()
// Check Tags
for idx := 0; idx < len(tags); idx += 1 {
	if !bytes.Equal(newTags[idx], []byte(tags[idx])) {
               fmt.Errorf("Tags not equal")
	}
}
result, err := newFC.DecryptAll(privateKeyB)
if err != nil {
	fmt.Errorf("Error Decrypting") 
}
if len(result) != 3 {
	fmt.Errorf("Unexpected result length")
}
r1 := *result[0].(*FCTest1)
r2 := *result[1].(*FCTest1)
r3 := *result[2].(*FCTest1)

if r1 != *testData1 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}
if r2 != *testData2 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}
if r3 != *testData3 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}

for idx := 0; idx < len(tags); idx += 1 {
	result, err := newFC.DecryptSingle([]byte(tags[idx]), privateKeyB)
	if err != nil {
	      fmt.Errorf("Error Decrypting") 
	}
	r := *result.(*FCTest1)
	if r != *testData[idx] {
	   fmt.Errorf("Encrypted and decrypted values not equal")
	}

}

Mixed No encryption and GCM with PBKDF2

In this example we will encrypt the first and third blocks with GCM-256 using PBKDF2 key derivation function and the second block will be unencrypted.


type FCTest struct{
   SecretText string
}

// init tests data
testData1 := initFCTest1(12)
testData2 := initFCTest1(2335)
testData3 := initFCTest1(123232)
testData := []*FCTest1{testData1, testData2, testData3}

// register struct
gob.Register(&FCTest1{})

// init key
key, err := genRandomBytes(FC_BSIZE_BYTES_256)

tags := [3]string{"BLOCK1", "BLOCK2", "BLOCK3"}
fc, err := New(3, "./testdata/sample1.dat", nil, key, FC_KEY_T_PBKDF2)
if err != nil {
	fmt.Errorf("Error creating FileCrypt Object")
}

// encrypt first block
err = fc.AddBlock([]byte(tags[0]), FC_GCM, testData1)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// encrypt second block
err = fc.AddBlock([]byte(tags[1]), FC_CLEAR, testData2)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// encrypt last block
err = fc.AddBlock([]byte(tags[2]), FC_GCM, testData3)
if err != nil {
	fmt.Errorf("Error Creating FileCrypt Block") 
}

// Decode filecrypt
newFC, err := NewFromFile("./testdata/sample1.dat")
if err != nil {
	fmt.Errorf("Error recovering FileCrypt Header") 
}

newTags := newFC.ListTags()
// Check Tags
for idx := 0; idx < len(tags); idx += 1 {
	if !bytes.Equal(newTags[idx], []byte(tags[idx])) {
               fmt.Errorf("Tags not equal")
	}
}
result, err := newFC.DecryptAll(key)
if err != nil {
	fmt.Errorf("Error Decrypting") 
}
if len(result) != 3 {
	fmt.Errorf("Unexpected result length")
}
r1 := *result[0].(*FCTest1)
r2 := *result[1].(*FCTest1)
r3 := *result[2].(*FCTest1)

if r1 != *testData1 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}
if r2 != *testData2 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}
if r3 != *testData3 {
	fmt.Errorf("Encrypted and decrypted values not equal")
}

for idx := 0; idx < len(tags); idx += 1 {
	result, err := newFC.DecryptSingle([]byte(tags[idx]), key)
	if err != nil {
	      fmt.Errorf("Error Decrypting") 
	}
	r := *result.(*FCTest1)
	if r != *testData[idx] {
	   fmt.Errorf("Encrypted and decrypted values not equal")
	}

}

Documentation

Index

Constants

View Source
const (
	FC_DIRECTKEYHDR_END_OFFSET = 2
	FC_DIRECTKEYHDR_NPARMS     = 2
)

Hdr format

View Source
const (
	FC_NOHASH = iota
	FC_HASH_SHA256
	FC_NHASH
)

Hash Functions

View Source
const (
	FC_HDR_REG_VERSION_SIZE = 1
	FC_HDR_REG_SEALT_SIZE   = 1
	FC_HDR_REG_NBLOCKS_SIZE = 8
	FC_HDR_REG_NONCE_SIZE   = FC_SEAL_SALTLEN
	FC_HDR_REG_BOFFSET_SIZE = 8
	FC_HDR_REG_BTAG_SIZE    = 9

	FC_HDR_REG_VERSION_OFFSET = 0
	FC_HDR_REG_SEALT_OFFSET   = FC_HDR_REG_VERSION_SIZE
	FC_HDR_REG_NBLOCKS_OFFSET = FC_HDR_REG_SEALT_OFFSET + FC_HDR_REG_SEALT_SIZE
	FC_HDR_REG_NONCE_OFFSET   = FC_HDR_REG_NBLOCKS_OFFSET + FC_HDR_REG_NBLOCKS_SIZE
	FC_HDR_REG_BDATA_OFFSET   = FC_HDR_REG_NONCE_OFFSET + FC_HDR_REG_NONCE_SIZE
	FC_HDR_REG_BOFFSET_OFFSET = 0
	FC_HDR_REG_BTAG_OFFSET    = FC_HDR_REG_BOFFSET_SIZE
	FC_HDR_REG_END_OFFSET     = FC_HDR_REG_BTAG_OFFSET + FC_HDR_REG_BTAG_SIZE
)

Offset and Sizes

View Source
const (
	MODE_INIT = iota
	MODE_MODIFY
)
View Source
const (
	FC_HDR_VERSION_1 = iota
	FC_HDR_NVERSION
)

Version (Backwards interop)

View Source
const (
	FC_SEAL_SALTLEN = 16
	FC_SEAL_NITER   = 80000
	FC_SEAL_KLEN    = FC_BSIZE_BYTES_256
	FC_SEAL_LEN     = FC_BSIZE_BYTES_256
)
View Source
const (
	FC_CLEAR = iota // No encryption
	FC_GCM          // GCM
	FC_RSA
	FC_NTYPE
)

Filecrypt supported encryption schemes

View Source
const (
	FC_BSIZE_BYTES_128  = 16
	FC_BSIZE_BYTES_256  = 32
	FC_BSIZE_BYTES_2048 = 256
	FC_BSIZE_BYTES_4096 = 512
)

Supported encryption block sizes in bytes

View Source
const (
	FC_HDRE_VERSION_1 = iota
	FC_HDRE_NVERSION
)

Version (Backwards interop)

View Source
const (
	FC_HDR_BSIZE_128 = iota
	FC_HDR_BSIZE_256
	FC_HDR_BSIZE_2048
	FC_HDR_BSIZE_4096
	FC_HDR_NBSIZE
)

block size

View Source
const (
	FC_HDR_VERSION_OFFSET        = 0
	FC_HDR_FCTYPE_OFFSET         = 1
	FC_HDR_BSIZE_OFFSET          = 2
	FC_HDR_NONCESIZE_OFFSET      = 3
	FC_HDR_LAST_BLOCKSIZE_OFFSET = 4
	FC_HDR_NBLOCKS_OFFSET        = 5
	FC_HDR_END_OFFSET            = 13
)

Hdr format

View Source
const (
	FC_KEY_T_NOKEY  = iota // no Key
	FC_KEY_T_DIRECT        // DIRECT
	FC_KEY_T_PBKDF2        // PBKDF2
	FC_KEY_NTYPE
)

KDF Supported Types

View Source
const (
	FC_HDRK_VERSION_1 = iota
	FC_HDREK_NVERSION
)

Version (Backwards interop)

View Source
const (
	FC_NOKEYHDR_END_OFFSET = 2
	FC_NOKEYHDR_NPARAMS    = 2
)

Hdr format

View Source
const (
	FC_PBKDF2HDR_MAXITER     = 100000
	FC_PBKDF2HDR_SALT_MAXLEN = 128
	FC_PBKDF2HDR_OUT_MAXLEN  = 128
	FC_PBKDF2HDR_MINPARAMS   = 2
	FC_PBKDF2HDR_MAXPARAMS   = 6
)
View Source
const (
	FC_PBKDF2HDR_LEN_OFFSET     = 2
	FC_PBKDF2HDR_HASH_OFFSET    = 3
	FC_PBKDF2HDR_ITER_OFFSET    = 4
	FC_PBKDF2HDR_OUTLEN_OFFSET  = 8
	FC_PBKDF2HDR_SALTLEN_OFFSET = 9
	FC_PBKDF2HDR_SALT_OFFSET    = 10
)

Hdr format

View Source
const (
	FC_PBKDF2HDR_DEF_HASH    = FC_HASH_SHA256
	FC_PBKDF2HDR_DEF_NITER   = 60000
	FC_PBKDF2HDR_DEF_OUTLEN  = FC_BSIZE_BYTES_256
	FC_PBKDF2HDR_DEF_SALTLEN = 12
)
View Source
const (
	FC_HDRE_DEF_VERSION = FC_HDRE_VERSION_1
)
View Source
const (
	FC_HDRK_DEF_VERSION = FC_HDRK_VERSION_1
)
View Source
const (
	FC_HDR_DEF_VERSION = FC_HDR_VERSION_1
)

Variables

This section is empty.

Functions

func NewHdrEncrypt

func NewHdrEncrypt(Version, Fctype, Blocksize int) (fileCryptEnc, error)

func NewHdrKey

func NewHdrKey(KeyIn []byte, params ...int) (fileCryptKey, error)

Types

type ClearFc

type ClearFc struct {
	// contains filtered or unexported fields
}

type DirectKeyFc

type DirectKeyFc struct {
	// contains filtered or unexported fields
}

type FileCrypt

type FileCrypt struct {
	// contains filtered or unexported fields
}

func New

func New(nBlocks int, fname string, hmacKey, fcKey []byte, fcKType int, params ...int) (*FileCrypt, error)

Constructor

params are optional parameters for the key header. If not supplied, the following default params are assigned:
   fcKTpye =  FC_KEY_T_NOKEY / FC__KEY_T_DIRECT  => No additional parameter required
   fcKType =  FC_KEY_T_PBKDF2     => hashType : FC_HASH_SHA256, Niter : 60000
                                     keyLen   : 256 B, Salt Len : 12 Bytes

func NewFromBytes

func NewFromBytes(reg []byte) *FileCrypt

Constructor from Bytes

func NewFromFile

func NewFromFile(hmacKey []byte, fname string) (*FileCrypt, error)

Constructor from File

func (*FileCrypt) AddBlock

func (fc *FileCrypt) AddBlock(tag []byte, encType int, cleartext interface{}) error

Add block to Filecrypt object. It will encrypt it according the specified headers

func (FileCrypt) DecryptAll

func (fc FileCrypt) DecryptAll(keyIn []byte) ([]interface{}, error)

Filecrypt decryption routine. Takes some cyphertext file and based on the type of decryption

specified in the header applies the desired decryption algorithm.

func (FileCrypt) DecryptSingle

func (fc FileCrypt) DecryptSingle(tag []byte, keyIn []byte) (interface{}, error)

Filecrypt decryption routine. Takes some cyphertext file and based on the type of decryption

specified in the header applies the desired decryption algorithm. Tag is used to decrypt block identified

by such tag

func (FileCrypt) HMACRead

func (fc FileCrypt) HMACRead() ([]byte, error)

Returns hmac code in FileCrypt object

func (FileCrypt) ListTags

func (fc FileCrypt) ListTags() [][]byte

List available tags

func (FileCrypt) NBlocks

func (fc FileCrypt) NBlocks() int

Returns number of FileCrypt blocks

func (*FileCrypt) Nonce

func (fc *FileCrypt) Nonce() []byte

Returns nonce used to derive hmac key

type GcmFc

type GcmFc struct {
	// contains filtered or unexported fields
}

type NoKeyFc

type NoKeyFc struct {
	// contains filtered or unexported fields
}

type Pbkdf2Fc

type Pbkdf2Fc struct {
	// contains filtered or unexported fields
}

Filecrypt Header added to every FC block

type RsaFc

type RsaFc struct {
	// contains filtered or unexported fields
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL