Skip to content

Commit a7ded1d

Browse files
committed
DummyProvider: generate valid attestation
1 parent 7ee2ec2 commit a7ded1d

2 files changed

Lines changed: 195 additions & 27 deletions

File tree

enclave/attestation_test.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import (
88
"sync"
99
"testing"
1010

11+
"github.com/0xsequence/tee-verifier/nitro"
1112
"github.com/aws/aws-sdk-go-v2/service/kms"
1213
"github.com/aws/aws-sdk-go-v2/service/kms/types"
1314
"github.com/stretchr/testify/assert"
1415
"github.com/stretchr/testify/require"
15-
"go.uber.org/mock/gomock"
1616

1717
"github.com/0xsequence/nitrocontrol/enclave"
1818
)
@@ -59,14 +59,9 @@ cJEGAbCDYhyjvtjBLNy7YDQ1hdmCnqMxg/5AIwUMkvTTRg+qepfboA==
5959
)
6060

6161
func TestNitroAttestation_Decrypt(t *testing.T) {
62-
ctrl := gomock.NewController(t)
63-
defer ctrl.Finish()
64-
6562
block, _ := pem.Decode([]byte(testPrivateKey))
6663
privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
6764
require.NoError(t, err)
68-
pkixPubKey, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey)
69-
require.NoError(t, err)
7065
ciphertextForRecipient, err := base64.StdEncoding.DecodeString(testCiphertextString)
7166
require.NoError(t, err)
7267

@@ -75,8 +70,11 @@ func TestNitroAttestation_Decrypt(t *testing.T) {
7570
assert.Equal(t, []byte("ciphertext"), params.CiphertextBlob)
7671
assert.Equal(t, types.EncryptionAlgorithmSpecSymmetricDefault, params.EncryptionAlgorithm)
7772
require.NotNil(t, params.Recipient)
78-
expectedDoc := "DEV ONLY. NOT FOR PROD USE. NONCE=nonce PUBKEY=" + string(pkixPubKey)
79-
assert.Equal(t, []byte(expectedDoc), params.Recipient.AttestationDocument)
73+
doc, err := nitro.Parse(params.Recipient.AttestationDocument)
74+
require.NoError(t, err)
75+
assert.Equal(t, []byte("nonce"), doc.Nonce)
76+
assert.NoError(t, doc.Validate(nitro.WithRootFingerprint("14e8bc5fabb52876f35f122289eaabfa08885837cc7f161149c6d242596258aa")))
77+
assert.NoError(t, doc.Verify())
8078
assert.Equal(t, types.KeyEncryptionMechanismRsaesOaepSha256, params.Recipient.KeyEncryptionAlgorithm)
8179
return &kms.DecryptOutput{CiphertextForRecipient: ciphertextForRecipient}, nil
8280
},
@@ -103,14 +101,9 @@ func TestNitroAttestation_Decrypt(t *testing.T) {
103101
}
104102

105103
func TestAttestation_GenerateDataKey(t *testing.T) {
106-
ctrl := gomock.NewController(t)
107-
defer ctrl.Finish()
108-
109104
block, _ := pem.Decode([]byte(testPrivateKey))
110105
privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
111106
require.NoError(t, err)
112-
pkixPubKey, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey)
113-
require.NoError(t, err)
114107
ciphertextForRecipient, err := base64.StdEncoding.DecodeString(testCiphertextString)
115108
require.NoError(t, err)
116109

