Skip to content

Commit 5c2f0bc

Browse files
committed
Add examples
1 parent 7fb654d commit 5c2f0bc

File tree

11 files changed

+512
-0
lines changed

11 files changed

+512
-0
lines changed

examples/kms/Dockerfile.enclave

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
FROM debian:bookworm-slim AS socat-builder
2+
RUN export DEBIAN_FRONTEND=noninteractive && \
3+
apt-get update && \
4+
apt-get install -y \
5+
wget make gcc
6+
RUN wget http://www.dest-unreach.org/socat/download/socat-1.7.4.4.tar.gz
7+
RUN echo "0f8f4b9d5c60b8c53d17b60d79ababc4a0f51b3bb6d2bd3ae8a6a4b9d68f195e socat-1.7.4.4.tar.gz" | sha256sum -c -
8+
RUN tar -xzf socat-1.7.4.4.tar.gz && \
9+
cd socat-1.7.4.4 && \
10+
./configure && \
11+
make && \
12+
make install
13+
14+
15+
FROM rust:1.81.0-slim-bookworm AS nsmlib-builder
16+
WORKDIR /workspace
17+
RUN export DEBIAN_FRONTEND=noninteractive && \
18+
apt-get update && \
19+
apt-get install -y git
20+
RUN git clone https://github.com/aws/aws-nitro-enclaves-nsm-api.git && \
21+
cd aws-nitro-enclaves-nsm-api && \
22+
git checkout v0.4.0 && \
23+
mkdir -p /workspace/target/lib && \
24+
cargo build --release --manifest-path Cargo.toml -p nsm-lib && \
25+
install target/release/libnsm.a /workspace/target/lib/libnsm.a
26+
27+
28+
FROM golang:1.23.1-bookworm AS example-builder
29+
WORKDIR /workspace
30+
COPY go.mod ./
31+
RUN go mod download
32+
COPY . ./
33+
COPY --from=nsmlib-builder /workspace/target/ target/
34+
RUN mkdir -p target/bin
35+
RUN go build -o target/bin/example .
36+
37+
38+
FROM debian:bookworm-slim
39+
COPY --from=socat-builder /usr/local/bin/socat /usr/local/bin/
40+
COPY --from=example-builder /workspace/target/bin/example /usr/local/bin/
41+
COPY runeif.sh /root
42+
RUN chmod +x /root/runeif.sh
43+
RUN export DEBIAN_FRONTEND=noninteractive && \
44+
apt-get update && \
45+
apt-get install -y \
46+
iproute2 \
47+
apt-get clean
48+
ENTRYPOINT [ "/root/runeif.sh" ]

examples/kms/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO

examples/kms/go.mod

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module github.com/distributed-lab/enclave-extras/examples/kms
2+
3+
go 1.23.4
4+
5+
require (
6+
github.com/aws/aws-sdk-go-v2 v1.36.3
7+
github.com/aws/aws-sdk-go-v2/config v1.29.9
8+
github.com/aws/aws-sdk-go-v2/credentials v1.17.62
9+
github.com/aws/aws-sdk-go-v2/service/iam v1.40.1
10+
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1
11+
github.com/distributed-lab/enclave-extras/attestation v0.0.0-20250304172112-7fb654ddb809
12+
github.com/distributed-lab/enclave-extras/nsm v0.0.0-20250304172112-7fb654ddb809
13+
github.com/ethereum/go-ethereum v1.15.4
14+
)
15+
16+
require (
17+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
18+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
19+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
20+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
21+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
22+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
23+
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect
24+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect
25+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect
26+
github.com/aws/smithy-go v1.22.2 // indirect
27+
github.com/holiman/uint256 v1.3.2 // indirect
28+
golang.org/x/crypto v0.35.0 // indirect
29+
)

