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

[kmstool-enclave-cli] Add GenerateRandom command and smoke test scripts #132

Merged
merged 6 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ if (NOT WIN32)

if (NOT CMAKE_CROSSCOMPILING)
if (BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(tests/kmstool-enclaves)
endif()
endif()

Expand Down
176 changes: 111 additions & 65 deletions bin/kmstool-enclave-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,96 +37,142 @@ By doing that, this tool can be used by any programming langauge that can intera

1. Use any subprocess method from your chosen programming language to interact with `kmstool-enclave-cli`

The **`decrypt`** call takes the following parameters:
1. `decrypt` command
1. The **`decrypt`** call takes the following parameters:
1. `decrypt` command

2. `--region` AWS region to use for KMS
2. `--region` AWS region to use for KMS

3. `--proxy-port` Connect to KMS proxy on PORT. Default: 8000
3. `--proxy-port` Connect to KMS proxy on PORT. Default: 8000

4. `--aws-access-key-id` AWS access key ID
4. `--aws-access-key-id` AWS access key ID

5. `--aws-secret-access-key` AWS secret access key
5. `--aws-secret-access-key` AWS secret access key

6. `--aws-session-token` Session token associated with the access key ID
6. `--aws-session-token` Session token associated with the access key ID

7. `--ciphertext` Base64-encoded ciphertext that need to decrypt
7. `--ciphertext` Base64-encoded ciphertext that need to decrypt

8. `--key-id KEY_ID` decrypt key id (for symmetric keys, is optional)
8. `--key-id KEY_ID` decrypt key id (for symmetric keys, is optional)

9. `--encryption-algorithm` encryption algorithm for ciphertext (required if `--key-id` has been set)
9. `--encryption-algorithm` encryption algorithm for ciphertext (required if `--key-id` has been set)


and outputs the base64-encoded plaintext with `PLAINTEXT: ` as prefix if the execution succeeds.
and outputs the base64-encoded plaintext with `PLAINTEXT: ` as prefix if the execution succeeds.

```shell
PLAINTEXT: <base64-encoded plaintext>
```
```shell
PLAINTEXT: <base64-encoded plaintext>
```

Below is an example for Python using `subprocess`
Below is an example for Python using `subprocess`

```python
proc = subprocess.Popen(
[
"/kmstool_enclave_cli",
"decrypt",
"--region", "us-east-1",
"--proxy-port", "8000",
"--aws-access-key-id", access_key_id,
"--aws-secret-access-key", secret_access_key,
"--aws-session-token", token,
"--ciphertext", ciphertext,
],
stdout=subprocess.PIPE
)

result = proc.communicate()[0].decode()
plaintext_b64 = result.split(":")[1].strip()
```

```
proc = subprocess.Popen(
[
"/kmstool_enclave_cli",
"decrypt",
"--region", "us-east-1",
"--proxy-port", "8000",
"--aws-access-key-id", access_key_id,
"--aws-secret-access-key", secret_access_key,
"--aws-session-token", token,
"--ciphertext", ciphertext,
],
stdout=subprocess.PIPE
)

result_b64 = proc.communicate()[0].decode()
plaintext_b64 = result.split(":")[1].strip()
```
1. The **`genkey`** call takes the following parameters:
1. `genkey` command

The **`genkey`** call takes the following parameters:
1. `genkey` command
2. `--region` AWS region to use for KMS

2. `--region` AWS region to use for KMS
3. `--proxy-port` Connect to KMS proxy on PORT. Default: 8000

3. `--proxy-port` Connect to KMS proxy on PORT. Default: 8000
4. `--aws-access-key-id` AWS access key ID

4. `--aws-access-key-id` AWS access key ID
5. `--aws-secret-access-key` AWS secret access key

5. `--aws-secret-access-key` AWS secret access key
6. `--aws-session-token` Session token associated with the access key ID

6. `--aws-session-token` Session token associated with the access key ID
7. `--key-id` KMS key ID to be used

7. `--key-id` KMS key ID to be used
8. `--key-spec` The key spec used to create the key (AES-256 or AES-128)

8. `--key-spec` The key spec used to create the key (AES-256 or AES-128)
and outputs the base64-encoded encrypted datakey with `CIPHERTEXT: ` as prefix, and base64-encoded plaintext datakey with `PLAINTEXT: ` as prefix if the execution succeeds.

and outputs the base64-encoded datakey with `CIPHERTEXT: ` as prefix if the execution succeeds.
```shell
CIPHERTEXT: <base64-encoded encrypted datakey>
PLAINTEXT: <base64-encoded plaintext datakey>
```

```shell
CIPHERTEXT: <base64-encoded datakey>
```
Below is an example for Python using `subprocess`

```python
proc = subprocess.Popen(
[
"/kmstool_enclave_cli",
"genkey",
"--region", "us-east-1",
"--proxy-port", "8000",
"--aws-access-key-id", access_key_id,
"--aws-secret-access-key", secret_access_key,
"--aws-session-token", token,
"--key-id", key_id,
"--key-spec", key_spec,
],
stdout=subprocess.PIPE
)

result = proc.communicate()[0].decode()

ciphertext_b64 = result.split("\n")[0].split(":")[1].strip()
plaintext_b64 = result.split("\n")[1].split(":")[1].strip()
```

Below is an example for Python using `subprocess`
1. The **`genrandom`** call takes the following parameters:
1. `genrandom` command

```
proc = subprocess.Popen(
[
"/kmstool_enclave_cli",
"genkey",
"--region", "us-east-1",
"--proxy-port", "8000",
"--aws-access-key-id", access_key_id,
"--aws-secret-access-key", secret_access_key,
"--aws-session-token", token,
"--ciphertext", ciphertext,
],
stdout=subprocess.PIPE
)

result_b64 = proc.communicate()[0].decode()
plaintext_b64 = result_b64.split(":")[1].strip()
```
2. `--region` AWS region to use for KMS

3. `--proxy-port` Connect to KMS proxy on PORT. Default: 8000

4. `--aws-access-key-id` AWS access key ID

5. `--aws-secret-access-key` AWS secret access key

6. `--aws-session-token` Session token associated with the access key ID

7. `--length` The length of the random byte string (in bytes)

and outputs the base64-encoded random bytes with `PLAINTEXT: ` as prefix if the execution succeeds.

```shell
PLAINTEXT: <base64-encoded random bytes>
```

Below is an example for Python using `subprocess`

```python
proc = subprocess.Popen(
[
"/kmstool_enclave_cli",
"genrandom",
"--region", "us-east-1",
"--proxy-port", "8000",
"--aws-access-key-id", access_key_id,
"--aws-secret-access-key", secret_access_key,
"--aws-session-token", token,
"--length", length,
],
stdout=subprocess.PIPE
)

result = proc.communicate()[0].decode()
plaintext_b64 = result.split(":")[1].strip()
```

## Troubleshooting

Expand Down
95 changes: 93 additions & 2 deletions bin/kmstool-enclave-cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@

#define DECRYPT_CMD "decrypt"
#define GENKEY_CMD "genkey"
#define GENRANDOM_CMD "genrandom"

#define AES_256_ARG "AES-256"
#define AES_128_ARG "AES-128"

#define MAX_SUB_COMMAND_LENGTH sizeof(DECRYPT_CMD)
#define MAX_SUB_COMMAND_LENGTH sizeof(GENRANDOM_CMD)
#define MAX_KEY_SPEC_LENGTH sizeof(AES_256_ARG)

enum status {
Expand Down Expand Up @@ -59,6 +60,9 @@ struct app_ctx {
const struct aws_string *encryption_algorithm;
const struct aws_string *key_id;
enum aws_key_spec key_spec;

/* GenRandom parameters */
uint32_t length;
};

/*
Expand All @@ -69,6 +73,7 @@ static void print_commands(int exit_code) {
fprintf(stderr, "\n Commands: \n\n");
fprintf(stderr, " decrypt: Decrypt a given ciphertext blob.\n");
fprintf(stderr, " genkey: Generate a datakey from KMS encrypted with the given key id.\n");
fprintf(stderr, " genrandom: Generate a random byte string from KMS.\n");
exit(exit_code);
}

Expand Down Expand Up @@ -107,6 +112,22 @@ static void s_usage_genkey(int exit_code) {
exit(exit_code);
}

/*
* Function to print out the arguments for genrandom
*/
static void s_usage_genrandom(int exit_code) {
fprintf(stderr, "usage: kmstool_enclave_cli genrandom [options]\n");
fprintf(stderr, "\n Options: \n\n");
fprintf(stderr, " --help: Displays this message and exits\n");
fprintf(stderr, " --region REGION: AWS region to use for KMS. Default: 'us-east-1'\n");
fprintf(stderr, " --proxy-port PORT: Connect to KMS proxy on PORT. Default: 8000\n");
fprintf(stderr, " --aws-access-key-id ACCESS_KEY_ID: AWS access key ID\n");
fprintf(stderr, " --aws-secret-access-key SECRET_ACCESS_KEY: AWS secret access key\n");
fprintf(stderr, " --aws-session-token SESSION_TOKEN: Session token associated with the access key ID\n");
fprintf(stderr, " --length NO_OF_BYTES: The length of the random byte string\n");
exit(exit_code);
}

/* Command line options */
static struct aws_cli_option s_long_options[] = {
{"region", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'r'},
Expand All @@ -118,6 +139,7 @@ static struct aws_cli_option s_long_options[] = {
{"key-id", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'K'},
{"key-spec", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'p'},
{"encryption-algorithm", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'a'},
{"length", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'l'},
{"help", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'h'},
{NULL, 0, NULL, 0},
};
Expand All @@ -140,12 +162,13 @@ static void s_parse_options(int argc, char **argv, const char *subcommand, struc
ctx->key_id = NULL;
ctx->key_spec = -1;
ctx->encryption_algorithm = NULL;
ctx->length = -1;

aws_cli_optind = 2;
while (true) {
int option_index = 0;

int c = aws_cli_getopt_long(argc, argv, "r:x:k:s:t:c:K:p:a:h", s_long_options, &option_index);
int c = aws_cli_getopt_long(argc, argv, "r:x:k:s:t:c:K:p:a:l:h", s_long_options, &option_index);
if (c == -1) {
break;
}
Expand Down Expand Up @@ -173,6 +196,8 @@ static void s_parse_options(int argc, char **argv, const char *subcommand, struc
s_usage_decrypt(1);
else if (strncmp(subcommand, GENKEY_CMD, MAX_SUB_COMMAND_LENGTH) == 0)
s_usage_genkey(1);
else if (strncmp(subcommand, GENRANDOM_CMD, MAX_SUB_COMMAND_LENGTH) == 0)
s_usage_genrandom(1);
break;
default:
if (strncmp(subcommand, DECRYPT_CMD, MAX_SUB_COMMAND_LENGTH) == 0) {
Expand Down Expand Up @@ -206,6 +231,12 @@ static void s_parse_options(int argc, char **argv, const char *subcommand, struc
}
break;
}
} else if (strncmp(subcommand, GENRANDOM_CMD, MAX_SUB_COMMAND_LENGTH) == 0) {
switch(c) {
case 'l':
ctx->length = atoi(aws_cli_optarg);
break;
}
}
}
}
Expand Down Expand Up @@ -252,6 +283,24 @@ static void s_parse_options(int argc, char **argv, const char *subcommand, struc
fprintf(stderr, "--key-spec must be set\n");
exit(1);
}
} else if (strncmp(subcommand, GENRANDOM_CMD, MAX_SUB_COMMAND_LENGTH) == 0) {
/* Check if the length is set */
if (ctx->length == -1) {
fprintf(stderr, "--length must be set\n");
exit(1);
}

/* Check if the length greater than 0 (KMS limit) */
if (ctx->length <= 0) {
fprintf(stderr, "--length must be greater than 0\n");
exit(1);
}

/* Check if the length smaller or equal to 1024 (KMS limit) */
if (ctx->length > 1024) {
fprintf(stderr, "--length must be smaller or equal to 1024\n");
exit(1);
}
}
}

Expand Down Expand Up @@ -390,6 +439,36 @@ static int gen_datakey(struct app_ctx *app_ctx, struct aws_byte_buf *ciphertext_
return AWS_OP_SUCCESS;
}

/*
* Function to generate random bytes from KMS with attestation.
*
* @param[in] app_ctx: Struct that has all of the necessary arguments
* @param[out] plaintext_b64: Byte buffer where the plaintext random bytes output will be stored
*/
static int gen_random(struct app_ctx *app_ctx, struct aws_byte_buf *plaintext_b64) {
ssize_t rc = 0;

struct aws_credentials *credentials = NULL;
struct aws_nitro_enclaves_kms_client *client = NULL;

init_kms_client(app_ctx, &credentials, &client);

/* Generate random bytes with KMS. */
struct aws_byte_buf plaintext;
rc = aws_kms_generate_random_blocking(client, app_ctx->length, &plaintext);
fail_on(rc != AWS_OP_SUCCESS, "Could not generate random bytes");

/* Encode random bytes into base64 for printing out the result. */
rc = encode_b64(app_ctx, &plaintext, plaintext_b64);
fail_on(rc != AWS_OP_SUCCESS, "Could not encode random bytes");

/* Cleaning up allocated memory. */
aws_nitro_enclaves_kms_client_destroy(client);
aws_credentials_release(credentials);

return AWS_OP_SUCCESS;
}

int main(int argc, char **argv) {
struct app_ctx app_ctx;
int rc;
Expand Down Expand Up @@ -449,6 +528,18 @@ int main(int argc, char **argv) {
fprintf(stdout, "PLAINTEXT: %s\n", (const char *)plaintext_b64.buffer);

aws_byte_buf_clean_up(&ciphertext_b64);
aws_byte_buf_clean_up(&plaintext_b64);
} else if (strncmp(subcommand, GENRANDOM_CMD, MAX_SUB_COMMAND_LENGTH) == 0) {
struct aws_byte_buf plaintext_b64;

rc = gen_random(&app_ctx, &plaintext_b64);

/* Error if random bytes wasn't generated */
fail_on(rc != AWS_OP_SUCCESS, "Could not generate random bytes\n");

/* Print the base64-encoded ciphertext and plaintext to stdout */
fprintf(stdout, "PLAINTEXT: %s\n", (const char *)plaintext_b64.buffer);

aws_byte_buf_clean_up(&plaintext_b64);
} else {
print_commands(1);
Expand Down
Loading