Skip to content

Commit

Permalink
first HSM integration for mxc-scc2 kernel driver
Browse files Browse the repository at this point in the history
  • Loading branch information
abarisani committed Jul 4, 2016
1 parent f47bc00 commit a9ce093
Show file tree
Hide file tree
Showing 13 changed files with 756 additions and 108 deletions.
58 changes: 55 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,24 @@ Messaging and file sharing:

* Signal protocol V2 via external library (https://github.com/janimo/textsecure)

Hardware Security Modules:

* NXP Security Controller (SCCv2)

The SCCv2 support allows symmetric AES-256-CBC ciphering using its device
specific secret key. This can be used to uniquely tie derived keys to the
individual hardware unit being used.

With the SCCv2, the AES-256-SCC symmetric cipher is available. This cipher is
identical to AES-256-OFB, however the password key derivation includes a stage
through SCCv2 encryption to make it device specific.

The LUKS passwords for accessing encrypted volumes can also be filtered
through the SCCv2 to make them device specific.

Finally INTERLOCK TLS certificates can be stored encrypted with SCCv2
support.

Key Storage
===========

Expand Down Expand Up @@ -248,11 +266,14 @@ Configuration
* bind_address: IP address, port pair.

* tls:

- "on" use tls_cert and tls_key paths as HTTPS TLS keypair;
- "off" disable HTTPS;

- "gen" generate a new TLS keypair and save it to tls_cert and tls_key
paths when pointing to non existent files (otherwise behaves like
"on"), useful for testing and TOFU (Trust On First Use) schemes.
"on"), useful for testing and TOFU (Trust On First Use) schemes;

- "off" disable HTTPS.

* tls_cert: HTTPS server TLS certificate.

Expand All @@ -262,11 +283,41 @@ Configuration
certificate requires TLS Web Client Authentication X509v3 Extended Key Usage
extension to be correctly validated.

* hsm:

- "<model>:<options>" enable <model> HSM support with <options>, multiple
options can be combined in a comma separated list
(e.g. "mxc-scc2":"luks,tls,cipher");

- "off" disable HSM support.

Available models:

