Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[se] add function to generate symmetric keys #30

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/spm/services/se.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package se

import (
"crypto/x509"
"github.com/lowRISC/opentitan-provisioning/src/pk11"
)

// Parameters for generating an RSA keypair.
Expand All @@ -29,6 +30,20 @@ type CertInfo struct {
WrappedKey, Iv, Cert []byte
}

const (
SymmetricKeyTypeRaw = iota
SymmetricKeyTypeHashedOtLcToken
)

// Parameters for GenerateSymmetricKey().
type SymmetricKeygenParams struct {
UseHighSecuritySeed bool
KeyType uint
SizeInBits uint
Sku string
Diversifier string
}

// SE is an interface representing a secure element, which may be implemented
// by various hardware modules under the hood.
//
Expand All @@ -50,6 +65,15 @@ type SE interface {
// successfully generated up until that point.
GenerateKeyPairAndCert(caCert *x509.Certificate, params []SigningParams) ([]CertInfo, error)

// Generates symmetric keys.
//
// These keys are generated via the HKDF mechanism and may be used as:
// - Wafer Authentication Secrets, or
// - Lifecycle Tokens.
//
// Returns: slice of AESKey objects.
GenerateSymmetricKey(params []*SymmetricKeygenParams) ([]pk11.AESKey, error)

// GenerateRandom returns random data extracted from the HSM.
GenerateRandom(length int) ([]byte, error)

Expand Down
73 changes: 70 additions & 3 deletions src/spm/services/se_pk11.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ func (q *sessionQueue) insert(s *pk11.Session) error {
// getHandle returns a session from the queue and a release function to
// get the session back into the queue. Recommended use:
//
// session, release := s.getHandle()
// defer release()
// session, release := s.getHandle()
// defer release()
//
// Note: failing to call the release function can result into deadlocks
// if the queue remains empty after calling the `insert` function.
Expand Down Expand Up @@ -87,6 +87,12 @@ type HSMConfig struct {
// KcaName is the KCA key label used to find the key in the HSM.
KcaName string

// KHsksName is the HighSecKdfSeed key label used to find the key in the HSM.
KHsksName string

// KLsksName is the LowSecKdfSeed key label used to find the key in the HSM.
KLsksName string

// hsmType contains the type of the HSM (SoftHSM or NetworkHSM)
HSMType pk11.HSMType
}
Expand All @@ -101,7 +107,7 @@ type HSM struct {
//
// May be nil if those keys are not present and not used by any of the called
// methods.
KG, KT, Kca []byte
KG, KT, Kca, KHsks, KLsks []byte

// The PKCS#11 session we're working with.
sessions *sessionQueue
Expand Down Expand Up @@ -169,6 +175,18 @@ func NewHSM(cfg HSMConfig) (*HSM, error) {
return nil, status.Errorf(codes.Internal, "fail to find KG key ID: %q, error: %v", cfg.KGName, err)
}
}
if cfg.KHsksName != "" {
hsm.KHsks, err = hsm.getKeyIDByLabel(session, pk11.ClassSecretKey, cfg.KHsksName)
if err != nil {
return nil, status.Errorf(codes.Internal, "fail to find KHsks key ID: %q, error: %v", cfg.KHsksName, err)
}
}
if cfg.KLsksName != "" {
hsm.KLsks, err = hsm.getKeyIDByLabel(session, pk11.ClassSecretKey, cfg.KLsksName)
if err != nil {
return nil, status.Errorf(codes.Internal, "fail to find KLsks key ID: %q, error: %v", cfg.KLsksName, err)
}
}

return hsm, nil
}
Expand Down Expand Up @@ -314,3 +332,52 @@ func (h *HSM) GenerateKeyPairAndCert(caCert *x509.Certificate, params []SigningP

return certs, nil
}

// GenerateSymmetricKey generates a symmetric key.
func (h *HSM) GenerateSymmetricKey(params []*SymmetricKeygenParams) ([]pk11.AESKey, error) {
session, release := h.sessions.getHandle()
defer release()
var symmetricKeys []pk11.AESKey

for _, p := range params {
// Select the seed asset to use (High or Low security seed).
var seed pk11.SecretKey
var err error
if p.UseHighSecuritySeed {
seed, err = session.FindSecretKey(h.KHsks)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get KHsks key object: %v", err)
}
} else {
seed, err = session.FindSecretKey(h.KLsks)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get KLsks key object: %v", err)
}
}

// Generate key from seed and extract.
seKey, err := seed.HKDFDeriveAES(crypto.SHA256, []byte(p.Sku),
[]byte(p.Diversifier), p.SizeInBits, &pk11.KeyOptions{Extractable: true})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed HKDFDeriveAES: %v", err)
}

// Extract the key from the SE.
exportedKey, err := seKey.ExportKey()
if err != nil {
return nil, status.Errorf(codes.Internal,
"failed to extract symmetric key: %v", err)
}

// Parse and format the key bytes.
keyBytes, ok := exportedKey.(pk11.AESKey)
if !ok {
return nil, status.Errorf(codes.Internal,
"failed to parse extracted symmetric key: %v", err)
}
// TODO: format it based on type (i.e. LC token or RAW)
symmetricKeys = append(symmetricKeys, keyBytes)
}

