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

pkcs11-tool: add support for AES/GCM encryption and decryption #2927

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions doc/tools/pkcs11-tool.1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,22 @@
</para></listitem>
</varlistentry>

<varlistentry>
<term>
<option>--aad-file</option> <replaceable>filename</replaceable>
</term>
<listitem><para>Optional file containing additional authenticated data
for AEAD methods.</para></listitem>
</varlistentry>

<varlistentry>
<term>
<option>--tag-len</option> <replaceable>bytes</replaceable>
</term>
<listitem><para>Length of the authentication tag appended to the end
of the ciphertext when using AEAD methods.</para></listitem>
</varlistentry>

</variablelist>
</para>
</refsect1>
Expand Down
147 changes: 141 additions & 6 deletions src/tools/pkcs11-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,9 @@ enum {
OPT_OBJECT_INDEX,
OPT_ALLOW_SW,
OPT_LIST_INTERFACES,
OPT_IV
OPT_IV,
OPT_AAD_FILE,
OPT_TAG_LEN
};

static const struct option options[] = {
Expand Down Expand Up @@ -296,7 +298,9 @@ static const struct option options[] = {
#endif
{ "generate-random", 1, NULL, OPT_GENERATE_RANDOM },
{ "allow-sw", 0, NULL, OPT_ALLOW_SW },
{ "iv", 1, NULL, OPT_IV },
{ "iv", 1, NULL, OPT_IV },
{ "aad-file", 1, NULL, OPT_AAD_FILE },
{ "tag-len", 1, NULL, OPT_TAG_LEN },

{ NULL, 0, NULL, 0 }
};
Expand Down Expand Up @@ -385,6 +389,8 @@ static const char *option_help[] = {
"Generate given amount of random data",
"Allow using software mechanisms (without CKF_HW)",
"Initialization vector",
"Additional authenticated data for AEAD methods",
"Authentication tag length in bytes (default: 16)",
};

static const char * app_name = "pkcs11-tool"; /* for utils.c */
Expand Down Expand Up @@ -444,6 +450,8 @@ static int opt_salt_len_given = 0; /* 0 - not given, 1 - given with input param
static int opt_always_auth = 0;
static CK_FLAGS opt_allow_sw = CKF_HW;
static const char * opt_iv = NULL;
static const char * opt_aad_file = NULL;
static int opt_tag_len = 16;

static void *module = NULL;
static CK_FUNCTION_LIST_3_0_PTR p11 = NULL;
Expand Down Expand Up @@ -597,6 +605,7 @@ static const char * CKR2Str(CK_ULONG res);
static int p11_test(CK_SESSION_HANDLE session);
static int test_card_detection(int);
static CK_BYTE_PTR get_iv(const char * iv_input, size_t *iv_size);
static CK_BYTE_PTR get_aad(const char *aad_file_input, size_t *aad_size);
static void pseudo_randomize(unsigned char *data, size_t dataLen);
static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE session);
static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session);
Expand Down Expand Up @@ -1146,6 +1155,12 @@ int main(int argc, char * argv[])
case OPT_IV:
opt_iv = optarg;
break;
case OPT_AAD_FILE:
opt_aad_file = optarg;
break;
case OPT_TAG_LEN:
opt_tag_len = strtoul(optarg, NULL, 0);
break;
default:
util_print_usage_and_die(app_name, options, option_help, NULL);
}
Expand Down Expand Up @@ -2505,13 +2520,23 @@ static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
{
unsigned char in_buffer[1024], out_buffer[1024];
CK_MECHANISM mech;
CK_GCM_PARAMS gcm_params;
CK_RV rv;
CK_RSA_PKCS_OAEP_PARAMS oaep_params;
CK_ULONG in_len, out_len;
int fd_in, fd_out;
int r;
CK_BYTE_PTR iv = NULL;
size_t iv_size = 0;
CK_BYTE_PTR aad = NULL;
size_t aad_size = 0;

// AEAD ciphers such as AES-GCM don't return decrypted data until the entire
// ciphertext is decrypted, so we load the entire ciphertext into a buffer
// and decrypt it all at once.
int use_aead_buffer = FALSE;
unsigned char* aead_in_buffer = NULL;
unsigned char* aead_out_buffer = NULL;

if (!opt_mechanism_used)
if (!find_mechanism(slot, CKF_DECRYPT|opt_allow_sw, NULL, 0, &opt_mechanism))
Expand Down Expand Up @@ -2577,6 +2602,24 @@ static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
mech.pParameter = iv;
mech.ulParameterLen = iv_size;
break;
case CKM_AES_GCM:
use_aead_buffer = TRUE;
iv_size = 16;
iv = get_iv(opt_iv, &iv_size);
gcm_params.pIv = iv;
gcm_params.ulIvLen = iv_size;
gcm_params.ulIvBits = iv_size * 8; // no one seems to know what this is for
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The section 2.13.5 AES GCM and CCM Mechanism parameters says:

ulIvBits length of initialization vector in bits. Do no use ulIvBits to specify the length of the initialization vector, but ulIvLen instead.

so I would drop this.

https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061261


aad_size = 0;
aad = get_aad(opt_aad_file, &aad_size);
gcm_params.pAAD = aad;
gcm_params.ulAADLen = aad_size;

gcm_params.ulTagBits = opt_tag_len * 8;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would sanity-check that the value is in the bounds required by the specs. From the above linked section, it should be between 0 and 128


mech.pParameter = &gcm_params;
mech.ulParameterLen = sizeof(gcm_params);
break;
default:
util_fatal("Mechanism %s illegal or not supported\n", p11_mechanism_to_name(opt_mechanism));
}
Expand Down Expand Up @@ -2617,14 +2660,37 @@ static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
util_fatal("failed to open %s: %m", opt_output);
}

r = read(fd_in, in_buffer, sizeof(in_buffer));
in_len = r;
if (use_aead_buffer) {
in_len = lseek(fd_in, 0, SEEK_END);
aead_in_buffer = calloc(sizeof(CK_BYTE), in_len);
if (aead_in_buffer == NULL)
util_fatal("calloc failed: %m");
lseek(fd_in, 0, SEEK_SET);
r = read(fd_in, aead_in_buffer, in_len);
} else {
r = read(fd_in, in_buffer, sizeof(in_buffer));
in_len = r;
}

if (r < 0)
util_fatal("Cannot read from %s: %m", opt_input);

rv = CKR_CANCEL;
if (r < (int) sizeof(in_buffer)) {
if (use_aead_buffer) {
rv = p11->C_DecryptInit(session, &mech, key);
if (rv != CKR_OK)
p11_fatal("C_DecryptInit", rv);
if ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key))
login(session, CKU_CONTEXT_SPECIFIC);
out_len = sizeof(out_buffer);
Comment on lines +2680 to +2685
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is still a lot of duplicated code. In both of these conditions, this block is the same. Would it be possible to rewrite it to avoid the code duplication by placing this part before the newly introduced condition?

rv = p11->C_Decrypt(session, aead_in_buffer, in_len, NULL, &out_len);
if (rv != CKR_OK)
p11_fatal("C_Decrypt", rv);
aead_out_buffer = calloc(sizeof(CK_BYTE), out_len);
if (aead_out_buffer == NULL)
util_fatal("calloc failed: %m");
rv = p11->C_Decrypt(session, aead_in_buffer, in_len, aead_out_buffer, &out_len);
} else if (r < (int) sizeof(in_buffer)) {
out_len = sizeof(out_buffer);
rv = p11->C_DecryptInit(session, &mech, key);
if (rv != CKR_OK)
Expand Down Expand Up @@ -2656,7 +2722,11 @@ static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
p11_fatal("C_DecryptFinal", rv);
}
if (out_len) {
r = write(fd_out, out_buffer, out_len);
if (use_aead_buffer) {
r = write(fd_out, aead_out_buffer, out_len);
} else {
r = write(fd_out, out_buffer, out_len);
}
if (r != (int) out_len)
util_fatal("Cannot write to %s: %m", opt_output);
}
Expand All @@ -2673,12 +2743,15 @@ static void encrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
{
unsigned char in_buffer[1024], out_buffer[1024];
CK_MECHANISM mech;
CK_GCM_PARAMS gcm_params;
CK_RV rv;
CK_ULONG in_len, out_len;
int fd_in, fd_out;
int r;
CK_BYTE_PTR iv = NULL;
size_t iv_size = 0;
CK_BYTE_PTR aad = NULL;
size_t aad_size = 0;

if (!opt_mechanism_used)
if (!find_mechanism(slot, CKF_ENCRYPT | opt_allow_sw, NULL, 0, &opt_mechanism))
Expand All @@ -2700,6 +2773,23 @@ static void encrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
mech.pParameter = iv;
mech.ulParameterLen = iv_size;
break;
case CKM_AES_GCM:
iv_size = 16;
iv = get_iv(opt_iv, &iv_size);
gcm_params.pIv = iv;
gcm_params.ulIvLen = iv_size;
gcm_params.ulIvBits = iv_size * 8; // no one seems to know what this is for

aad_size = 0;
aad = get_aad(opt_aad_file, &aad_size);
gcm_params.pAAD = aad;
gcm_params.ulAADLen = aad_size;

gcm_params.ulTagBits = opt_tag_len * 8;

mech.pParameter = &gcm_params;
mech.ulParameterLen = sizeof(gcm_params);
break;
default:
util_fatal("Mechanism %s illegal or not supported\n", p11_mechanism_to_name(opt_mechanism));
}
Expand Down Expand Up @@ -8284,6 +8374,51 @@ static CK_BYTE_PTR get_iv(const char *iv_input, size_t *iv_size)

return iv;
}
static CK_BYTE_PTR get_aad(const char *aad_file_input, size_t *aad_size)
{
*aad_size = 0;
/* no AAD filepath supplied on command line */
if (!aad_file_input) {
return NULL;
}
if (strlen(aad_file_input) == 0) {
fprintf(stderr, "Warning: AAD filepath empty, AAD will not be used.\n");
return NULL;
}

FILE* aad_file = fopen(aad_file_input, "rb");
if (!aad_file) {
fprintf(stderr, "Warning: Couldn't open AAD file, AAD will not be used.\n");
return NULL;
}

fseek(aad_file, 0, SEEK_END);
size_t size = ftell(aad_file);
if (size == 0) {
fprintf(stderr, "Warning: AAD file is empty, AAD will not be used.\n");
return NULL;
}
fseek(aad_file, 0, SEEK_SET);

CK_BYTE_PTR aad = calloc(sizeof(CK_BYTE), size);
if (!aad) {
fprintf(stderr, "Warning: Out of memory, AAD will not be used.\n");
fclose(aad_file);
return NULL;
}

size_t read = fread(aad, 1, size, aad_file);
fclose(aad_file);
if (read != size) {
fprintf(stderr, "Warning: Couldn't read all of the AAD file, AAD will not be used.\n");
free(aad);
return NULL;
}

*aad_size = size;

return aad;
}

static void pseudo_randomize(unsigned char *data, size_t dataLen)
{
Expand Down