aboutsummaryrefslogtreecommitdiff
path: root/internal/cryptocore/cryptocore.go
blob: 9e25bfa7a55714e8d667485be9e4d19121460caa (plain)
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
// Package cryptocore wraps OpenSSL and Go GCM crypto and provides
// a nonce generator.
package cryptocore

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha512"
	"fmt"
	"log"

	"github.com/rfjakob/eme"

	"github.com/rfjakob/gocryptfs/internal/siv_aead"
	"github.com/rfjakob/gocryptfs/internal/stupidgcm"
)

// AEADTypeEnum indicates the type of AEAD backend in use.
type AEADTypeEnum int

const (
	// KeyLen is the cipher key length in bytes.  32 for AES-256.
	KeyLen = 32
	// AuthTagLen is the length of a GCM auth tag in bytes.
	AuthTagLen = 16

	_ = iota // Skip zero
	// BackendOpenSSL specifies the OpenSSL backend.
	BackendOpenSSL AEADTypeEnum = iota
	// BackendGoGCM specifies the Go based GCM backend.
	BackendGoGCM AEADTypeEnum = iota
	// BackendAESSIV specifies an AESSIV backend.
	BackendAESSIV AEADTypeEnum = iota
)

// CryptoCore is the low level crypto implementation.
type CryptoCore struct {
	// EME is used for filename encryption.
	EMECipher *eme.EMECipher
	// GCM or AES-SIV. This is used for content encryption.
	AEADCipher cipher.AEAD
	// Which backend is behind AEADCipher?
	AEADBackend AEADTypeEnum
	// GCM needs unique IVs (nonces)
	IVGenerator *nonceGenerator
	IVLen       int
}

// New returns a new CryptoCore object or panics.
//
// Even though the "GCMIV128" feature flag is now mandatory, we must still
// support 96-bit IVs here because they were used for encrypting the master
// key in gocryptfs.conf up to gocryptfs v1.2. v1.3 switched to 128 bits.
func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDecode bool) *CryptoCore {
	if len(key) != KeyLen {
		log.Panic(fmt.Sprintf("Unsupported key length %d", len(key)))
	}
	// We want the IV size in bytes
	IVLen := IVBitLen / 8

	// Initialize EME for filename encryption.
	var emeCipher *eme.EMECipher
	{
		emeKey := key
		if useHKDF {
			emeKey = hkdfDerive(key, hkdfInfoEMENames, KeyLen)
		}
		emeBlockCipher, err := aes.NewCipher(emeKey)
		if err != nil {
			log.Panic(err)
		}
		emeCipher = eme.New(emeBlockCipher)
	}

	// Initialize an AEAD cipher for file content encryption.
	var aeadCipher cipher.AEAD
	if aeadType == BackendOpenSSL || aeadType == BackendGoGCM {
		gcmKey := key
		if useHKDF {
			gcmKey = hkdfDerive(key, hkdfInfoGCMContent, KeyLen)
		}
		switch aeadType {
		case BackendOpenSSL:
			if IVLen != 16 {
				log.Panic("stupidgcm only supports 128-bit IVs")
			}
			// stupidgcm does not create a private copy of the key, so things
			// break when initFuseFrontend() overwrites it with zeros. Create
			// a copy here. This is unnecessary when useHKDF == true, but
			// does no harm.
			var stupidgcmKey []byte
			stupidgcmKey = append(stupidgcmKey, gcmKey...)
			aeadCipher = stupidgcm.New(stupidgcmKey, forceDecode)
		case BackendGoGCM:
			goGcmBlockCipher, err := aes.NewCipher(gcmKey)
			if err != nil {
				log.Panic(err)
			}
			aeadCipher, err = cipher.NewGCMWithNonceSize(goGcmBlockCipher, IVLen)
			if err != nil {
				log.Panic(err)
			}
		}
	} else if aeadType == BackendAESSIV {
		if IVLen != 16 {
			// SIV supports any nonce size, but we only use 16.
			log.Panic("AES-SIV must use 16-byte nonces")
		}
		var key64 []byte
		if useHKDF {
			key64 = hkdfDerive(key, hkdfInfoSIVContent, siv_aead.KeyLen)
		} else {
			// AES-SIV uses 1/2 of the key for authentication, 1/2 for
			// encryption, so we need a 64-bytes key for AES-256. Derive it from
			// the master key by hashing it with SHA-512.
			s := sha512.Sum512(key)
			key64 = s[:]
		}
		aeadCipher = siv_aead.New(key64)
	} else {
		log.Panic("unknown backend cipher")
	}

	return &CryptoCore{
		EMECipher:   emeCipher,
		AEADCipher:  aeadCipher,
		AEADBackend: aeadType,
		IVGenerator: &nonceGenerator{nonceLen: IVLen},
		IVLen:       IVLen,
	}
}