return symmetricKeys, nil
}
106 changes: 99 additions & 7 deletions src/spm/services/se_pk11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import (
"crypto/elliptic"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"io"
"log"
"math/rand"
"testing"
"time"
Expand All @@ -30,34 +33,123 @@ import (
//
// Returns the hsm, the (host-side) bytes of the KG, and the (host-side) bytes
// of the KT.
func MakeHSM(t *testing.T) (*HSM, []byte, []byte) {
func MakeHSM(t *testing.T) (*HSM, []byte, []byte, []byte, []byte) {
t.Helper()
s := ts.GetSession(t)
ts.Check(t, s.Login(pk11.NormalUser, ts.UserPin))

// Initialize HSM with KG.
global, err := s.GenerateAES(256, &pk11.KeyOptions{Extractable: true})
ts.Check(t, err)
gUID, err := global.UID()
ts.Check(t, err)
globalBytes, err := global.ExportKey()
globalKeyBytes, err := global.ExportKey()
ts.Check(t, err)

secret := []byte("this is secret data for generating keys from")
transport, err := s.ImportKeyMaterial(secret, &pk11.KeyOptions{Extractable: true})
// Initialize HSM with KT.
transportKeySeed := []byte("this is secret data for generating keys from")
transport, err := s.ImportKeyMaterial(transportKeySeed, &pk11.KeyOptions{Extractable: true})
ts.Check(t, err)
tUID, err := transport.UID()
ts.Check(t, err)

// Initialize HSM with KHsks.
hsKeySeed := []byte("high security KDF seed")
hsks, err := s.ImportKeyMaterial(hsKeySeed, &pk11.KeyOptions{Extractable: false})
ts.Check(t, err)
hsksUID, err := hsks.UID()
ts.Check(t, err)

// Initialize HSM with KLsks.
lsKeySeed := []byte("low security KDF seed")
lsks, err := s.ImportKeyMaterial(lsKeySeed, &pk11.KeyOptions{Extractable: false})
ts.Check(t, err)
lsksUID, err := lsks.UID()
ts.Check(t, err)

// Initialize session queue.
numSessions := 1
sessions := newSessionQueue(numSessions)
err = sessions.insert(s)
ts.Check(t, err)

return &HSM{KG: gUID, KT: tUID, sessions: sessions}, []byte(globalBytes.(pk11.AESKey)), secret
return &HSM{KG: gUID, KT: tUID, KHsks: hsksUID, KLsks: lsksUID, sessions: sessions},
[]byte(globalKeyBytes.(pk11.AESKey)),
transportKeySeed,
hsKeySeed,
lsKeySeed
}

func TestGenerateSymmKey(t *testing.T) {
hsm, _, _, hsKeySeed, lsKeySeed := MakeHSM(t)

// Symmetric keygen parameters.
// test unlock token
testUnlockTokenParams := SymmetricKeygenParams{
UseHighSecuritySeed: false,
KeyType: SymmetricKeyTypeRaw,
SizeInBits: 128,
Sku: "test sku",
Diversifier: "test_unlock",
}
// test exit token
testExitTokenParams := SymmetricKeygenParams{
UseHighSecuritySeed: false,
KeyType: SymmetricKeyTypeRaw,
SizeInBits: 128,
Sku: "test sku",
Diversifier: "test_exit",
}
// RMA token
rmaTokenParams := SymmetricKeygenParams{
UseHighSecuritySeed: true,
KeyType: SymmetricKeyTypeRaw,
SizeInBits: 128,
Sku: "test sku",
Diversifier: "rma: device_id",
}
// wafer authentication secret
wasParams := SymmetricKeygenParams{
UseHighSecuritySeed: true,
KeyType: SymmetricKeyTypeRaw,
SizeInBits: 256,
Sku: "test sku",
Diversifier: "was",
}
params := []*SymmetricKeygenParams{
&testUnlockTokenParams,
&testExitTokenParams,
&rmaTokenParams,
&wasParams,
}

// Generate the actual keys (using the HSM).
keys, err := hsm.GenerateSymmetricKey(params)
ts.Check(t, err)

// Check actual keys match those generated using the go crypto package.
for i, p := range params {
// Generate expected key.
var keyGenerator io.Reader
if p.UseHighSecuritySeed {
keyGenerator = hkdf.New(crypto.SHA256.New, hsKeySeed, []byte(p.Sku), []byte(p.Diversifier))
} else {
keyGenerator = hkdf.New(crypto.SHA256.New, lsKeySeed, []byte(p.Sku), []byte(p.Diversifier))
}
expected_key := make([]byte, len(keys[i]))
keyGenerator.Read(expected_key)

// Check the actual and expected keys are equal.
log.Printf("Actual Key: %q", hex.EncodeToString(keys[i]))
log.Printf("Expected Key: %q", hex.EncodeToString(expected_key))
if !bytes.Equal(keys[i], expected_key) {
t.Fatal("symmetric keygen failed")
}
}
}

func TestTransport(t *testing.T) {
hsm, kg, kt := MakeHSM(t)
hsm, kg, kt, _, _ := MakeHSM(t)

key, err := hsm.DeriveAndWrapTransportSecret([]byte("my device id"))
ts.Check(t, err)
Expand Down Expand Up @@ -85,7 +177,7 @@ func CreateCAKeys(t *testing.T, hsm *HSM) (pk11.KeyPair, error) {
}

func TestGenerateCert(t *testing.T) {
hsm, kg, _ := MakeHSM(t)
hsm, kg, _, _, _ := MakeHSM(t)

ca, err := CreateCAKeys(t, hsm)
ts.Check(t, err)
Expand Down
Loading