examples/kms/go.sum

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
2+
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
3+
github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0=
4+
github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U=
5+
github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU=
6+
github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8=
7+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
8+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
9+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
10+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
11+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
12+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
13+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
14+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
15+
github.com/aws/aws-sdk-go-v2/service/iam v1.40.1 h1:PaHCkW8rtLrA89xM/0LsY/NSIQETqmN+f1vt70EmpB8=
16+
github.com/aws/aws-sdk-go-v2/service/iam v1.40.1/go.mod h1:mPJkGQzeCoPs82ElNILor2JzZgYENr4UaSKUT8K27+c=
17+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
18+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
19+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
20+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
21+
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 h1:tecq7+mAav5byF+Mr+iONJnCBf4B4gon8RSp4BrweSc=
22+
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
23+
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0=
24+
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
25+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA=
26+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
27+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
28+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
29+
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
30+
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
31+
github.com/distributed-lab/enclave-extras/attestation v0.0.0-20250304172112-7fb654ddb809 h1:wDjryjLEKn9WtZ7CiQMbTIPEUE7m+jyTKlSIztl9PHE=
32+
github.com/distributed-lab/enclave-extras/attestation v0.0.0-20250304172112-7fb654ddb809/go.mod h1:KZDHZ4yaV8pPbwxph1js4TL1/U9pdiyaqNirRRl2tR4=
33+
github.com/distributed-lab/enclave-extras/nsm v0.0.0-20250304172112-7fb654ddb809 h1:yAGCcKd6hPxfK/PJSNy1m5I/MES1KVplswH9FOY01S8=
34+
github.com/distributed-lab/enclave-extras/nsm v0.0.0-20250304172112-7fb654ddb809/go.mod h1:B6Wi/qX+DTjfmRjelkyn0OoeDUu7fcPSH9teOIfsMbw=
35+
github.com/ethereum/go-ethereum v1.15.4 h1:a0P+AalZaosp97rfKoYXHYWzyK3+jXWZrciM9S7XFrI=
36+
github.com/ethereum/go-ethereum v1.15.4/go.mod h1:1LG2LnMOx2yPRHR/S+xuipXH29vPr6BIH6GElD8N/fo=
37+
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
38+
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
39+
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
40+
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=