- "mxc-scc2" NXP Security Controller (SCCv2), requires kernel driver
[mxc-scc2](https://github.com/inversepath/mxc-scc2).

Available options:

- "luks" use HSM secret key to AES encrypt LUKS passwords and
make them device specific before use; LUKS login and
password operations (add, change, remove) fallback, in
case of failure, to plain ones in order to allow change
of credentials on pre-HSM deployments;

- "tls" use HSM secret key to AES-256-OFB encrypt the HTTPS
server TLS key (tls_key), automatically convert
existing plaintext keys;

- "cipher" expose AES-256-OFB derived symmetric cipher with
password key derivation through HSM encryption to make
it device specific.

* key_path: path for public/private key storage on the encrypted filesystem.

* volume_group: volume group name.

* ciphers: array of cipher names to enable.
* ciphers: array of cipher names to enable, supported values are
["OpenPGP", "AES-256-OFB", "TOTP", "Signal"].

The following example illustrates the configuration file format (plain JSON)
and its default values.
Expand All @@ -281,6 +332,7 @@ and its default values.
"tls_cert": "certs/cert.pem",
"tls_key": "certs/key.pem",
"tls_client_ca": "",
"hsm": "off",
"key_path": "keys",
"volume_group": "lvmvolume"
"ciphers": [
Expand Down
1 change: 1 addition & 0 deletions interlock.conf.default
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"tls_cert": "certs/cert.pem",
"tls_key": "certs/key.pem",
"tls_client_ca": "",
"hsm": "off",
"key_path": "keys",
"volume_group": "lvmvolume",
"ciphers": [
Expand Down
140 changes: 74 additions & 66 deletions src/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ import (
"io"
"net/http"
"os"

"golang.org/x/crypto/pbkdf2"
)

// Symmetric file encryption using AES256OFB, key is derived from password
// Symmetric file encryption using AES-256-OFB, key is derived from password
// using PBKDF2 with SHA256 and 4096 rounds. The salt, initialization vector
// are prepended to the encrypted file, the HMAC for authentication is
// appended:
Expand All @@ -38,10 +36,10 @@ func init() {
conf.SetAvailableCipher(new(aes256OFB).Init())
}

func (a *aes256OFB) Init() (c cipherInterface) {
func (a *aes256OFB) Init() cipherInterface {
a.info = cipherInfo{
Name: "AES-256-OFB",
Description: "Go AES OFB with 32 bytes derived key using PBKDF2",
Description: "AES OFB w/ 256 bit key derived using PBKDF2",
KeyFormat: "password",
Enc: true,
Dec: true,
Expand Down Expand Up @@ -82,23 +80,88 @@ func (a *aes256OFB) Encrypt(input *os.File, output *os.File, sign bool) (err err
return errors.New("symmetric cipher does not support signing")
}

salt := make([]byte, 8)
_, err = io.ReadFull(rand.Reader, salt)
iv := make([]byte, aes.BlockSize)
_, err = io.ReadFull(rand.Reader, iv)

if err != nil {
return
}

key := pbkdf2.Key([]byte(a.password), salt, 4096, 32, sha256.New)
block, err := aes.NewCipher(key)
salt, key, err := DeriveKeyPBKDF2(nil, a.password)

if err != nil {
return
}

err = EncryptOFB(key, salt, iv, input, output)

return
}

func (a *aes256OFB) Decrypt(input *os.File, output *os.File, verify bool) (err error) {
if verify {
return errors.New("symmetric cipher does not support signature verification")
}

salt := make([]byte, 8)
_, err = io.ReadFull(input, salt)

if err != nil {
return
}

iv := make([]byte, aes.BlockSize)
_, err = io.ReadFull(input, iv)

_, err = io.ReadFull(rand.Reader, iv)
if err != nil {
return
}

_, key, err := DeriveKeyPBKDF2(salt, a.password)

if err != nil {
return
}

err = DecryptOFB(key, salt, iv, input, output)

return
}

func (a *aes256OFB) GenKey(i string, e string) (p string, s string, err error) {
err = errors.New("symmetric cipher does not support key generation")
return
}

func (a *aes256OFB) GetKeyInfo(k key) (i string, err error) {
err = errors.New("symmetric cipher does not support key")
return
}

func (a *aes256OFB) SetKey(k key) error {
return errors.New("symmetric cipher does not support key")
}

func (a *aes256OFB) Sign(i *os.File, o *os.File) error {
return errors.New("symmetric cipher does not support signing")
}

func (a *aes256OFB) Verify(i *os.File, s *os.File) error {
return errors.New("symmetric cipher does not support signature verification")
}

func (a *aes256OFB) GenOTP(timestamp int64) (otp string, exp int64, err error) {
err = errors.New("cipher does not support OTP generation")
return
}

func (a *aes256OFB) HandleRequest(w http.ResponseWriter, r *http.Request) (res jsonObject) {
res = notFound(w)
return
}

func EncryptOFB(key []byte, salt []byte, iv []byte, input *os.File, output *os.File) (err error) {
block, err := aes.NewCipher(key)

if err != nil {
return
Expand Down Expand Up @@ -153,32 +216,13 @@ func (a *aes256OFB) Encrypt(input *os.File, output *os.File, sign bool) (err err
return
}

func (a *aes256OFB) Decrypt(input *os.File, output *os.File, verify bool) (err error) {
if verify {
return errors.New("symmetric cipher does not support signature verification")
}

salt := make([]byte, 8)
_, err = io.ReadFull(input, salt)

if err != nil {
return
}

key := pbkdf2.Key([]byte(a.password), salt, 4096, 32, sha256.New)
func DecryptOFB(key []byte, salt []byte, iv []byte, input *os.File, output *os.File) (err error) {
block, err := aes.NewCipher(key)

if err != nil {
return
}

iv := make([]byte, aes.BlockSize)
_, err = io.ReadFull(input, iv)

if err != nil {
return
}

stat, err := input.Stat()

if err != nil {
Expand Down Expand Up @@ -223,41 +267,5 @@ func (a *aes256OFB) Decrypt(input *os.File, output *os.File, verify bool) (err e

_, err = io.Copy(writer, ciphertextReader)

if err != nil {
return
}

return
}

func (a *aes256OFB) GenKey(i string, e string) (p string, s string, err error) {
err = errors.New("symmetric cipher does not support key generation")
return
}

func (a *aes256OFB) GetKeyInfo(k key) (i string, err error) {
err = errors.New("symmetric cipher does not support key")
return
}

func (a *aes256OFB) SetKey(k key) error {
return errors.New("symmetric cipher does not support key")
}

func (a *aes256OFB) Sign(i *os.File, o *os.File) error {
return errors.New("symmetric cipher does not support signing")
}

func (a *aes256OFB) Verify(i *os.File, s *os.File) error {
return errors.New("symmetric cipher does not support signature verification")
}

func (a *aes256OFB) GenOTP(timestamp int64) (otp string, exp int64, err error) {
err = errors.New("cipher does not support OTP generation")
return
}

func (a *aes256OFB) HandleRequest(w http.ResponseWriter, r *http.Request) (res jsonObject) {
res = notFound(w)
return
}
55 changes: 55 additions & 0 deletions src/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"os"
"path/filepath"
"strconv"
"strings"
"time"
)

Expand All @@ -31,12 +32,16 @@ type config struct {
TLSCert string `json:"tls_cert"`
TLSKey string `json:"tls_key"`
TLSClientCA string `json:"tls_client_ca"`
HSM string `json:"hsm"`
KeyPath string `json:"key_path"`
VolumeGroup string `json:"volume_group"`
Ciphers []string `json:"ciphers"`

availableCiphers map[string]cipherInterface
enabledCiphers map[string]cipherInterface
availableHSMs map[string]HSMInterface
authHSM HSMInterface
tlsHSM HSMInterface
mountPoint string
testMode bool
logFile *os.File
Expand All @@ -52,6 +57,14 @@ func (c *config) SetAvailableCipher(cipher cipherInterface) {
c.availableCiphers[cipher.GetInfo().Name] = cipher
}

func (c *config) SetAvailableHSM(model string, HSM HSMInterface) {
if c.availableHSMs == nil {
c.availableHSMs = make(map[string]HSMInterface)
}

c.availableHSMs[model] = HSM
}

func (c *config) GetAvailableCipher(cipherName string) (cipher cipherInterface, err error) {
cipher, ok := c.availableCiphers[cipherName]

Expand Down Expand Up @@ -115,6 +128,47 @@ func (c *config) EnableCiphers() (err error) {
return
}

func (c *config) EnableHSM() (err error) {
if c.HSM == "off" {
return
}

HSMConf := strings.Split(c.HSM, ":")

if len(HSMConf) < 2 {
log.Fatal("invalid hsm configuration directive")
}

model := HSMConf[0]

if val, ok := c.availableHSMs[model]; ok {
options := strings.Split(HSMConf[1], ",")

status.Log(syslog.LOG_NOTICE, "enabling %s HSM %s", model, options)

HSM := val.New()

for i := 0; i < len(options); i++ {
switch options[i] {
case "luks":
c.authHSM = HSM
case "tls":
c.tlsHSM = HSM
case "cipher":
cipher := HSM.Cipher()
c.SetAvailableCipher(cipher)
c.enabledCiphers[cipher.GetInfo().Name] = cipher
default:
log.Fatal("invalid hsm option")
}
}
} else {
log.Fatal("invalid hsm model")
}

return
}

func (c *config) ActivateCiphers(activate bool) {
for _, val := range c.enabledCiphers {
err := val.Activate(activate)
Expand All @@ -140,6 +194,7 @@ func (c *config) SetDefaults() {
c.TLS = "on"
c.TLSCert = "certs/cert.pem"
c.TLSKey = "certs/key.pem"
c.HSM = "off"
c.KeyPath = "keys"
c.Ciphers = []string{"OpenPGP", "AES-256-OFB", "TOTP"}
c.testMode = false
Expand Down
Loading

0 comments on commit a9ce093

Please sign in to comment.