Noauth-enclaved is a nip46 signer to be deployed inside AWS Nitro Enclave. This allows clients to cryptographically verify various claims about the code running on the server, and thus feel (more) confident that their keys won't be stolen.
From AWS:
AWS Nitro Enclaves enables customers to create isolated compute environments to further protect and securely process highly sensitive data... Nitro Enclaves uses the same Nitro Hypervisor technology that provides CPU and memory isolation for EC2 instances... Nitro Enclaves includes cryptographic attestation for your software, so that you can be sure that only authorized code is running...
The security of the AWS Nitro enclaves depends 100% on AWS - if you don't trust AWS then stop reading now and don't use this code.
Read this and this for an independent analysis of the AWS Nitro enclaves' security.
After more than 2 years in existence, there is still no reliable nip46
signer. There is a tension between self-custody and reliability of the signer: you either keep the keys on your device (which is unreliable, especially on mobile), or you upload the keys to a signer server (which usually means you are 100% trusting the server to not steal your keys). To learn more about the issue, read here.
AWS Nitro enclaves promise to solve the problem of lack of trust for a particular service. The code running inside an enclave is fully isolated by AWS from the person launching it. The attestation signed by AWS and reproducible open-source code let anyone verify what exactly is happening inside the enclave (as long as one trusts AWS VM infrastructure). For a signer server in particular, this means there's much less chance for your keys to be stolen or hacked. Add to the mix a tie to npub
s of people who built and launched the service, and you can use Nostr and WoT to discover and interact with provably safe services running inside AWS enclaves. The noauth-enclaved
is the first prototype of such service.
An app holding your nsec may discover an instance of noauth-enclaved
, may verify it's cryptographic attestation
, and may upload your nsec to the instance. Once nsec is uploaded, the server starts processing nip46
requests to your keys. Permissions of apps connected to your keys can be managed using nsec.app - they are saved on Nostr as encrypted events and are immediately discovered by the server. There is also an API (below) for more direct control over the keys.
The attestation
announced by the noauth-enclaved
as a Nostr event includes a verifiable info about the hashes
of the server-side code, an npub of the builder
- the person who built the AWS Nitro enclave image, and an npub of the launcher
- the person who launched (and is running) the built image on AWS. The service pubkey
that signs the instance
Nostr event can be used to communicate with the service in an end-to-end-encrypted way over Nostr relays. A section below for Launching the instance can serve as a draft NIP for discovery and verification of services running in AWS Nitro enclaves.
With the above data, clients can verify the validity of every instance
event, can choose between builds (re-)produced by different people and instances launched by different people using WOT. Users can also reproduce a build of noauth-enclaved
themselves to be sure which code is running in a specific instance.
The noauth-enclaved
service running inside Nitro Enclave provides:
- attestation - a document signed by AWS that specifies hashes of the code image running on the server
- isolation - AWS virtualization layer ensures that whoever launched the enclave does not have access to whatever is happening in the enclave
- reproducibility - the code of
noauth-enclaved
is open source and reproducibly built into an enclave image, allowing anyone to review it, rebuild it, and compare image hashes to ones published by any running instance - attribution - each image has a cryptographic link to
builder
npub that created the image, andlauncher
npub that is running it, allowing clients to apply WoT filters and other Nostr-enabled interactions - reliability - the end goal of deploying a signer on a server is achieved without a typical sacrifice of fully trusting a custodial black-box service
A process running inside the enclave has no access to persistent file storage, and no direct access to the network. The parent EC2 instance that launches the enclave does not have access to anything happening inside the enclave, and the only way for them to communicate is through a VSOCK
interface.
The noauth-enclaved
service consists of two processes - one called enclave
(running inside the AWS Nitro enclave), and one called parent
(running on the parent EC2 instance). The enclave
uses socks
protocol to forward all it's network requests (to Nostr relays) to the parent
over vsock
. The parent
is running a socks
proxy to forward the traffic from vsock
to the target relays. This way enclave
can receive and process nip46
requests and can accept nsecs provided by clients.
The enclave
code is reproducibly built into a docker image and then into AWS Nitro enclave image. The parent
process runs a complementary service to supply the enclave
process with build and instance metadata (builder
and launcher
info). The builder
info is kind:63795
event (build signature
) generated during the enclave image build and tied to the resulting image. The launcher
info is kind:63796
event (instance signature
) and is tied to the unique EC2 parent instance id. Both build signature
andinstance signature
events are tied to the attestation
produced by a running enclave
process, discussed next.
When the enclave
process is started, it fetches the build signature
and instance signature
events from the parent
, fetches the attestation
from the AWS VM that's running it and validates the events against the attestation
to make sure parent isn't misbehaving. The enclave
process then generates a service pubkey
and publishes a kind:63793
event (instance event
) that includes the attestation
, build signature
and instance signature
. The event is published onto launcher
and builder
outbox relays. A new instance event
is published every hour
to keep the attestation
document fresh (it's signed by AWS and is valid for 3 hours).
Clients discovering the noauth-enclaved
instances using instance events
can validate all the data included in the events - the attestation
, build signature
and instance signature
are cryptographically tied together. If the instance event
is valid, the client can be sure which specific version of the enclaved
process is behind the service pubkey
, which builder
pubkey built the image, and which launcher
pubkey is running it. Expected hashes are located in pcrs.json
and docker.json
files committed to this repo (updated whenever changes are made to the code).
If client and/or user decide to use a particular noauth-enclaved
instance, the user's nsec
can be imported into the instance by sending a nip46-like
request to the service pubkey
on a relay, announced in the instance event
. After the nsec
is imported, the instance starts processing nip46
requests for it.
A reproducible docker image for the enclave
process is produced using BuildKit - it is used under the hood by the docker buildx
, but docker doesn't expose the options necessary for reproducibility (rewrite-timestamp=true
). Run:
./build-docker.sh
The command launches the BuiltKit's buildctl
inside a docker container to produce a docker image in ./build/noauth-enclaved.tar
. The docker image metadata is saved to ./build/docker.json
, the hashes containerimage.config.digest
and containerimage.digest
must match for a reproduced image.
After docker image is built we can build the enclave image - the .eif
file. Installing the AWS Nitro build toolset (nitro-cli
) isn't trivial, so it's best to perform the build on a nitro-capable EC2 instance. Suitable instance types are listed here, the build wasn't tested on ARM so don't choose ones with g
suffix.
First, install nitro-cli
and docker
on your EC2 instance. Then build the docker image as above. Next, run:
./build-enclave-signed.sh ${YOUR_NPUB}
This will generate an enclave image at ./build/noauth-enclaved.eif
and ./build/pcrs.json
file with PCR
(platform configuration registers) hashes. These hashes will be included by the enclave VM in an attestation
document if this image is launched. Values PCR0
, PCR1
and PCR2
must be reproducible from the same docker image.
To tie ${YOUR_NPUB}
to the produced .eif
image, we will use PCR8
register. The build script will generate a single-use x509
key and will create an x509 certificate self-signed by that key at ./build/crt.pem
. The certificate
will include O=Nostr
and OU=${YOUR_NPUB}
.
The build script will ask nitro-cli
to sign the .eif
with the x509 key and provide the certificate
. The fingerprint hash of the certificate
will be included as PCR8
in ./build/pcrs.json
and will be reported in the attestation
. The temporary x509 key is then deleted to avoid leaking it.
Next, a build signature
event of kind:63795
will be generated, including tags ["cert", <certificate_base64>]
and ["PCR8", <PCR8>]
. A nip46 connection to ${YOUR_NPUB}
will be established, the event signed and written to ./build/build.json
. This event will be supplied to the enclave
process and included in the instance event
along with the attestation
, allowing clients to verify the PCR8
against the certificate
and your npub, ensuring that builder info can't be forged.
First, the parent
process should be started:
./launch-parent.sh
It will start two socat
processes forwarding vsock
ports 1080
and 2080
to the same local tcp
ports. It will also launch the parent
process that serves socks
proxy on 1080
port, and a WebSocket parent server on 2080
.
Then you can launch the enclave
:
./launch-enclave-signed.sh ${YOUR_NPUB}
This will first ask for your EC2 instance ID, convert it into PCR4
register format, create nip46 connection to ${YOUR_NPUB}
(can differ from the builder npub) and produce an instance signature
event of kind:63796
with tag ["PCR4", <PCR4>]
in a file ./instance/instance.json
. It will also copy ./build/build.json
to ./instance/build.json
. These two files will be served by the parent
process to the enclave
when it's launched.
The PCR4
value will be reported in an attestation
from withing the enclave, and since EC2 instance IDs are unique to any launched instance and never reused, this value ties the enclave
to ${YOUR_NPUB}
using instance signature
event.
Next, nitro-cli
will be used to launch a Nitro enclave using the ./build/noauth-enclave.eif
image. The enclave
process starts with ./enclave.sh
, which launches two socat
processes to forward tcp
ports 1080
and 2080
to the vsock
interface, and launches the enclave
process of the noauth-enclaved
.
The enclave
process will generate a new Nostr key to serve as service pubkey
. It will then get the attestation
from the enclave VM, asking it to include the service pubkey
into the public_key
field of the attestation
document. This way, the attestation
ensures that the service pubkey
can safely be used to talk to the enclave
process.
Next, enclave
will make a WebSocket request to 2080
to their parent
, supplying the attestation
. The parent
will verify the validity of the attestation
's PCR8
value against build signature
from ./instance/build.json
and validity of PCR4
value against ./instance/instance.json
, and then forward these events to the enclave
.
The enclave
will validate the received build signature
and instance signature
events against it's own attestation
, and then publish the instance
event of kind:63793
with tags ["build", <build-signature-event>]
, ["instance", <instance-signature-event>]
and base64-encoded attestation
as content
. The PCR
values will be included as x
tags for discovery on relays. The instance
event will be published onto the outbox relays of the builder
and launcher
.
Example instance
event:
{
"kind": 63793,
"id": "dcd7769314c4a0dac97afb05ab68cebb94fcd120f4d34355f5e0788b3acd9757",
"pubkey": "ac116b22152178636e06d75188c260da10ecd54765d3999bcb579c8f530a9441",
"created_at": 1743516970,
"tags": [
[
"r",
"https://github.com/nostrband/noauth-enclaved"
],
[
"name",
"noauth-enclaved"
],
[
"v",
"1.0.0"
],
[
"m",
"i-0ffff615a409a72d7-enc0195f17eaba9b385"
],
[
"x",
"517a9ec66c4c8e8f3b309c4a4598e2383dff4ec07dfa48617c2d7ec9b1fbf86a597b4376b18114914a31af2ea12a2db6",
"PCR0"
],
[
"x",
"4b4d5b3661b3efc12920900c80e126e4ce783c522de6c02a2a5bf7af3a2b9327b86776f188e4be1c1c404a129dbda493",
"PCR1"
],
[
"x",
"365caf856d5ef95d4ef49b1883367179fd5d40504d75d8b0c8af7672aff3bb37c0026a69c89d170bdc724b53e8423a7d",
"PCR2"
],
[
"x",
"6386cee86c94b2a713c98e1d883134e8f2c019a17a712eb950fde15e9d6667575569c4e5c5e66eb9c920369961025fd2",
"PCR4"
],
[
"x",
"7e3f4c20f65f0a62de884a41ef73fd693c136173fec4ad19336d2ce3b1d63246da3383cbb83cd10dad77d5d1aafcdce1",
"PCR8"
],
[
"t",
"dev"
],
[
"relay",
"wss://relay.nsec.app"
],
[
"expiration",
"1743527770"
],
[
"alt",
"noauth-enclaved instance"
],
[
"build",
"{\"id\":\"09826e152bcddce14a72179dc9fc648f8781cc5019de0b03dd83a059b90579ce\",\"created_at\":1743513252,\"pubkey\":\"3356de61b39647931ce8b2140b2bab837e0810c0ef515bbe92de0248040b8bdd\",\"kind\":63795,\"tags\":[[\"-\"],[\"t\",\"dev\"],[\"cert\",\"MIICZjCCAewCFHrSncuGEPMYTOF8uzoyP8fUj8C3MAoGCCqGSM49BAMDMIGWMQ4wDAYDVQQDDAVOb3N0cjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxlMQ4wDAYDVQQKDAVOb3N0cjFIMEYGA1UECww/bnB1YjF4ZHRkdWNkbmplcmV4ODhna2cycWsyYXRzZGxxc3l4cWFhZzRoMDVqbWNweXNwcXQzMHdzY21udHh5MB4XDTI1MDQwMTEzMTQwNVoXDTMwMDkyMjEzMTQwNVowgZYxDjAMBgNVBAMMBU5vc3RyMQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxDjAMBgNVBAoMBU5vc3RyMUgwRgYDVQQLDD9ucHViMXhkdGR1Y2RuamVyZXg4OGdrZzJxazJhdHNkbHFzeXhxYWFnNGgwNWptY3B5c3BxdDMwd3NjbW50eHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATk4LYEsRVWZWQvqYeyREoIaf10MMs7QSYNZC5Lpi7fTJnn/p+A20WWbgWJhsUe5BXjbvw3SU9ZCZpGt5QhY3VJV9LiHbQs9cDk7LW0S3sl0rN/9X5RRLh96hvJzg9cR90wCgYIKoZIzj0EAwMDaAAwZQIxAJ1F+wpyeV0OKb94PRvVWELfhuiZ6w40xDahGuGmcFTkXe0YPviTo0uhB07XsibGVAIwdf5SZEn181+99J3GQoAWkimMy41c51IaezIY8hpstky8SDhRhOpdvgw2cyst7ciq\"],[\"PCR8\",\"7e3f4c20f65f0a62de884a41ef73fd693c136173fec4ad19336d2ce3b1d63246da3383cbb83cd10dad77d5d1aafcdce1\"]],\"content\":\"\",\"sig\":\"17f87814835b14c3028789bfc76725a095bcceea59886dfce4dda487bb17eb76858794cdd354ae1f64be39c72ecb7c149e5093e94e7176425aa1214a556389af\"}"
],
[
"p",
"3356de61b39647931ce8b2140b2bab837e0810c0ef515bbe92de0248040b8bdd",
"builder"
],
[
"instance",
"{\"id\":\"ba52dbd7d4943068d10030f14b74dc3511698ecf53dff1190c446ffc0749d646\",\"created_at\":1743491523,\"pubkey\":\"3356de61b39647931ce8b2140b2bab837e0810c0ef515bbe92de0248040b8bdd\",\"kind\":63796,\"tags\":[[\"-\"],[\"t\",\"dev\"],[\"PCR4\",\"6386cee86c94b2a713c98e1d883134e8f2c019a17a712eb950fde15e9d6667575569c4e5c5e66eb9c920369961025fd2\"]],\"content\":\"\",\"sig\":\"bd9c2278beb7cd0023e3e8cb8794e1d29ac76e28182cb54240c8f56752f92019233463a643c606362dc996d303db532df88633e1a53532ee0f23d724df89e1dd\"}"
],
[
"p",
"3356de61b39647931ce8b2140b2bab837e0810c0ef515bbe92de0248040b8bdd",
"launcher"
]
],
"content": "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",
"sig": "391a1beafa5432c78cb176b1e1e899a1c61e80fbf97ccec94a6575d657452c10214494a0504cc81568baf7ebd1c63790c77265bcca7184ed159d676259c5c78a"
}
Matching build signature
example (from build
tag):
{
"id": "09826e152bcddce14a72179dc9fc648f8781cc5019de0b03dd83a059b90579ce",
"created_at": 1743513252,
"pubkey": "3356de61b39647931ce8b2140b2bab837e0810c0ef515bbe92de0248040b8bdd",
"kind": 63795,
"tags": [
[
"-"
],
[
"t",
"dev"
],
[
"cert",
"MIICZjCCAewCFHrSncuGEPMYTOF8uzoyP8fUj8C3MAoGCCqGSM49BAMDMIGWMQ4wDAYDVQQDDAVOb3N0cjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxlMQ4wDAYDVQQKDAVOb3N0cjFIMEYGA1UECww/bnB1YjF4ZHRkdWNkbmplcmV4ODhna2cycWsyYXRzZGxxc3l4cWFhZzRoMDVqbWNweXNwcXQzMHdzY21udHh5MB4XDTI1MDQwMTEzMTQwNVoXDTMwMDkyMjEzMTQwNVowgZYxDjAMBgNVBAMMBU5vc3RyMQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxDjAMBgNVBAoMBU5vc3RyMUgwRgYDVQQLDD9ucHViMXhkdGR1Y2RuamVyZXg4OGdrZzJxazJhdHNkbHFzeXhxYWFnNGgwNWptY3B5c3BxdDMwd3NjbW50eHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATk4LYEsRVWZWQvqYeyREoIaf10MMs7QSYNZC5Lpi7fTJnn/p+A20WWbgWJhsUe5BXjbvw3SU9ZCZpGt5QhY3VJV9LiHbQs9cDk7LW0S3sl0rN/9X5RRLh96hvJzg9cR90wCgYIKoZIzj0EAwMDaAAwZQIxAJ1F+wpyeV0OKb94PRvVWELfhuiZ6w40xDahGuGmcFTkXe0YPviTo0uhB07XsibGVAIwdf5SZEn181+99J3GQoAWkimMy41c51IaezIY8hpstky8SDhRhOpdvgw2cyst7ciq"
],
[
"PCR8",
"7e3f4c20f65f0a62de884a41ef73fd693c136173fec4ad19336d2ce3b1d63246da3383cbb83cd10dad77d5d1aafcdce1"
]
],
"content": "",
"sig": "17f87814835b14c3028789bfc76725a095bcceea59886dfce4dda487bb17eb76858794cdd354ae1f64be39c72ecb7c149e5093e94e7176425aa1214a556389af"
}
Matching instance signature
example (from instance
tag):
{
"id": "ba52dbd7d4943068d10030f14b74dc3511698ecf53dff1190c446ffc0749d646",
"created_at": 1743491523,
"pubkey": "3356de61b39647931ce8b2140b2bab837e0810c0ef515bbe92de0248040b8bdd",
"kind": 63796,
"tags": [
[
"-"
],
[
"t",
"dev"
],
[
"PCR4",
"6386cee86c94b2a713c98e1d883134e8f2c019a17a712eb950fde15e9d6667575569c4e5c5e66eb9c920369961025fd2"
]
],
"content": "",
"sig": "bd9c2278beb7cd0023e3e8cb8794e1d29ac76e28182cb54240c8f56752f92019233463a643c606362dc996d303db532df88633e1a53532ee0f23d724df89e1dd"
}
Anyone who discovers the instance
event can validate the supplied attestation
in the content
against build
and instance
tags and decide if they are willing to trust the instance. They could then communicate to the service pubkey
using a relay specified in the relay
tag. Verification code prototype is available at nostr-enclaves.
FIXME prod/dev build signature tags?
To import a key into noauth-enclaved
you can use nip46
-like protocol using kind:24135
(instead of kind:24133
). Requests must target service pubkey
and must be signed by the user key
. Methods:
method | params | result | description |
---|---|---|---|
ping | [] | "pong" | check if signer is alive |
has_key | [] | "true" OR "false" | check if caller's key is imported |
import_key | [<privkey> , <comma-separated-nip46-relays> ] |
"ok" | import caller's key |
connect_key | [<privkey> , <app_pubkey> , <comma-separated-nip46-relays> ] |
"ok" | import caller's key and connect to app_pubkey with full perms |
delete_key | [] | "ok" | delete caller's key |
generate_test_key | [<comma-separated-nip46-relays> ] |
<bunker-url> |
create new key valid for 1 day with full perms and return bunker url |
DANGER: do not use your real nsec! This is experimental tech!
First, find an instance service pubkey
:
SERVICE_PUBKEY=`echo '{"kinds":[63793], "limit": 1}' | nak req wss://relay.primal.net | jq .pubkey`
The wss://relay.primal.net
is an outbox relay of the launcher of the dev instance.
Then import your nsec:
echo ${NSEC} | tsx src/index.ts cli import_key wss://relay.nsec.app ${SERVICE_PUBKEY}
The wss://relay.nsec.app
is announced in the kind:63793
event above.
Note that the enclave
does not process nip46 connect
requests - you need to import the nsec
into nsec.app
first, then establish a connection to some client, then log-out in nsec.app
and import the key into the enclave. After that, nip46 requests sent by the client will be served by the enclave.
Another method to import a key without depending on nsec.app
is connect_key
- it imports your nsec and connects it to a client pubkey
you provide and gives full permissions to it:
echo ${NSEC} | tsx src/index.ts cli connect_key wss://relay.nsec.app ${SERVICE_PUBKEY} ${CLIENT_PUBKEY}
To delete your key call delete_key
:
echo ${NSEC} | tsx src/index.ts cli delete_key wss://relay.nsec.app ${SERVICE_PUBKEY}
To generate a test key with a reusable bunker url
and full permissions, valid for 1 day:
tsx src/index.ts cli generate_test_key wss://relay.nsec.app ${SERVICE_PUBKEY} # returns a bunker url
// FIXME
- publishing build reproduction/review event