examples/kms/main.go

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"crypto/rand"
6+
"crypto/rsa"
7+
"crypto/x509"
8+
"encoding/hex"
9+
"encoding/json"
10+
"fmt"
11+
"time"
12+
13+
"github.com/distributed-lab/enclave-extras/attestation/kmshelpers"
14+
"github.com/distributed-lab/enclave-extras/nsm"
15+
16+
"github.com/aws/aws-sdk-go-v2/aws"
17+
"github.com/aws/aws-sdk-go-v2/aws/arn"
18+
"github.com/aws/aws-sdk-go-v2/config"
19+
"github.com/aws/aws-sdk-go-v2/credentials"
20+
"github.com/aws/aws-sdk-go-v2/service/iam"
21+
"github.com/aws/aws-sdk-go-v2/service/kms"
22+
kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
23+
"github.com/ethereum/go-ethereum/common/hexutil"
24+
"github.com/ethereum/go-ethereum/log"
25+
)
26+
27+
const (
28+
region = "you_region"
29+
accessKey = "iam_access_key"
30+
secretKey = "iam_secret_key"
31+
)
32+
33+
func main() {
34+
time.Sleep(15 * time.Second)
35+
36+
awsConfig, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(region), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")))
37+
if err != nil {
38+
panic(fmt.Errorf("failed to load default aws config: %w", err))
39+
}
40+
41+
fmt.Println("All pcrs values: should be zeros at start")
42+
for i := 0; i < 32; i++ {
43+
_, pcrData, err := nsm.DescribePCR(i)
44+
if err != nil {
45+
panic(fmt.Errorf("failed to get pcr0 data: %w", err))
46+
}
47+
fmt.Printf("PCR%d data: %s\n", i, hexutil.Encode(pcrData))
48+
}
49+
50+
_, pcr0Data, err := nsm.DescribePCR(0)
51+
if err != nil {
52+
panic(fmt.Errorf("failed to get pcr0 data: %w", err))
53+
}
54+
55+
keyID, err := CreateKMSEnclaveKey(awsConfig, map[string]string{
56+
"kms:RecipientAttestation:PCR0": hex.EncodeToString(pcr0Data),
57+
})
58+
if err != nil {
59+
panic(fmt.Errorf("failed to create kms key: %w", err))
60+
}
61+
fmt.Printf("KeyID: %s\n", keyID)
62+
63+
rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096)
64+
if err != nil {
65+
panic(fmt.Errorf("failed to generate rsa 4096 private key: %w", err))
66+
}
67+
fmt.Printf("PrivateKey: %s\n", hexutil.Encode(x509.MarshalPKCS1PrivateKey(rsaPrivateKey)))
68+
fmt.Printf("PublicKey: %s\n", hexutil.Encode(x509.MarshalPKCS1PublicKey(&rsaPrivateKey.PublicKey)))
69+
70+
derEncodedPublicKey, err := x509.MarshalPKIXPublicKey(&rsaPrivateKey.PublicKey)
71+
if err != nil {
72+
panic(fmt.Errorf("faield to encode public key in DER format: %w", err))
73+
}
74+
fmt.Printf("DEREncodedPublicKey: %s\n", hexutil.Encode(derEncodedPublicKey))
75+
76+
attestationDoc, err := nsm.GetAttestationDoc(nil, nil, derEncodedPublicKey)
77+
if err != nil {
78+
panic(fmt.Errorf("faield to get attestatiokn doc: %w", err))
79+
}
80+
fmt.Printf("AttestationDoc: %s\n", hexutil.Encode(attestationDoc))
81+
82+
awsKMSEnclaveClient := kmshelpers.NewFromConfig(awsConfig)
83+
84+
encryptResp, err := awsKMSEnclaveClient.Encrypt(context.TODO(), &kms.EncryptInput{
85+
KeyId: aws.String(keyID),
86+
Plaintext: []byte("Hello world"),
87+
})
88+
if err != nil {
89+
panic(fmt.Errorf("failed to encrypt plaintext: %w", err))
90+
}
91+
fmt.Printf("Ciphertext: %s\n", hexutil.Encode(encryptResp.CiphertextBlob))
92+
93+
decryptResp, err := awsKMSEnclaveClient.Decrypt(context.TODO(), &kms.DecryptInput{
94+
KeyId: aws.String(keyID),
95+
CiphertextBlob: encryptResp.CiphertextBlob,
96+
Recipient: &kmstypes.RecipientInfo{
97+
AttestationDocument: attestationDoc,
98+
KeyEncryptionAlgorithm: kmstypes.KeyEncryptionMechanismRsaesOaepSha256,
99+
},
100+
}, rsaPrivateKey)
101+
if err != nil {
102+
panic(fmt.Errorf("failed to decrypt ciphertext on KMS: %w", err))
103+
}
104+
fmt.Printf("Plaintext(decrypt): %s\n", string(decryptResp.Plaintext))
105+
106+
generateDataKeyResp, err := awsKMSEnclaveClient.GenerateDataKey(context.TODO(), &kms.GenerateDataKeyInput{
107+
KeyId: aws.String(keyID),
108+
KeySpec: kmstypes.DataKeySpecAes256,
109+
Recipient: &kmstypes.RecipientInfo{
110+
AttestationDocument: attestationDoc,
111+
KeyEncryptionAlgorithm: kmstypes.KeyEncryptionMechanismRsaesOaepSha256,
112+
},
113+
}, rsaPrivateKey)
114+
if err != nil {
115+
panic(fmt.Errorf("failed to generate data key on KMS: %w", err))
116+
}
117+
fmt.Printf("Plaintext(generate-data-key): %s\n", hexutil.Encode(generateDataKeyResp.Plaintext))
118+
119+
generateDataKeyPairResp, err := awsKMSEnclaveClient.GenerateDataKeyPair(context.TODO(), &kms.GenerateDataKeyPairInput{
120+
KeyId: aws.String(keyID),
121+
KeyPairSpec: kmstypes.DataKeyPairSpecEccNistP521,
122+
Recipient: &kmstypes.RecipientInfo{
123+
AttestationDocument: attestationDoc,
124+
KeyEncryptionAlgorithm: kmstypes.KeyEncryptionMechanismRsaesOaepSha256,
125+
},
126+
}, rsaPrivateKey)
127+
if err != nil {
128+
panic(fmt.Errorf("failed to generate data key pair on KMS: %w", err))
129+
}
130+
fmt.Printf("PrivateKeyPlaintext(generate-data-key-pair): %s\n", hexutil.Encode(generateDataKeyPairResp.PrivateKeyPlaintext))
131+
fmt.Printf("PublicKey: %s\n", hexutil.Encode(generateDataKeyPairResp.PublicKey))
132+
133+
generateRandomResp, err := awsKMSEnclaveClient.GenerateRandom(context.TODO(), &kms.GenerateRandomInput{
134+
NumberOfBytes: aws.Int32(128),
135+
Recipient: &kmstypes.RecipientInfo{
136+
AttestationDocument: attestationDoc,
137+
KeyEncryptionAlgorithm: kmstypes.KeyEncryptionMechanismRsaesOaepSha256,
138+
},
139+
}, rsaPrivateKey)
140+
if err != nil {
141+
panic(fmt.Errorf("failed to genearate random on KMS: %w", err))
142+
}
143+
fmt.Printf("Plaintext(generate-random): %s\n", hexutil.Encode(generateRandomResp.Plaintext))
144+
}
145+
146+
func CreateKMSEnclaveKey(cfg aws.Config, pcrs map[string]string) (string, error) {
147+
callerARN, err := getCallerARN(cfg)
148+
if err != nil {
149+
return "", fmt.Errorf("failed to get arn of active user: %w", err)
150+
}
151+
152+
log.Debug("Create KMS Restricted key: Caller ARN", callerARN)
153+
154+
rootARN, err := arn.Parse(callerARN)
155+
if err != nil {
156+
return "", fmt.Errorf("failed to parse arn: %w", err)
157+
}
158+
rootARN.Resource = "root"
159+
160+
log.Debug("Create KMS Restricted key: Root ARN", rootARN.String())
161+
162+
keyPolicy := map[string]interface{}{
163+
"Version": "2012-10-17",
164+
"Id": "key-default-1",
165+
"Statement": []map[string]interface{}{
166+
{
167+
"Sid": "Allow access for Key Administrators",
168+
"Effect": "Allow",
169+
"Principal": map[string]interface{}{
170+
"AWS": rootARN.String(),
171+
},
172+
"Action": []string{
173+
"kms:CancelKeyDeletion",
174+
"kms:DescribeKey",
175+
"kms:DisableKey",
176+
"kms:EnableKey",
177+
"kms:GetKeyPolicy",
178+
"kms:ScheduleKeyDeletion",
179+
},
180+
"Resource": "*",
181+
},
182+
// should be removed if you want to
183+
// trust private key generated in enclave
184+
{
185+
"Sid": "Enable encrypt from instance",
186+
"Effect": "Allow",
187+
"Principal": map[string]interface{}{
188+
"AWS": callerARN,
189+
},
190+
"Action": "kms:Encrypt",
191+
"Resource": "*",
192+
},
193+
{
194+
"Sid": "Enable decrypt from enclave",
195+
"Effect": "Allow",
196+
"Principal": map[string]interface{}{
197+
"AWS": callerARN,
198+
},
199+
"Action": []string{
200+
"kms:Decrypt",
201+
"kms:GenerateRandom",
202+
"kms:GenerateDataKey",
203+
"kms:GenerateDataKeyPair",
204+
},
205+
"Resource": "*",
206+
"Condition": map[string]interface{}{
207+
"StringEqualsIgnoreCase": pcrs,
208+
},
209+
},
210+
},
211+
}
212+
213+
policyBytes, err := json.Marshal(keyPolicy)
214+
if err != nil {
215+
return "", fmt.Errorf("failed to marshal policy: %w", err)
216+
}
217+
218+
log.Debug("Create KMS Restricted key: Key policy", string(policyBytes))
219+
220+
awsKMSClient := kms.NewFromConfig(cfg)
221+
resp, err := awsKMSClient.CreateKey(context.TODO(), &kms.CreateKeyInput{
222+
// DANGER: The key may become unmanageable
223+
BypassPolicyLockoutSafetyCheck: true,
224+
Description: aws.String("Nitro Enclave Key"),
225+
Policy: aws.String(string(policyBytes)),
226+
})
227+
if err != nil {
228+
return "", fmt.Errorf("failed to create kms key")
229+
}
230+
231+
// Should never be nil
232+
return *resp.KeyMetadata.KeyId, nil
233+
}
234+
235+
func getCallerARN(cfg aws.Config) (string, error) {
236+
awsIAMClient := iam.NewFromConfig(cfg)
237+
resp, err := awsIAMClient.GetUser(context.TODO(), &iam.GetUserInput{})
238+
if err != nil {
239+
return "", err
240+
}
241+
242+
// Should never be nil
243+
return *resp.User.Arn, nil
244+
}

0 commit comments

Comments
 (0)