@@ -120,8 +113,11 @@ func TestAttestation_GenerateDataKey(t *testing.T) {
120113
require.NotNil(t, params.Recipient)
121114
assert.Equal(t, "key-id", *params.KeyId)
122115
assert.Equal(t, types.DataKeySpecAes256, params.KeySpec)
123-
expectedDoc := "DEV ONLY. NOT FOR PROD USE. NONCE=nonce PUBKEY=" + string(pkixPubKey)
124-
assert.Equal(t, []byte(expectedDoc), params.Recipient.AttestationDocument)
116+
doc, err := nitro.Parse(params.Recipient.AttestationDocument)
117+
require.NoError(t, err)
118+
assert.Equal(t, []byte("nonce"), doc.Nonce)
119+
assert.NoError(t, doc.Validate(nitro.WithRootFingerprint("14e8bc5fabb52876f35f122289eaabfa08885837cc7f161149c6d242596258aa")))
120+
assert.NoError(t, doc.Verify())
125121
assert.Equal(t, types.KeyEncryptionMechanismRsaesOaepSha256, params.Recipient.KeyEncryptionAlgorithm)
126122
return &kms.GenerateDataKeyOutput{
127123
CiphertextBlob: []byte("ciphertext"),

enclave/provider_dummy.go

Lines changed: 184 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,104 @@ package enclave
22

33
import (
44
"context"
5+
"crypto"
6+
"crypto/ecdsa"
7+
"crypto/elliptic"
58
"crypto/rand"
9+
"crypto/rsa"
10+
"crypto/x509"
11+
"crypto/x509/pkix"
12+
"encoding/asn1"
13+
"encoding/pem"
614
"fmt"
715
"io"
16+
"math"
17+
"math/big"
818
"sync/atomic"
19+
"time"
920

1021
"github.com/0xsequence/nsm/request"
1122
"github.com/0xsequence/nsm/response"
23+
"github.com/fxamacker/cbor/v2"
1224
)
1325

14-
func DummyProvider(_ context.Context) (Session, error) {
15-
return &dummySession{random: rand.Reader}, nil
26+
var dummyPrivKey = `-----BEGIN RSA PRIVATE KEY-----
27+
MIIEpAIBAAKCAQEAujDWnWEKVYoHUwieLegkzR2K+4z2Fg3uVEwmZ16iRJiYm5TO
28+
ltLN6BSHaLCqreA1bYXXTFlIG10z2+h16fhkCNKzy4yKwjwUdXJlbBivypQers8h
29+
Pwy1l4c+uID/VX5zXG4y7g7aNc0Ude+lzBvydh9vFz5PwupFzY6ok3czI95ODni7
30+
hn/X/8TBGTyh0eYZu8ehfKy6W9AHbX7D+yL2qebSWWkJBEribptpCcaJi8QPUx9M
31+
HWz8j1j83+M6rnG1FQpLl8VNOO6BXmzb5FNr+6lwEfvwHbht0Azhk0ArMQZ/r0lO
32+
ObAvVDmE2AuudXyWWh5sRrXnXlVitDjTQybQAQIDAQABAoIBAQCYf9Poh0jdkvY4
33+
zkAwvYkW73GcY3JT0gk4xj5WQC6MHKgyFgm3guXfhqD54GmLjK52DD+xaxciQo5t
34+
OdMKVcYpa9qTh4NHX8oqAA6OIRIqzHLtHv3OFGzPtZhrqkx4C+AU/rV8QnH7ywNN
35+
LYIQ0XsfwNNOqFzP+u49VPFCB0m9v7r7mJxeUXp8PDfdhquFT69hpKwNdpzuIDA7
36+
kVOG4ATkkPTGp3AmJj9Vrit9ffi+xlbhrNIuBui9Fxo1v5G6VT2uBhXJU22zl1hS
37+
uYWT4rCOwVQaV/TBDj4T8diDxYpnAXvpO8U+WdqLddhUNaYeDym/HPq2cFsN9VdY
38+
9FYiVl4ZAoGBAOWVsrRAWgFTmx99nUwy6XhobSWgZDrCQiSK50VGzblBdVnmMvyW
39+
Q3LmdqtVQUkZLETx7PZXYkvIzMRP4oWGcViBPaSZ/IqX/kF5WJeXWW7Zgl5HEXTk
40+
GaN26xl7yFjQ5l0f++HAwSW485B2GXvMcdp+6n7OfG6Xo1cg8CgWck5TAoGBAM+c
41+
/h03pASGVvUDNNfeDulyxcXR/PZZTt1YMTqeYLmkbkJcIJVa2uTdDmzcEbGDA0eq
42+
ezMDA+omGB+WR7HRe9+vgmz7Ww4BZRhKjvnxRgHlTGYHBsHhYr21fgPteGv/aDi2
43+
xhAGqyOj1jua8ooqpw8TviYXk6ZbxMNF7eV9KxXbAoGAasEjKaHKuFcyCICWhfoe
44+
ifi02AwuzwvJSci1JYd43a3MbZMXHlCY6HK1t5GbG+xyo1SDRUD42hhy7s3enQwY
45+
5HikO0fHIILwnW1ZfpPH6D2H22LcgSgXq+T+CQl/7ZyloaPfsee5aFsKFqBz1RcJ
46+
0fm1/GTzg1FLiJYuVdWqLTUCgYAaOURHwH1xLN7S9+K22Y+coSimAg4nt8QkZT1i
47+
oBqrmD9tFmHvO5imi92Elo+NknTZmokROnJGIyWs57iKl2FEMdERnvYzYK26UcCZ
48+
hYZIOwRZZs3Ns4BbYg9Ww6oQSiSJ9VwzLgRz7f/ja4DzPsv3NZExEo1N2A2UdMLF
49+
1/eXPQKBgQDSCJ1tWQYVLvjrzJBC5gute7kHf1AhMoIEqpsEvk51JXu7+xN8BMnb
50+
zSwIPR3fSngqLJqGw+Tz5LT3iSsDNVj7EnaHoYvTrxsd2yFYtVmz2fHgnHXBjZmj
51+
AzDn4G6VZ+F11K/sdfuo+1vfgxPendYDkjp0ZtgJc97iBq49Devv1A==
52+
-----END RSA PRIVATE KEY-----`
53+
var dummyCert = `-----BEGIN CERTIFICATE-----
54+
MIIDITCCAgmgAwIBAgIBATANBgkqhkiG9w0BAQsFADAyMREwDwYDVQQKEwhTZXF1
55+
ZW5jZTEdMBsGA1UEAxMUZHVtbXkubml0cm8tZW5jbGF2ZXMwHhcNMjUwNDI0MTM0
56+
NjA5WhcNMzUwNDI0MTM0NjA5WjAyMREwDwYDVQQKEwhTZXF1ZW5jZTEdMBsGA1UE
57+
AxMUZHVtbXkubml0cm8tZW5jbGF2ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
58+
ggEKAoIBAQC6MNadYQpVigdTCJ4t6CTNHYr7jPYWDe5UTCZnXqJEmJiblM6W0s3o
59+
FIdosKqt4DVthddMWUgbXTPb6HXp+GQI0rPLjIrCPBR1cmVsGK/KlB6uzyE/DLWX
60+
hz64gP9VfnNcbjLuDto1zRR176XMG/J2H28XPk/C6kXNjqiTdzMj3k4OeLuGf9f/
61+
xMEZPKHR5hm7x6F8rLpb0AdtfsP7Ivap5tJZaQkESuJum2kJxomLxA9TH0wdbPyP
62+
WPzf4zqucbUVCkuXxU047oFebNvkU2v7qXAR+/AduG3QDOGTQCsxBn+vSU45sC9U
63+
OYTYC651fJZaHmxGtedeVWK0ONNDJtABAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
64+
hjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSw2hfihIyfiqyiuiuTp3OCt0Sl
65+
8DANBgkqhkiG9w0BAQsFAAOCAQEAl55+EnYlS5/YTQQhZozA/XW7Y9Kt00w9k0Ix
66+
9vXTVeZdzTNR/YKCAzG7ynNjNbdFkhJcqqwKycVOSID0Xz4dWvB6jVukIV6B3W2u
67+
ta/P4SYg4VQ9YzPqF1n1sUzX3OwKOhEcSxQQjvs8ssRaWq9aqEHyxCxuc9BWoqvB
68+
Am9iwrNpmUmlRbFwDOwtICZRbqAf799pOFo1i8WKQc/J5y1KwZCCg3GAEBv8CNQE
69+
vMVH5ygi1fMeQPNg8oWDD+3gP1GmLGMP14kHT/aPyDAHHUMrq7nSgA8SXTC9fihO
70+
sygULgtpiSjKgeg9cTvK9yhz7T0c2CxFgyhUnz4v6uZtQTJK2Q==
71+
-----END CERTIFICATE-----`
72+
73+
func DummyProvider() (Session, error) {
74+
block, _ := pem.Decode([]byte(dummyPrivKey))
75+
if block == nil || block.Type != "RSA PRIVATE KEY" {
76+
return nil, fmt.Errorf("invalid PEM block")
77+
}
78+
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
79+
if err != nil {
80+
return nil, fmt.Errorf("failed to parse dummy private key: %v", err)
81+
}
82+
83+
certBlock, _ := pem.Decode([]byte(dummyCert))
84+
caCert, err := x509.ParseCertificate(certBlock.Bytes)
85+
if err != nil {
86+
return nil, fmt.Errorf("failed to parse CA certificate: %v", err)
87+
}
88+
89+
return &dummySession{
90+
random: rand.Reader,
91+
privateKey: key,
92+
caCert: caCert,
93+
caCertDER: certBlock.Bytes,
94+
}, nil
1695
}
1796

1897
type dummySession struct {
19-
random io.Reader
20-
closed atomic.Bool
98+
privateKey *rsa.PrivateKey
99+
caCert *x509.Certificate
100+
caCertDER []byte
101+
random io.Reader
102+
closed atomic.Bool
21103
}
22104

23105
func (d *dummySession) Read(p []byte) (n int, err error) {
@@ -32,7 +114,7 @@ func (d *dummySession) Close() error {
32114
return nil
33115
}
34116

35-
func (d *dummySession) Send(req request.Request) (response.Response, error) {
117+
func (d *dummySession) Send(ctx context.Context, req request.Request) (response.Response, error) {
36118
switch req := req.(type) {
37119
case *request.Attestation:
38120
return d.handleAttestation(req)
@@ -44,14 +126,71 @@ func (d *dummySession) Send(req request.Request) (response.Response, error) {
44126
}
45127

46128
func (d *dummySession) handleAttestation(req *request.Attestation) (response.Response, error) {
47-
document := []byte("DEV ONLY. NOT FOR PROD USE.")
48-
if len(req.Nonce) > 0 {
49-
document = append(document, []byte(" NONCE=")...)
50-
document = append(document, req.Nonce...)
129+
certDER, privateKey, err := d.generateCertificate()
130+
if err != nil {
131+
return response.Response{}, fmt.Errorf("failed to generate certificate: %v", err)
132+
}
133+
134+
pcr := make([]byte, 48)
135+
rawDoc := struct {
136+
ModuleID string `cbor:"module_id"`
137+
Timestamp uint64 `cbor:"timestamp"`
138+
Digest string `cbor:"digest"`
139+
PCRs map[int][]byte `cbor:"pcrs"`
140+
Certificate []byte `cbor:"certificate"`
141+
CABundle [][]byte `cbor:"cabundle"`
142+
PublicKey []byte `cbor:"public_key"`
143+
UserData []byte `cbor:"user_data"`
144+
Nonce []byte `cbor:"nonce"`
145+
}{
146+
ModuleID: "dummy-module",
147+
Timestamp: uint64(time.Now().Unix() * 1000),
148+
Digest: "SHA384",
149+
PCRs: map[int][]byte{
150+
0: pcr,
151+
1: pcr,
152+
2: pcr,
153+
3: pcr,
154+
},
155+
Certificate: certDER,
156+
CABundle: [][]byte{d.caCertDER},
157+
PublicKey: req.PublicKey,
158+
UserData: req.UserData,
159+
Nonce: req.Nonce,
160+
}
161+
162+
rawDocBytes, err := cbor.Marshal(rawDoc)
163+
if err != nil {
164+
return response.Response{}, fmt.Errorf("failed to marshal raw document: %v", err)
165+
}
166+
167+
sigStructCBOR, err := cbor.Marshal([]any{"Signature1", []byte{}, []byte{}, rawDocBytes})
168+
if err != nil {
169+
return response.Response{}, fmt.Errorf("failed to marshal signature structure: %v", err)
170+
}
171+
hash := crypto.SHA384.New()
172+
if _, err := hash.Write(sigStructCBOR); err != nil {
173+
return response.Response{}, fmt.Errorf("failed to hash signature structure: %v", err)
51174
}
52-
if len(req.PublicKey) > 0 {
53-
document = append(document, []byte(" PUBKEY=")...)
54-
document = append(document, req.PublicKey...)
175+
digest := hash.Sum(nil)
176+
177+
derSig, err := privateKey.Sign(rand.Reader, digest, crypto.SHA384)
178+
if err != nil {
179+
return response.Response{}, fmt.Errorf("failed to sign digest: %v", err)
180+
}
181+
182+
var esig struct{ R, S *big.Int }
183+
if _, err := asn1.Unmarshal(derSig, &esig); err != nil {
184+
return response.Response{}, fmt.Errorf("failed to unmarshal ASN.1 signature: %v", err)
185+
}
186+
187+
rBytes := esig.R.FillBytes(make([]byte, 48)) // 48 bytes for P-384
188+
sBytes := esig.S.FillBytes(make([]byte, 48))
189+
sig := append(rBytes, sBytes...)
190+
191+
document, err := cbor.Marshal([]any{[]byte{}, struct{}{}, rawDocBytes, sig})
192+
if err != nil {
193+
return response.Response{}, fmt.Errorf("failed to marshal COSE_Sign1: %v", err)
55194
}
56195

57196
res := response.Response{
@@ -72,3 +211,36 @@ func (d *dummySession) handleDescribePCR(req *request.DescribePCR) (response.Res
72211
}
73212
return res, nil
74213
}
214+
215+
func (d *dummySession) generateCertificate() ([]byte, *ecdsa.PrivateKey, error) {
216+
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
217+
if err != nil {
218+
return nil, nil, fmt.Errorf("failed to generate private key: %v", err)
219+
}
220+
221+
serialNumber, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
222+
if err != nil {
223+
return nil, nil, fmt.Errorf("failed to generate serial number: %v", err)
224+
}
225+
226+
template := x509.Certificate{
227+
SerialNumber: serialNumber,
228+
Subject: pkix.Name{
229+
Organization: []string{"Sequence"},
230+
CommonName: "dummy.nitro-enclaves",
231+
},
232+
NotBefore: time.Now(),
233+
NotAfter: time.Now().Add(time.Hour * 24),
234+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment,
235+
ExtKeyUsage: []x509.ExtKeyUsage{},
236+
BasicConstraintsValid: true,
237+
IsCA: false,
238+
}
239+
240+
certBytes, err := x509.CreateCertificate(rand.Reader, &template, d.caCert, &privateKey.PublicKey, d.privateKey)
241+
if err != nil {
242+
return nil, nil, fmt.Errorf("failed to create certificate: %v", err)
243+
}
244+
245+
return certBytes, privateKey, nil
246+
}

0 commit comments

Comments
 (0)