# Generate validator keys for Ethereum with trusted code in AWS Lambda and AWS Signer

## Personas

* **Developer** - update and build code and package unsigned code to S3
* **Release Manager** - reviews, signs and deploys code

## Code Deployment

* Install [Amazon SAM CLI](https://docs.amazonaws.cn/en_us/serverless-application-model/latest/developerguide/install-sam-cli.html)
* Install [yq](https://github.com/mikefarah/yq/releases/tag/v4.30.4)
* Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) for cross-compile support

### Create S3 bucket

Release Manager creates S3 bucket to host the code

```bash
# !!!Make sure you change the region name to the one you use!!!
export AWS_DEFAULT_REGION=ap-southeast-2
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
BUCKET_NAME=secure-keygen-sam-$ACCOUNT_ID

aws s3 mb s3://$BUCKET_NAME

aws s3api put-bucket-versioning --bucket $BUCKET_NAME --versioning-configuration MFADelete=Disabled,Status=Enabled
```

### Create Signing Profile

Release Manager creates signing profile

```bash
aws signer put-signing-profile --platform-id "AWSLambda-SHA384-ECDSA" --profile-name Test1
```

### Create Developer user

Create developer user, record the credentials and create a new profile based on the credentials

```bash
cat <<EOT > s3access.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::$BUCKET_NAME/*"
      ]
    }
  ]
}
EOT


aws iam create-user --user-name secure-keygen-dev
aws iam put-user-policy --user-name secure-keygen-dev --policy-name S3Access --policy-document file://s3access.json
aws iam create-access-key --user-name secure-keygen-dev
```

### Build and package

Open a new terminal and use the developer profile created previously.

```bash
# Developer build the packages
sam build --use-container

# Upload the package to S3
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
BUCKET_NAME=secure-keygen-sam-$ACCOUNT_ID
export AWS_DEFAULT_REGION=ap-southeast-2 # !!!Make sure you change the region name to the one you use!!!
sam package --s3-bucket $BUCKET_NAME --output-template-file .aws-sam/build/template.yaml --region $AWS_DEFAULT_REGION
```

Developer should have deny access to perform code signing. Developer sends over `template.yaml` to Release Manager

### Deploy

Release manager downloads and reviews the unsigned code. Developer also passes the generated CloudFormation template file i.e. `./.aws-sam/build/template.yaml` file to Release Manager.

```bash
CODE_S3_LOCATION=$(cat .aws-sam/build/template.yaml | yq '.Resources.ValidatorKeyGenFunction.Properties.CodeUri')

mkdir -p review
aws s3 cp $CODE_S3_LOCATION ./review/source.zip
```

To review the code, unzip the file:

```bash
cd ./review
unzip source.zip
```

After review, release manager signs the code and updates the stack with the signed code

```bash
SIGNING_PROFILE_ARN=$(aws signer get-signing-profile --profile-name Test1 --query profileVersionArn --output text)

sam deploy \
 --region $AWS_DEFAULT_REGION \
 --stack-name secure-keygen \
 --template-file ./.aws-sam/build/template.yaml \
 --parameter-overrides SigningProfileVersionArnParameter=$SIGNING_PROFILE_ARN \
 --signing-profiles ValidatorKeyGenFunction=Test1 \
 --confirm-changeset \
 --capabilities CAPABILITY_IAM \
 --no-disable-rollback
```

## Generate keys

We need to invoke the Lambda function to generate the keys. Observe the [payload](events/lambdaPayload.json) structure to understand the parameters expected by the Lambda function

```bash
FUNCTION_ARN=$(aws cloudformation describe-stacks --stack-name secure-keygen --query "Stacks[0].Outputs[?OutputKey=='ValidatorKeyGenFunction'].OutputValue" --output text)

aws lambda invoke --function-name $FUNCTION_ARN --payload fileb://events/lambdaPayload.json output.json
```

Upon completion of the Lambda function execution, observe that there are new rows inserted into the DynamoDB table. You can find the DynamoDB table ARN by issuing the following command:

```bash
aws cloudformation describe-stacks --stack-name secure-keygen --query "Stacks[0].Outputs[?OutputKey=='ValidatorKeysTable'].OutputValue" --output text
```

Each DynamoDB table entry has the following field:

* **web3signer_uuid** - Used to assign the keys to Web3Signer instances. Each instance is assigned a UUID, and will query the table using this field. To assign the keys to a specific instance, modify the data in this field accordingly
* **pubkey** - The public key
* **active** - Applications such as Web3Signer can optionally use this field to determine whether to load the keys
* **chain** - Ethereum chain
* **datetime** - The date and time in which the key is generated
* **deposit_json_b64** - The deposit JSON data in base64 encoding. Decode this data and upload it to [Ethereum Staking Launchpad](https://launchpad.ethereum.org)
* **encrypted_key_password_mnemonic_b64** - KMS-encrypted JSON-formatted data structure consisting of:
  * `keystore_b64` - Encrypted BLS12-381 keystore in base64 encoding
  * `password_b64` - randomly generated password to decrypt the keystore in base64 encoding
  * `mnemonic_b64` mnemonic from which the private key is derived from in base64 encoding

## Cleanup

To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following:

```bash
aws cloudformation delete-stack --stack-name secure-keygen
```

Delete the developer user from IAM

```bash
aws iam delete-user --user-name secure-keygen-dev
```

Delete the Signer profile

```bash
PROFILE_VERSION=$(aws signer get-signing-profile --profile-name Test1 --query profileVersion --output text)
aws signer revoke-signing-profile --profile-name Test1 \
--profile-version $PROFILE_VERSION \
--reason "Unused" \
--effective-time $(date +%s)
```

Delete the deployment S3 bucket and its contents

```bash
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
BUCKET_NAME=secure-keygen-sam-$ACCOUNT_ID

aws s3api delete-objects \
      --bucket $BUCKET_NAME \
      --delete "$(aws s3api list-object-versions \
      --bucket $BUCKET_NAME | \
      jq '{Objects: [.Versions[] | {Key:.Key, VersionId : .VersionId}], Quiet: false}')"

aws s3api delete-bucket --bucket $BUCKET_NAME
```

## Development

* Install [Python 3.9](https://www.python.org/downloads/release/python-390/)
* Install [Poetry](https://python-poetry.org/docs/)

```bash
# Install dependencies
poetry install

# Activate virtual environment
source $(poetry env info --path)/bin/activate
```

If you modify the dependencies, export the `requirements.txt` file

```bash
poetry export --without dev --without-hashes > secure_keygen/requirements.txt
```