2016-07-06 21:51:25 +02:00
|
|
|
// Package cryptocore wraps OpenSSL and Go GCM crypto and provides
|
|
|
|
// a nonce generator.
|
2016-02-06 19:20:54 +01:00
|
|
|
package cryptocore
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/aes"
|
2016-02-06 20:23:36 +01:00
|
|
|
"crypto/cipher"
|
2016-10-04 00:12:29 +02:00
|
|
|
"crypto/sha512"
|
2016-12-10 11:50:16 +01:00
|
|
|
"log"
|
2018-02-18 11:33:47 +01:00
|
|
|
"runtime"
|
2016-05-04 19:09:14 +02:00
|
|
|
|
2021-08-21 12:08:37 +02:00
|
|
|
"golang.org/x/crypto/chacha20poly1305"
|
|
|
|
|
2017-03-05 13:58:24 +01:00
|
|
|
"github.com/rfjakob/eme"
|
|
|
|
|
2021-08-23 15:05:15 +02:00
|
|
|
"github.com/rfjakob/gocryptfs/v2/internal/siv_aead"
|
|
|
|
"github.com/rfjakob/gocryptfs/v2/internal/stupidgcm"
|
|
|
|
"github.com/rfjakob/gocryptfs/v2/internal/tlog"
|
2016-02-06 19:20:54 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2021-08-23 22:10:23 +02:00
|
|
|
// KeyLen is the cipher key length in bytes. All backends use 32 bytes.
|
2016-10-02 06:14:18 +02:00
|
|
|
KeyLen = 32
|
2021-08-23 22:10:23 +02:00
|
|
|
// AuthTagLen is the length of a authentication tag in bytes.
|
|
|
|
// All backends use 16 bytes.
|
2016-02-06 20:23:36 +01:00
|
|
|
AuthTagLen = 16
|
2018-02-18 16:20:38 +01:00
|
|
|
)
|
2016-09-20 21:58:04 +02:00
|
|
|
|
2018-02-18 16:20:38 +01:00
|
|
|
// AEADTypeEnum indicates the type of AEAD backend in use.
|
2021-08-23 22:10:23 +02:00
|
|
|
type AEADTypeEnum struct {
|
|
|
|
Name string
|
|
|
|
NonceSize int
|
|
|
|
}
|
2018-02-18 16:20:38 +01:00
|
|
|
|
2021-09-08 20:32:16 +02:00
|
|
|
// BackendOpenSSL specifies the OpenSSL AES-256-GCM backend.
|
2021-08-23 22:10:23 +02:00
|
|
|
// "AES-GCM-256-OpenSSL" in gocryptfs -speed.
|
|
|
|
var BackendOpenSSL AEADTypeEnum = AEADTypeEnum{"AES-GCM-256-OpenSSL", 16}
|
2016-02-06 19:20:54 +01:00
|
|
|
|
2021-09-08 20:32:16 +02:00
|
|
|
// BackendGoGCM specifies the Go based AES-256-GCM backend.
|
2021-08-23 22:10:23 +02:00
|
|
|
// "AES-GCM-256-Go" in gocryptfs -speed.
|
|
|
|
var BackendGoGCM AEADTypeEnum = AEADTypeEnum{"AES-GCM-256-Go", 16}
|
|
|
|
|
|
|
|
// BackendAESSIV specifies an AESSIV backend.
|
|
|
|
// "AES-SIV-512-Go" in gocryptfs -speed.
|
|
|
|
var BackendAESSIV AEADTypeEnum = AEADTypeEnum{"AES-SIV-512-Go", siv_aead.NonceSize}
|
|
|
|
|
|
|
|
// BackendXChaCha20Poly1305 specifies XChaCha20-Poly1305-Go.
|
|
|
|
// "XChaCha20-Poly1305-Go" in gocryptfs -speed.
|
|
|
|
var BackendXChaCha20Poly1305 AEADTypeEnum = AEADTypeEnum{"XChaCha20-Poly1305-Go", chacha20poly1305.NonceSizeX}
|
2021-06-21 11:32:04 +02:00
|
|
|
|
2021-09-02 11:33:06 +02:00
|
|
|
// BackendXChaCha20Poly1305OpenSSL specifies XChaCha20-Poly1305-OpenSSL.
|
|
|
|
var BackendXChaCha20Poly1305OpenSSL AEADTypeEnum = AEADTypeEnum{"XChaCha20-Poly1305-OpenSSL", chacha20poly1305.NonceSizeX}
|
|
|
|
|
2016-10-02 06:14:18 +02:00
|
|
|
// CryptoCore is the low level crypto implementation.
|
2016-02-06 19:20:54 +01:00
|
|
|
type CryptoCore struct {
|
2017-03-05 13:58:24 +01:00
|
|
|
// EME is used for filename encryption.
|
|
|
|
EMECipher *eme.EMECipher
|
2016-09-26 23:25:13 +02:00
|
|
|
// GCM or AES-SIV. This is used for content encryption.
|
2016-09-20 21:58:04 +02:00
|
|
|
AEADCipher cipher.AEAD
|
2016-09-20 22:59:10 +02:00
|
|
|
// Which backend is behind AEADCipher?
|
2017-03-05 17:08:16 +01:00
|
|
|
AEADBackend AEADTypeEnum
|
2016-09-20 21:58:04 +02:00
|
|
|
// GCM needs unique IVs (nonces)
|
|
|
|
IVGenerator *nonceGenerator
|
2021-08-21 10:55:20 +02:00
|
|
|
// IVLen in bytes
|
|
|
|
IVLen int
|
2016-02-06 19:20:54 +01:00
|
|
|
}
|
|
|
|
|
2016-10-02 06:14:18 +02:00
|
|
|
// New returns a new CryptoCore object or panics.
|
2016-07-31 13:36:38 +02:00
|
|
|
//
|
|
|
|
// Even though the "GCMIV128" feature flag is now mandatory, we must still
|
2017-03-05 18:03:03 +01:00
|
|
|
// 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.
|
2018-02-18 12:41:11 +01:00
|
|
|
//
|
|
|
|
// Note: "key" is either the scrypt hash of the password (when decrypting
|
|
|
|
// a config file) or the masterkey (when finally mounting the filesystem).
|
2017-04-08 02:09:28 +02:00
|
|
|
func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDecode bool) *CryptoCore {
|
2021-06-21 11:32:04 +02:00
|
|
|
tlog.Debug.Printf("cryptocore.New: key=%d bytes, aeadType=%v, IVBitLen=%d, useHKDF=%v, forceDecode=%v",
|
|
|
|
len(key), aeadType, IVBitLen, useHKDF, forceDecode)
|
|
|
|
|
2016-02-06 19:20:54 +01:00
|
|
|
if len(key) != KeyLen {
|
2021-08-21 10:55:20 +02:00
|
|
|
log.Panicf("Unsupported key length of %d bytes", len(key))
|
|
|
|
}
|
2021-08-21 12:08:37 +02:00
|
|
|
if IVBitLen != 96 && IVBitLen != 128 && IVBitLen != chacha20poly1305.NonceSizeX*8 {
|
2021-08-21 10:55:20 +02:00
|
|
|
log.Panicf("Unsupported IV length of %d bits", IVBitLen)
|
2016-02-06 19:20:54 +01:00
|
|
|
}
|
|
|
|
|
2017-03-05 21:59:55 +01:00
|
|
|
// Initialize EME for filename encryption.
|
|
|
|
var emeCipher *eme.EMECipher
|
2018-02-18 16:07:09 +01:00
|
|
|
var err error
|
2017-03-05 21:59:55 +01:00
|
|
|
{
|
2018-02-18 16:07:09 +01:00
|
|
|
var emeBlockCipher cipher.Block
|
2017-03-05 21:59:55 +01:00
|
|
|
if useHKDF {
|
2018-02-18 16:07:09 +01:00
|
|
|
emeKey := hkdfDerive(key, hkdfInfoEMENames, KeyLen)
|
|
|
|
emeBlockCipher, err = aes.NewCipher(emeKey)
|
|
|
|
for i := range emeKey {
|
|
|
|
emeKey[i] = 0
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
emeBlockCipher, err = aes.NewCipher(key)
|
2017-03-05 21:59:55 +01:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
emeCipher = eme.New(emeBlockCipher)
|
2016-02-06 19:20:54 +01:00
|
|
|
}
|
|
|
|
|
2017-08-11 18:42:30 +02:00
|
|
|
// Initialize an AEAD cipher for file content encryption.
|
2016-10-04 00:15:27 +02:00
|
|
|
var aeadCipher cipher.AEAD
|
2017-03-05 21:59:55 +01:00
|
|
|
if aeadType == BackendOpenSSL || aeadType == BackendGoGCM {
|
2018-02-18 16:07:09 +01:00
|
|
|
var gcmKey []byte
|
2017-03-05 21:59:55 +01:00
|
|
|
if useHKDF {
|
2017-05-27 14:41:20 +02:00
|
|
|
gcmKey = hkdfDerive(key, hkdfInfoGCMContent, KeyLen)
|
2018-02-18 16:07:09 +01:00
|
|
|
} else {
|
2021-08-21 10:55:20 +02:00
|
|
|
// Filesystems created by gocryptfs v0.7 through v1.2 don't use HKDF.
|
|
|
|
// Example: tests/example_filesystems/v0.9
|
2018-02-18 16:07:09 +01:00
|
|
|
gcmKey = append([]byte{}, key...)
|
2016-09-20 21:58:04 +02:00
|
|
|
}
|
2017-03-05 21:59:55 +01:00
|
|
|
switch aeadType {
|
|
|
|
case BackendOpenSSL:
|
2021-08-21 10:55:20 +02:00
|
|
|
if IVBitLen != 128 {
|
|
|
|
log.Panicf("stupidgcm only supports 128-bit IVs, you wanted %d", IVBitLen)
|
2017-03-05 21:59:55 +01:00
|
|
|
}
|
2021-09-07 18:11:11 +02:00
|
|
|
aeadCipher = stupidgcm.NewAES256GCM(gcmKey, forceDecode)
|
2017-03-05 21:59:55 +01:00
|
|
|
case BackendGoGCM:
|
|
|
|
goGcmBlockCipher, err := aes.NewCipher(gcmKey)
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
2021-08-21 10:55:20 +02:00
|
|
|
aeadCipher, err = cipher.NewGCMWithNonceSize(goGcmBlockCipher, IVBitLen/8)
|
2017-03-05 21:59:55 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
2021-09-08 20:32:16 +02:00
|
|
|
default:
|
|
|
|
log.Panicf("BUG: unhandled case: %v", aeadType)
|
2017-03-05 21:59:55 +01:00
|
|
|
}
|
2018-02-18 16:07:09 +01:00
|
|
|
for i := range gcmKey {
|
|
|
|
gcmKey[i] = 0
|
|
|
|
}
|
2017-03-05 21:59:55 +01:00
|
|
|
} else if aeadType == BackendAESSIV {
|
2021-08-21 10:55:20 +02:00
|
|
|
if IVBitLen != 128 {
|
|
|
|
// SIV supports any nonce size, but we only use 128.
|
|
|
|
log.Panicf("AES-SIV must use 128-bit IVs, you wanted %d", IVBitLen)
|
2016-10-09 17:05:12 +02:00
|
|
|
}
|
2018-02-18 16:07:09 +01:00
|
|
|
// 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 32-byte master key using HKDF, or, for older filesystems, with
|
|
|
|
// SHA256.
|
2017-03-05 21:59:55 +01:00
|
|
|
var key64 []byte
|
|
|
|
if useHKDF {
|
2017-05-27 14:41:20 +02:00
|
|
|
key64 = hkdfDerive(key, hkdfInfoSIVContent, siv_aead.KeyLen)
|
2017-03-05 21:59:55 +01:00
|
|
|
} else {
|
|
|
|
s := sha512.Sum512(key)
|
|
|
|
key64 = s[:]
|
|
|
|
}
|
|
|
|
aeadCipher = siv_aead.New(key64)
|
2018-02-18 16:07:09 +01:00
|
|
|
for i := range key64 {
|
|
|
|
key64[i] = 0
|
|
|
|
}
|
2021-09-08 20:32:16 +02:00
|
|
|
} else if aeadType == BackendXChaCha20Poly1305 || aeadType == BackendXChaCha20Poly1305OpenSSL {
|
2021-08-21 12:08:37 +02:00
|
|
|
// We don't support legacy modes with XChaCha20-Poly1305
|
|
|
|
if IVBitLen != chacha20poly1305.NonceSizeX*8 {
|
|
|
|
log.Panicf("XChaCha20-Poly1305 must use 192-bit IVs, you wanted %d", IVBitLen)
|
|
|
|
}
|
|
|
|
if !useHKDF {
|
|
|
|
log.Panic("XChaCha20-Poly1305 must use HKDF, but it is disabled")
|
|
|
|
}
|
|
|
|
derivedKey := hkdfDerive(key, hkdfInfoXChaChaPoly1305Content, chacha20poly1305.KeySize)
|
2021-09-08 20:32:16 +02:00
|
|
|
if aeadType == BackendXChaCha20Poly1305 {
|
|
|
|
aeadCipher, err = chacha20poly1305.NewX(derivedKey)
|
|
|
|
} else if aeadType == BackendXChaCha20Poly1305OpenSSL {
|
|
|
|
aeadCipher = stupidgcm.NewXchacha20poly1305(derivedKey)
|
|
|
|
} else {
|
|
|
|
log.Panicf("BUG: unhandled case: %v", aeadType)
|
|
|
|
}
|
2021-08-21 12:08:37 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
2017-03-05 21:59:55 +01:00
|
|
|
} else {
|
2021-08-23 22:10:23 +02:00
|
|
|
log.Panicf("unknown cipher backend %q", aeadType.Name)
|
2021-08-21 12:08:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if aeadCipher.NonceSize()*8 != IVBitLen {
|
|
|
|
log.Panicf("Mismatched aeadCipher.NonceSize*8=%d and IVBitLen=%d bits",
|
|
|
|
aeadCipher.NonceSize()*8, IVBitLen)
|
2016-09-20 21:58:04 +02:00
|
|
|
}
|
2016-02-06 19:20:54 +01:00
|
|
|
|
|
|
|
return &CryptoCore{
|
2017-03-05 13:58:24 +01:00
|
|
|
EMECipher: emeCipher,
|
2016-10-04 00:15:27 +02:00
|
|
|
AEADCipher: aeadCipher,
|
2017-03-05 17:08:16 +01:00
|
|
|
AEADBackend: aeadType,
|
2021-08-21 10:55:20 +02:00
|
|
|
IVGenerator: &nonceGenerator{nonceLen: IVBitLen / 8},
|
|
|
|
IVLen: IVBitLen / 8,
|
2016-02-06 19:20:54 +01:00
|
|
|
}
|
|
|
|
}
|
2018-02-18 11:33:47 +01:00
|
|
|
|
2018-02-18 16:07:09 +01:00
|
|
|
type wiper interface {
|
|
|
|
Wipe()
|
|
|
|
}
|
|
|
|
|
2018-02-18 11:33:47 +01:00
|
|
|
// Wipe tries to wipe secret keys from memory by overwriting them with zeros
|
|
|
|
// and/or setting references to nil.
|
|
|
|
//
|
|
|
|
// This is not bulletproof due to possible GC copies, but
|
|
|
|
// still raises to bar for extracting the key.
|
|
|
|
func (c *CryptoCore) Wipe() {
|
2018-02-18 16:07:09 +01:00
|
|
|
be := c.AEADBackend
|
|
|
|
if be == BackendOpenSSL || be == BackendAESSIV {
|
2021-08-23 22:10:23 +02:00
|
|
|
tlog.Debug.Printf("CryptoCore.Wipe: Wiping AEADBackend %s key", be.Name)
|
2018-02-18 11:33:47 +01:00
|
|
|
// We don't use "x, ok :=" because we *want* to crash loudly if the
|
2018-02-18 16:07:09 +01:00
|
|
|
// type assertion fails.
|
|
|
|
w := c.AEADCipher.(wiper)
|
|
|
|
w.Wipe()
|
2018-02-18 11:33:47 +01:00
|
|
|
} else {
|
2018-02-28 09:02:18 +01:00
|
|
|
tlog.Debug.Printf("CryptoCore.Wipe: Only nil'ing stdlib refs")
|
2018-02-18 11:33:47 +01:00
|
|
|
}
|
|
|
|
// We have no access to the keys (or key-equivalents) stored inside the
|
|
|
|
// Go stdlib. Best we can is to nil the references and force a GC.
|
|
|
|
c.AEADCipher = nil
|
|
|
|
c.EMECipher = nil
|
|
|
|
runtime.GC()
|
|
|
|
}
|