├── .gitignore ├── debug.h ├── util.h ├── certificates.h ├── attributes.h ├── test └── cert.pem ├── debug.cpp ├── openssl_compat.h ├── pkcs11_compat.h ├── aws_kms_slot.h ├── certificates_test.cpp ├── LICENSE ├── util.cpp ├── gpg_signing.md ├── aws_kms_slot.cpp ├── certificates.cpp ├── kernel_signing.md ├── .circleci └── config.yml ├── unsupported.h ├── unsupported.cpp ├── Makefile ├── aws_kms_pkcs11_test.c ├── attributes.cpp ├── README.md └── aws_kms_pkcs11.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | *.so 4 | aws_kms_pkcs11_test 5 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | 2 | extern bool debug_enabled; 3 | void debug(const char *fmt, ...); 4 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #include 2 | Aws::Client::ClientConfiguration create_aws_config(std::string region); 3 | -------------------------------------------------------------------------------- /certificates.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | X509* parseCertificateFromFile(const char* filename); 5 | X509* parseCertificateFromB64Der(const char* b64Der); 6 | X509* parseCertificateFromARN(const std::string &ca_arn, const std::string &arn, const std::string ®ion); 7 | -------------------------------------------------------------------------------- /attributes.h: -------------------------------------------------------------------------------- 1 | #include "pkcs11_compat.h" 2 | #include "aws_kms_slot.h" 3 | 4 | CK_RV getKmsKeyAttributeValue(AwsKmsSlot& slot, CK_ATTRIBUTE_TYPE attr, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen); 5 | CK_RV getCertificateAttributeValue(AwsKmsSlot& slot, CK_ATTRIBUTE_TYPE attr, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen); 6 | -------------------------------------------------------------------------------- /test/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBMzCB2qADAgECAhRhCYiLH5mYg8WuUXk7+QwmFqZaWjAKBggqhkjOPQQDAjAR 3 | MQ8wDQYDVQQDDAZteWNlcnQwHhcNMjEwNjA1MDUwMzQ3WhcNMjIwNjA2MDUwMzQ3 4 | WjARMQ8wDQYDVQQDDAZteWNlcnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQO 5 | H6FWqrb+0jujRBi/LLCulKIy1DvLtNvQwV3N2dkM/86ieenmQF02gwaoPmQSEpsY 6 | i+swzkLiJiHxFHEP696VoxAwDjAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMCA0gA 7 | MEUCIB0HGcO4henfTmmQbHAvp7karU25057Fjilwgz1hJEkCAiEAwoasCkulAYBd 8 | f1+L9F1+a/FGeFtel6d8G9J6VzhT/y0= 9 | -----END CERTIFICATE----- 10 | -------------------------------------------------------------------------------- /debug.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "debug.h" 6 | 7 | bool debug_enabled = false; 8 | void debug(const char *fmt, ...) { 9 | va_list args; 10 | 11 | if (!debug_enabled) { 12 | return; 13 | } 14 | 15 | char* longer_fmt = (char*)malloc(strlen(fmt)+11); 16 | strcpy(longer_fmt, "AWS_KMS: "); 17 | strcpy(longer_fmt+9, fmt); 18 | longer_fmt[strlen(fmt)+9] = '\n'; 19 | longer_fmt[strlen(fmt)+10] = '\0'; 20 | 21 | va_start(args, fmt); 22 | vprintf(longer_fmt, args); 23 | va_end(args); 24 | 25 | free(longer_fmt); 26 | } 27 | -------------------------------------------------------------------------------- /openssl_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPENSSL_COMPAT_H 2 | #define __OPENSSL_COMPAT_H 3 | 4 | #include 5 | 6 | #if OPENSSL_VERSION_NUMBER < 0x10101000L 7 | #define EVP_PKEY_get0_RSA(_pkey) ((_pkey)->pkey.rsa) 8 | #define RSA_get0_n(_rsa) ((_rsa)->n) 9 | #define RSA_get0_e(_rsa) ((_rsa)->e) 10 | #define EVP_PKEY_get0_EC_KEY(_pkey) ((_pkey)->pkey.ec) 11 | #define ECDSA_SIG_get0_r(_sig) ((_sig)->r) 12 | #define ECDSA_SIG_get0_s(_sig) ((_sig)->s) 13 | #define X509_get0_serialNumber X509_get_serialNumber 14 | #endif 15 | 16 | #if OPENSSL_VERSION_NUMBER < 0x30000000L 17 | #define OSSL_UNCONST(_type, _expr) (const_cast <_type *>(_expr)) 18 | #else 19 | #define OSSL_UNCONST(_type, _expr) (_expr) 20 | #endif 21 | 22 | #endif /* __OPENSSL_COMPAT_H */ 23 | -------------------------------------------------------------------------------- /pkcs11_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Wrapper around pkcs11.h which defines some macros/types that may or may not be 3 | * there depending on the variant of pkcs11.h present on the system. 4 | * 5 | * The recommendation seems to be, long run, to switch to using NULL, true and false 6 | * directly rather than those macros. 7 | */ 8 | #ifndef __PKCS11_COMPAT_H__ 9 | #define __PKCS11_COMPAT_H__ 10 | 11 | #include 12 | #include 13 | 14 | #ifndef CK_NULL_PTR 15 | #define CK_NULL_PTR NULL 16 | #endif 17 | 18 | #ifndef NULL_PTR 19 | #define NULL_PTR NULL 20 | #endif 21 | 22 | #ifndef CK_TRUE 23 | #define CK_TRUE true 24 | #endif 25 | 26 | #ifndef CK_FALSE 27 | #define CK_FALSE false 28 | #endif 29 | 30 | /* opencryptoki in ubuntu 20.04 is missing that one */ 31 | #ifndef CKR_ACTION_PROHIBITED 32 | #define CKR_ACTION_PROHIBITED (0x1BUL) 33 | #endif 34 | 35 | #endif /* __PKCS11_COMPAT_H__ */ 36 | 37 | -------------------------------------------------------------------------------- /aws_kms_slot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using std::string; 9 | 10 | class AwsKmsSlot { 11 | private: 12 | const string label; 13 | const string kms_key_id; 14 | const string aws_region; 15 | const X509* certificate; 16 | bool public_key_data_fetched; 17 | 18 | Aws::Utils::ByteBuffer public_key_data; 19 | Aws::KMS::Model::KeySpec key_spec; 20 | void FetchPublicKeyData(); 21 | public: 22 | AwsKmsSlot(const string &label, const string &kms_key_id, const string aws_region, 23 | const X509* certificate); 24 | const string& GetLabel(); 25 | const string& GetKmsKeyId(); 26 | const string& GetAwsRegion(); 27 | const X509* GetCertificate(); 28 | Aws::Utils::ByteBuffer GetPublicKeyData(); 29 | Aws::KMS::Model::KeySpec GetKeySpec(); 30 | }; 31 | -------------------------------------------------------------------------------- /certificates_test.cpp: -------------------------------------------------------------------------------- 1 | #include "certificates.h" 2 | 3 | int main(int argc, char** argv) { 4 | X509* cert = parseCertificateFromFile("test/cert.pem"); 5 | if (cert == NULL) { 6 | printf("Failed to parse certificate from file.\n"); 7 | return 1; 8 | } 9 | X509_free(cert); 10 | 11 | cert = parseCertificateFromB64Der("MIIBMzCB2qADAgECAhRhCYiLH5mYg8WuUXk7+QwmFqZaWjAKBggqhkjOPQQDAjARMQ8wDQYDVQQDDAZteWNlcnQwHhcNMjEwNjA1MDUwMzQ3WhcNMjIwNjA2MDUwMzQ3WjARMQ8wDQYDVQQDDAZteWNlcnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQOH6FWqrb+0jujRBi/LLCulKIy1DvLtNvQwV3N2dkM/86ieenmQF02gwaoPmQSEpsYi+swzkLiJiHxFHEP696VoxAwDjAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMCA0gAMEUCIB0HGcO4henfTmmQbHAvp7karU25057Fjilwgz1hJEkCAiEAwoasCkulAYBdf1+L9F1+a/FGeFtel6d8G9J6VzhT/y0="); 12 | if (cert == NULL) { 13 | printf("Failed to parse B64 DER certificate.\n"); 14 | return 1; 15 | } 16 | X509_free(cert); 17 | 18 | printf("Test successful.\n"); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ian Haken 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "util.h" 6 | 7 | #include 8 | #include 9 | 10 | Aws::Client::ClientConfiguration create_aws_config(std::string region) { 11 | Aws::Client::ClientConfiguration awsConfig; 12 | 13 | #ifdef AWS_SDK_USE_SYSTEM_PROXY 14 | awsConfig.allowSystemProxy = true; 15 | #endif 16 | 17 | if (region.length() > 0) { 18 | awsConfig.region = region; 19 | } 20 | 21 | const char* endpoint = std::getenv("AWS_ENDPOINT_URL"); 22 | if (endpoint != nullptr) { 23 | const char* prefix = "https"; 24 | awsConfig.enableEndpointDiscovery = false; 25 | 26 | std::string url(endpoint); 27 | std::string::size_type pos = url.find("://"); 28 | if (pos != std::string::npos) { 29 | // Remove the scheme part (e.g., "http://") 30 | url = url.substr(pos + 3); 31 | } 32 | awsConfig.endpointOverride = Aws::String(url); 33 | awsConfig.scheme = (strncmp(endpoint, prefix, strlen(prefix)) == 0) ? Aws::Http::Scheme::HTTPS : Aws::Http::Scheme::HTTP; 34 | awsConfig.verifySSL = (strncmp(endpoint, prefix, strlen(prefix)) == 0) ? true : false; 35 | } 36 | 37 | return awsConfig; 38 | } 39 | -------------------------------------------------------------------------------- /gpg_signing.md: -------------------------------------------------------------------------------- 1 | GPG Signing using AWS KMS 2 | ========================= 3 | 4 | `gpg` can use the PKCS#11 provider by way of [gnupg-pkcs11-scd](https://github.com/alonbl/gnupg-pkcs11-scd). 5 | Note that 0.10.0+ is required. 6 | 7 | Configure `gpg-agent` to consult the smartcard daemon. 8 | Keys must have corresponding certificates to be discovered by the daemon. 9 | ``` 10 | mkdir gpgtmp 11 | export GNUPGHOME="${PWD}/gpgtmp" 12 | 13 | # configure the agent 14 | cat <> "${GNUPGHOME}/gpg-agent.conf" 15 | scdaemon-program /usr/bin/gnupg-pkcs11-scd 16 | EOF 17 | 18 | # configure the smartcard daemon 19 | cat <> "${GNUPGHOME}/gnupg-pkcs11-scd.conf" 20 | providers kms 21 | provider-kms-library /usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so 22 | log-file /dev/null 23 | EOF 24 | ``` 25 | 26 | The first import into `gpg` requires the keygrip and additional metadata. 27 | ``` 28 | # read keys from the card 29 | gpg --card-status 30 | 31 | # find the keygrip 32 | KEYGRIP=$(find ${GNUPGHOME}/private-keys-*.d -type f -name '*.key' -printf '%P'|cut -d '.' -f1|head -n1) 33 | 34 | # import signing key 35 | # (toggle 'e' since encryption is not supported) 36 | gpg --expert --full-generate-key --command-fd 0 < 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "aws_kms_slot.h" 8 | #include "util.h" 9 | #include "debug.h" 10 | 11 | using std::string; 12 | 13 | AwsKmsSlot::AwsKmsSlot(const string &label, const string &kms_key_id, const string aws_region, 14 | const X509* certificate) : 15 | label(label), kms_key_id(kms_key_id), aws_region(aws_region), 16 | certificate(certificate), 17 | public_key_data_fetched(false) 18 | { 19 | } 20 | 21 | const string &AwsKmsSlot::GetLabel() { 22 | return this->label; 23 | } 24 | const string & AwsKmsSlot::GetAwsRegion() { 25 | return this->aws_region; 26 | } 27 | const string & AwsKmsSlot::GetKmsKeyId() { 28 | return this->kms_key_id; 29 | } 30 | const X509* AwsKmsSlot::GetCertificate() { 31 | return this->certificate; 32 | } 33 | void AwsKmsSlot::FetchPublicKeyData() { 34 | if (this->public_key_data_fetched) { 35 | return; 36 | } 37 | Aws::Client::ClientConfiguration awsConfig = create_aws_config(this->aws_region); 38 | Aws::KMS::KMSClient kms(awsConfig); 39 | Aws::KMS::Model::GetPublicKeyRequest req; 40 | 41 | debug("Getting public key for key %s", this->kms_key_id.c_str()); 42 | req.SetKeyId(this->kms_key_id); 43 | Aws::KMS::Model::GetPublicKeyOutcome res = kms.GetPublicKey(req); 44 | if (!res.IsSuccess()) { 45 | debug("Got error from AWS fetching public key for key id %s: %s", this->kms_key_id.c_str(), res.GetError().GetMessage().c_str()); 46 | this->public_key_data = Aws::Utils::ByteBuffer(); 47 | this->key_spec = Aws::KMS::Model::KeySpec::NOT_SET; 48 | } else { 49 | debug("Successfully fetched public key data."); 50 | this->public_key_data = res.GetResult().GetPublicKey(); 51 | this->key_spec = res.GetResult().GetKeySpec(); 52 | } 53 | this->public_key_data_fetched = true; 54 | } 55 | Aws::Utils::ByteBuffer AwsKmsSlot::GetPublicKeyData() { 56 | this->FetchPublicKeyData(); 57 | return this->public_key_data; 58 | } 59 | Aws::KMS::Model::KeySpec AwsKmsSlot::GetKeySpec() { 60 | this->FetchPublicKeyData(); 61 | return this->key_spec; 62 | } 63 | -------------------------------------------------------------------------------- /certificates.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "debug.h" 12 | #include "util.h" 13 | #include "openssl_compat.h" 14 | 15 | using std::string; 16 | 17 | X509* parseCertificateFromFile(const char* filename) { 18 | int res; 19 | long len; 20 | char *name, *header; 21 | unsigned char *data; 22 | X509* cert = NULL; 23 | 24 | FILE* f = fopen(filename, "r"); 25 | if (f == NULL) { 26 | return NULL; 27 | } 28 | 29 | res = 1; 30 | while (res == 1) { 31 | res = PEM_read(f, &name, &header, &data, &len); 32 | if (res == 0) { 33 | fclose(f); 34 | return NULL; 35 | } 36 | if (strcmp("CERTIFICATE", name) == 0) { 37 | const unsigned char* d = data; 38 | cert = d2i_X509(NULL, &d, len); 39 | } 40 | OPENSSL_free(name); 41 | OPENSSL_free(header); 42 | OPENSSL_free(data); 43 | if (cert != NULL) { 44 | fclose(f); 45 | return cert; 46 | } 47 | } 48 | 49 | fclose(f); 50 | return NULL; 51 | } 52 | 53 | X509* parseCertificateFromB64Der(const char* b64Der) { 54 | BIO *bio_mem = BIO_new(BIO_s_mem()); 55 | BIO_puts(bio_mem, b64Der); 56 | BIO *b64 = BIO_new(BIO_f_base64()); 57 | BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 58 | BIO_push(b64, bio_mem); 59 | X509* cert = d2i_X509_bio(b64, NULL); 60 | BIO_free_all(b64); 61 | return cert; 62 | } 63 | 64 | X509* parseCertificateFromARN(const string &ca_arn, const string &arn, const std::string ®ion) { 65 | Aws::Client::ClientConfiguration awsConfig = create_aws_config(region); 66 | Aws::ACMPCA::ACMPCAClient acmpca(awsConfig); 67 | Aws::ACMPCA::Model::GetCertificateRequest req; 68 | 69 | req.SetCertificateArn(arn); 70 | req.SetCertificateAuthorityArn(ca_arn); 71 | auto res = acmpca.GetCertificate(req); 72 | if (!res.IsSuccess()) { 73 | debug("Failed to retreive certificate %s from CA %s\n", arn, ca_arn); 74 | return NULL; 75 | } 76 | auto pem = res.GetResult().GetCertificate(); 77 | auto bio = BIO_new_mem_buf((char *)pem.c_str(), -1); 78 | if (!bio) { 79 | debug("Failed to allocate BIO for cert\n"); 80 | return NULL; 81 | } 82 | auto cert = PEM_read_bio_X509(bio, NULL, 0, NULL); 83 | BIO_free(bio); 84 | return cert; 85 | } 86 | -------------------------------------------------------------------------------- /kernel_signing.md: -------------------------------------------------------------------------------- 1 | Kernel Signing using AWS KMS 2 | ============================== 3 | 4 | When building a custom kernel it may be useful to sign the modules to prevent unauthorized module loading on the system. This is most appropriate for embedded devices. The process is pretty straightforward. 5 | 6 | At first we need to do a fake build to get a x509.genkey file which we can use for generating the certificate. A sample is included, but it's better to use the same one the kernel would use. The kernel will automatically generate this file and create a certificate if CONFIG_MODULE_SIG_KEY="", so we take advantage of this to get a sample x509.genkey file. 7 | 8 | Add the following to the kernel config: 9 | ``` 10 | CONFIG_MODULE_SIG=y 11 | CONFIG_MODULE_SIG_SHA256=y 12 | CONFIG_MODULE_SIG_KEY="" 13 | ``` 14 | Then build the kernel using `make` and the x509.genkey file should be located in `/certs/x509.genkey`. You will use this file to self-sign a certificate in the following step. 15 | 16 | If you want to skip the above step and use a passed file here is a sample. Some extra fields have been added (such as Organisation Name) to provide extra information in the certificate. 17 | ``` 18 | [ req ] 19 | default_bits = 2048 20 | distinguished_name = req_distinguished_name 21 | prompt = no 22 | string_mask = utf8only 23 | x509_extensions = myexts 24 | 25 | [ req_distinguished_name ] 26 | countryName = US 27 | stateOrProvinceName = Your State 28 | localityName = Your City 29 | organizationName = Your Company 30 | commonName = Kernel Signing Key 31 | emailAddress = you@example.com 32 | 33 | [ myexts ] 34 | basicConstraints=critical,CA:FALSE 35 | keyUsage=digitalSignature 36 | subjectKeyIdentifier=hash 37 | authorityKeyIdentifier=keyid 38 | ``` 39 | 40 | Make sure your aws-kms-pkcs11 json config is setup to point to the key, then sign your certificate with the following 41 | `AWS_KMS_PKCS11_DEBUG=1 PKCS11_MODULE_PATH="/usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so" openssl req -config <(cat "/certs/x509.genkey") -x509 -key "pkcs11:token=" -keyform engine -engine pkcs11 -out mycert.pem -days 36500`. 42 | 43 | Now you have a signed certificate with "mycert.pem", add this as a certificate in your aws kms config, update with this line (more details in config section below): `"certificate_path": "mykey.crt"`. 44 | 45 | Update your kernel config: 46 | `CONFIG_MODULE_SIG_KEY="pkcs11:token=` 47 | 48 | Now make the kernel as normal and modules will be signed using the kms private key and your public certificate you just signed above. 49 | 50 | Make sure to keep the self signed certificate in a safe place. 51 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | commands: 4 | build_common: 5 | steps: 6 | - checkout 7 | - run: 8 | command: sudo apt-get update && sudo bash -c 'DEBIAN_FRONTEND=noninteractive apt-get install build-essential gcc g++ cmake libcurl4-openssl-dev libssl-dev libopencryptoki-dev libjson-c-dev libp11-kit-dev' 9 | - restore_cache: 10 | key: aws-sdk-cpp-1.11.511-{{ checksum "/etc/os-release" }}-{{arch}} 11 | - run: 12 | command: | 13 | if [[ ! -e ~/aws-sdk-cpp ]]; then 14 | curl -o ~/aws-sdk-cpp.tar.gz -L https://github.com/aws/aws-sdk-cpp/archive/1.11.511.tar.gz 15 | [[ "FASxC7+IysPIgmVndcl9r8JcCaocPH4DbOuX2dP+ryA=" == $(openssl dgst -sha256 -binary < ~/aws-sdk-cpp.tar.gz | openssl enc -base64) ]] || exit 1 16 | mkdir ~/aws-sdk-cpp-src 17 | tar -C ~/aws-sdk-cpp-src --strip-components=1 -zxf ~/aws-sdk-cpp.tar.gz 18 | cd ~/aws-sdk-cpp-src && ./prefetch_crt_dependency.sh 19 | mkdir ~/aws-sdk-cpp-src/sdk_build 20 | cd ~/aws-sdk-cpp-src/sdk_build && cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_ONLY="kms;acm-pca" -DENABLE_TESTING=OFF -DCMAKE_INSTALL_PREFIX=$HOME/aws-sdk-cpp -DBUILD_SHARED_LIBS=OFF && make && make install 21 | fi 22 | - save_cache: 23 | key: aws-sdk-cpp-1.11.511-{{ checksum "/etc/os-release" }}-{{arch}} 24 | paths: 25 | - ~/aws-sdk-cpp 26 | - run: 27 | command: AWS_SDK_PATH=$HOME/aws-sdk-cpp make 28 | - run: 29 | command: mkdir -p artifacts && cp aws_kms_pkcs11.so "artifacts/aws_kms_pkcs11-ubuntu$(lsb_release -sr)-$(uname -m).so" 30 | - store_artifacts: 31 | path: artifacts 32 | - persist_to_workspace: 33 | root: artifacts 34 | paths: 35 | - "aws_kms_pkcs11-*.so" 36 | 37 | 38 | jobs: 39 | build-jammy-amd64: 40 | machine: 41 | image: ubuntu-2204:current 42 | resource_class: medium 43 | steps: 44 | - build_common 45 | build-noble-amd64: 46 | machine: 47 | image: ubuntu-2404:current 48 | resource_class: medium 49 | steps: 50 | - build_common 51 | build-jammy-arm64: 52 | machine: 53 | image: ubuntu-2204:current 54 | resource_class: arm.medium 55 | steps: 56 | - build_common 57 | build-noble-arm64: 58 | machine: 59 | image: ubuntu-2404:current 60 | resource_class: arm.medium 61 | steps: 62 | - build_common 63 | 64 | publish-github-release: 65 | docker: 66 | - image: cibuilds/github:0.10 67 | steps: 68 | - attach_workspace: 69 | at: ./artifacts 70 | - run: 71 | name: "Publish Release on GitHub" 72 | command: | 73 | ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${CIRCLE_TAG} ./artifacts/ 74 | 75 | workflows: 76 | build: 77 | jobs: 78 | - build-jammy-amd64: 79 | filters: 80 | tags: 81 | only: /.*/ 82 | - build-noble-amd64: 83 | filters: 84 | tags: 85 | only: /.*/ 86 | - build-jammy-arm64: 87 | filters: 88 | tags: 89 | only: /.*/ 90 | - build-noble-arm64: 91 | filters: 92 | tags: 93 | only: /.*/ 94 | - publish-github-release: 95 | requires: 96 | - build-jammy-amd64 97 | - build-noble-amd64 98 | - build-jammy-arm64 99 | - build-noble-arm64 100 | filters: 101 | branches: 102 | ignore: /.*/ 103 | tags: 104 | only: /^v\d+\.\d+\.\d+$/ 105 | 106 | -------------------------------------------------------------------------------- /unsupported.h: -------------------------------------------------------------------------------- 1 | #include "pkcs11_compat.h" 2 | 3 | CK_RV C_CancelFunction(CK_SESSION_HANDLE); 4 | CK_RV C_CopyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); 5 | CK_RV C_CreateObject(CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); 6 | CK_RV C_Decrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 7 | CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 8 | CK_RV C_DecryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR); 9 | CK_RV C_DecryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE); 10 | CK_RV C_DecryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 11 | CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 12 | CK_RV C_DeriveKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); 13 | CK_RV C_DestroyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); 14 | CK_RV C_Digest(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 15 | CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 16 | CK_RV C_DigestFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR); 17 | CK_RV C_DigestInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR); 18 | CK_RV C_DigestKey(CK_SESSION_HANDLE, CK_OBJECT_HANDLE); 19 | CK_RV C_DigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG); 20 | CK_RV C_Encrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 21 | CK_RV C_EncryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR); 22 | CK_RV C_EncryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE); 23 | CK_RV C_EncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 24 | CK_RV C_GenerateKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); 25 | CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR, CK_OBJECT_HANDLE_PTR); 26 | CK_RV C_GenerateRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG); 27 | CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE); 28 | CK_RV C_GetObjectSize(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ULONG_PTR); 29 | CK_RV C_GetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR); 30 | CK_RV C_InitPIN(CK_SESSION_HANDLE, CK_CHAR_PTR, CK_ULONG); 31 | CK_RV C_InitToken(CK_SLOT_ID, CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR); 32 | CK_RV C_SeedRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG); 33 | CK_RV C_SetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG); 34 | CK_RV C_SetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE); 35 | CK_RV C_SetPIN(CK_SESSION_HANDLE, CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR, CK_ULONG); 36 | CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 37 | CK_RV C_SignRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 38 | CK_RV C_SignRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE); 39 | CK_RV C_UnwrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); 40 | CK_RV C_Verify(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG); 41 | CK_RV C_VerifyFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG); 42 | CK_RV C_VerifyInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE); 43 | CK_RV C_VerifyRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); 44 | CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE); 45 | CK_RV C_VerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG); 46 | CK_RV C_WaitForSlotEvent(CK_FLAGS, CK_SLOT_ID_PTR, CK_VOID_PTR); 47 | CK_RV C_WrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR); 48 | -------------------------------------------------------------------------------- /unsupported.cpp: -------------------------------------------------------------------------------- 1 | #include "pkcs11_compat.h" 2 | 3 | CK_RV C_CancelFunction(CK_SESSION_HANDLE) { 4 | return CKR_FUNCTION_NOT_SUPPORTED; 5 | } 6 | CK_RV C_CopyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR) { 7 | return CKR_FUNCTION_NOT_SUPPORTED; 8 | } 9 | CK_RV C_CreateObject(CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR) { 10 | return CKR_FUNCTION_NOT_SUPPORTED; 11 | } 12 | CK_RV C_Decrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 13 | return CKR_FUNCTION_NOT_SUPPORTED; 14 | } 15 | CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 16 | return CKR_FUNCTION_NOT_SUPPORTED; 17 | } 18 | CK_RV C_DecryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) { 19 | return CKR_FUNCTION_NOT_SUPPORTED; 20 | } 21 | CK_RV C_DecryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) { 22 | return CKR_FUNCTION_NOT_SUPPORTED; 23 | } 24 | CK_RV C_DecryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 25 | return CKR_FUNCTION_NOT_SUPPORTED; 26 | } 27 | CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 28 | return CKR_FUNCTION_NOT_SUPPORTED; 29 | } 30 | CK_RV C_DeriveKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR) { 31 | return CKR_FUNCTION_NOT_SUPPORTED; 32 | } 33 | CK_RV C_DestroyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE) { 34 | return CKR_FUNCTION_NOT_SUPPORTED; 35 | } 36 | CK_RV C_Digest(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 37 | return CKR_FUNCTION_NOT_SUPPORTED; 38 | } 39 | CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 40 | return CKR_FUNCTION_NOT_SUPPORTED; 41 | } 42 | CK_RV C_DigestFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) { 43 | return CKR_FUNCTION_NOT_SUPPORTED; 44 | } 45 | CK_RV C_DigestInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR) { 46 | return CKR_FUNCTION_NOT_SUPPORTED; 47 | } 48 | CK_RV C_DigestKey(CK_SESSION_HANDLE, CK_OBJECT_HANDLE) { 49 | return CKR_FUNCTION_NOT_SUPPORTED; 50 | } 51 | CK_RV C_DigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) { 52 | return CKR_FUNCTION_NOT_SUPPORTED; 53 | } 54 | CK_RV C_Encrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 55 | return CKR_FUNCTION_NOT_SUPPORTED; 56 | } 57 | CK_RV C_EncryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) { 58 | return CKR_FUNCTION_NOT_SUPPORTED; 59 | } 60 | CK_RV C_EncryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) { 61 | return CKR_FUNCTION_NOT_SUPPORTED; 62 | } 63 | CK_RV C_EncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 64 | return CKR_FUNCTION_NOT_SUPPORTED; 65 | } 66 | CK_RV C_GenerateKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR) { 67 | return CKR_FUNCTION_NOT_SUPPORTED; 68 | } 69 | CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR, CK_OBJECT_HANDLE_PTR) { 70 | return CKR_FUNCTION_NOT_SUPPORTED; 71 | } 72 | CK_RV C_GenerateRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) { 73 | return CKR_FUNCTION_NOT_SUPPORTED; 74 | } 75 | CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE) { 76 | return CKR_FUNCTION_NOT_SUPPORTED; 77 | } 78 | CK_RV C_GetObjectSize(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ULONG_PTR) { 79 | return CKR_FUNCTION_NOT_SUPPORTED; 80 | } 81 | CK_RV C_GetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) { 82 | return CKR_FUNCTION_NOT_SUPPORTED; 83 | } 84 | CK_RV C_InitPIN(CK_SESSION_HANDLE, CK_CHAR_PTR, CK_ULONG) { 85 | return CKR_FUNCTION_NOT_SUPPORTED; 86 | } 87 | CK_RV C_InitToken(CK_SLOT_ID, CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR) { 88 | return CKR_FUNCTION_NOT_SUPPORTED; 89 | } 90 | CK_RV C_SeedRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) { 91 | return CKR_FUNCTION_NOT_SUPPORTED; 92 | } 93 | CK_RV C_SetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG) { 94 | return CKR_FUNCTION_NOT_SUPPORTED; 95 | } 96 | CK_RV C_SetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE) { 97 | return CKR_FUNCTION_NOT_SUPPORTED; 98 | } 99 | CK_RV C_SetPIN(CK_SESSION_HANDLE, CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR, CK_ULONG) { 100 | return CKR_FUNCTION_NOT_SUPPORTED; 101 | } 102 | CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 103 | return CKR_FUNCTION_NOT_SUPPORTED; 104 | } 105 | CK_RV C_SignRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 106 | return CKR_FUNCTION_NOT_SUPPORTED; 107 | } 108 | CK_RV C_SignRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) { 109 | return CKR_FUNCTION_NOT_SUPPORTED; 110 | } 111 | CK_RV C_UnwrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR) { 112 | return CKR_FUNCTION_NOT_SUPPORTED; 113 | } 114 | CK_RV C_Verify(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG) { 115 | return CKR_FUNCTION_NOT_SUPPORTED; 116 | } 117 | CK_RV C_VerifyFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) { 118 | return CKR_FUNCTION_NOT_SUPPORTED; 119 | } 120 | CK_RV C_VerifyInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) { 121 | return CKR_FUNCTION_NOT_SUPPORTED; 122 | } 123 | CK_RV C_VerifyRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR) { 124 | return CKR_FUNCTION_NOT_SUPPORTED; 125 | } 126 | CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) { 127 | return CKR_FUNCTION_NOT_SUPPORTED; 128 | } 129 | CK_RV C_VerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) { 130 | return CKR_FUNCTION_NOT_SUPPORTED; 131 | } 132 | CK_RV C_WaitForSlotEvent(CK_FLAGS, CK_SLOT_ID_PTR, CK_VOID_PTR) { 133 | return CKR_FUNCTION_NOT_SUPPORTED; 134 | } 135 | CK_RV C_WrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) { 136 | return CKR_FUNCTION_NOT_SUPPORTED; 137 | } 138 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Try to locate the AWS SDK if not specified with AWS_SDK_PATH 2 | MACHINE := $(shell gcc -dumpmachine) 3 | ifeq ($(AWS_SDK_PATH),) 4 | ifneq ($(wildcard /usr/include/aws),) 5 | AWS_SDK_PATH := /usr 6 | else ifneq ($(wildcard /usr/local/include/aws),) 7 | AWS_SDK_PATH := /usr/local 8 | else 9 | $(error AWS SDK not found in common include path, please specify AWS_SDK_PATH) 10 | endif 11 | endif 12 | 13 | # Try to find which subdir of the SDK has the libraries 14 | ifeq ($(AWS_SDK_LIB_PATH),) 15 | ifneq ($(wildcard $(AWS_SDK_PATH)/lib/libaws-c-common.*),) 16 | AWS_SDK_LIB_PATH := $(addsuffix /lib,$(AWS_SDK_PATH)) 17 | else ifneq ($(wildcard $(AWS_SDK_PATH)/lib64/libaws-c-common.*),) 18 | AWS_SDK_LIB_PATH := $(addsuffix /lib64,$(AWS_SDK_PATH)) 19 | else ifneq ($(wildcard $(AWS_SDK_PATH)/lib/$(MACHINE)/libaws-c-common.*),) 20 | AWS_SDK_LIB_PATH := $(addsuffix /lib/$(MACHINE),$(AWS_SDK_PATH)) 21 | else 22 | $(error neither lib or lib64 found in AWS SDK) 23 | endif 24 | endif 25 | 26 | # The SDK has two main sets of components, the C runtimes and the C++ runtimes, 27 | # depending on how the SDK is built, they can be separately either static libs, 28 | # dynamic libs, or both. 29 | # 30 | # Let's try to intuit this unless specified, with a bias towards static libs 31 | # if available, as they SDK versions tend to have ABI compatilbility issues 32 | # 33 | # Use these variables to override the mechanisms for those two sets: 34 | # 35 | # AWS_SDK_STATIC = y : Force use of static libraries for both C and C++ 36 | # AWS_SDK_STATIC = n : Force use of dynamic libraries for both C and C++ 37 | # AWS_SDK_C_STATIC = y : Force use of static libraries for C 38 | # AWS_SDK_C_STATIC = n : Force use of dynamic libraries for C 39 | # AWS_SDK_CPP_STATIC = y : Force use of static libraries for C++ 40 | # AWS_SDK_CPP_STATIC = n : Force use of dynamic libraries for C++ 41 | 42 | ifdef AWS_SDK_STATIC 43 | ifeq ($(AWS_SDK_STATIC),y) 44 | AWS_SDK_C_STATIC := y 45 | AWS_SDK_CPP_STATIC := y 46 | else ifeq ($(AWS_SDK_STATIC),n) 47 | AWS_SDK_C_STATIC := n 48 | AWS_SDK_CPP_STATIC := n 49 | else 50 | $(error Unrecognized value for AWS_SDK_STATIC, use y or n) 51 | endif 52 | endif 53 | 54 | ifndef AWS_SDK_C_STATIC 55 | ifneq ($(wildcard ${AWS_SDK_LIB_PATH}/libaws-c-common.a),) 56 | AWS_SDK_C_STATIC := y 57 | else ifneq ($(wildcard ${AWS_SDK_LIB_PATH}/libaws-c-common.so),) 58 | AWS_SDK_C_STATIC := n 59 | else 60 | $(error Cannot find either static or dynamic SDK C libraries) 61 | endif 62 | endif 63 | 64 | ifndef AWS_SDK_CPP_STATIC 65 | ifneq ($(wildcard ${AWS_SDK_LIB_PATH}/libaws-cpp-sdk-kms.a),) 66 | AWS_SDK_CPP_STATIC := y 67 | else ifneq ($(wildcard ${AWS_SDK_LIB_PATH}/libaws-cpp-sdk-core.so),) 68 | AWS_SDK_CPP_STATIC := n 69 | else 70 | $(error Cannot find either static or dynamic SDK C++ libraries) 71 | endif 72 | endif 73 | 74 | # Try to locate the pkcs11.h if location not specified with PKCS11_INC 75 | ifeq ($(PKCS11_INC),) 76 | PKCS11_INC := $(shell pkg-config --cflags p11-kit-1 2>/dev/null) 77 | ifneq ($(PKCS11_INC),) 78 | PKCS11_INC := $(addsuffix /p11-kit,$(PKCS11_INC)) 79 | else 80 | PKCS11_INC := $(shell pkg-config --cflags nss 2>/dev/null) 81 | endif 82 | ifeq ($(PKCS11_INC),) 83 | ifneq ($(wildcard /usr/include/opencryptoki),) 84 | PKCS11_INC := -I/usr/include/opencryptoki 85 | endif 86 | endif 87 | ifeq ($(PKCS11_INC),) 88 | $(error p11-kit or nss not found, specify PKCS11_INC) 89 | endif 90 | endif 91 | 92 | # Try to locate target install location if not specified with PKCS11_MOD_PATH 93 | ifeq ($(PKCS11_MOD_PATH),) 94 | PKCS11_MOD_PATH := $(shell pkg-config --variable p11_module_path p11-kit-1 2>/dev/null) 95 | ifeq ($(PKCS11_MOD_PATH),) 96 | PKCS11_MOD_PATH := $(shell pkg-config --variable libdir nss 2>/dev/null) 97 | ifneq ($(PKCS11_MOD_PATH),) 98 | PKCS11_MOD_PATH := $(addsuffix /pkcs11,$(PKCS11_MOD_PATH)) 99 | endif 100 | endif 101 | ifeq ($(PKCS11_MOD_PATH),) 102 | $(error p11-kit or nss not found, specify PKCS11_MOD_PATH) 103 | endif 104 | endif 105 | 106 | # Try to locate the json-c headers if location not specified with JSON_C_INC 107 | ifeq ($(JSON_C_INC),) 108 | JSON_C_INC := $(shell pkg-config --cflags json-c 2>/dev/null) 109 | ifeq ($(JSON_C_INC),) 110 | $(error json-c not found, specify JSON_C_INC) 111 | endif 112 | endif 113 | 114 | ifdef AWS_SDK_USE_SYSTEM_PROXY 115 | ifeq ($(AWS_SDK_USE_SYSTEM_PROXY),y) 116 | PROXY_CFLAGS := -DAWS_SDK_USE_SYSTEM_PROXY=1 117 | else ifeq ($(AWS_SDK_USE_SYSTEM_PROXY),n) 118 | PROXY_CFLAGS := 119 | else 120 | $(error Invalid value for AWS_SDK_USE_SYSTEM_PROXY, use y or n) 121 | endif 122 | endif 123 | 124 | # Build library link list 125 | STATIC_LIBS := 126 | LIBS := 127 | ifeq ($(AWS_SDK_CPP_STATIC),y) 128 | $(info Using C++ SDK static libraries) 129 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-cpp-sdk-kms.a 130 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-cpp-sdk-acm-pca.a 131 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-cpp-sdk-core.a 132 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-crt-cpp.a 133 | else ifeq ($(AWS_SDK_CPP_STATIC),n) 134 | $(info Using C++ SDK dynamic libraries) 135 | LIBS += $(AWS_SDK_LIB_PATH)/libaws-cpp-sdk-core.so 136 | LIBS += $(AWS_SDK_LIB_PATH)/libaws-cpp-sdk-kms.so 137 | LIBS += $(AWS_SDK_LIB_PATH)/libaws-cpp-sdk-acm-pca.so 138 | else 139 | $(error Unrecognized value for AWS_SDK_CPP_STATIC, use y or n) 140 | endif 141 | ifeq ($(AWS_SDK_C_STATIC),y) 142 | $(info Using C SDK static libraries) 143 | STATIC_LIBS += -Wl,--start-group 144 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-checksums.a 145 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-common.a 146 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-event-stream.a 147 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-auth.a 148 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-http.a 149 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-io.a 150 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-mqtt.a 151 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-cal.a 152 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-compression.a 153 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-s3.a 154 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-sdkutils.a 155 | STATIC_LIBS += $(AWS_SDK_LIB_PATH)/libs2n.a 156 | STATIC_LIBS += -Wl,--end-group 157 | else ifeq ($(AWS_SDK_C_STATIC),n) 158 | $(info Using C SDK dynamic libraries) 159 | LIBS += $(AWS_SDK_LIB_PATH)/libaws-checksums.so 160 | LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-common.so 161 | LIBS += $(AWS_SDK_LIB_PATH)/libaws-c-event-stream.so 162 | else 163 | $(error Unrecognized value for AWS_SDK_C_STATIC, use y or n) 164 | endif 165 | 166 | # Source files 167 | SRC = attributes.cpp aws_kms_pkcs11.cpp certificates.cpp unsupported.cpp debug.cpp util.cpp aws_kms_slot.cpp 168 | 169 | all: aws_kms_pkcs11.so 170 | 171 | clean: 172 | rm -f aws_kms_pkcs11.so aws_kms_pkcs11_test aws_kms_client_test 173 | 174 | test: aws_kms_pkcs11_test certificates_test 175 | ./certificates_test 176 | AWS_KMS_PKCS11_DEBUG=1 ./aws_kms_pkcs11_test 177 | 178 | certificates_test: certificates.cpp certificates_test.cpp 179 | g++ -g -fPIC -Wall -I$(AWS_SDK_PATH)/include $(PKCS11_INC) $(JSON_C_INC) $(PROXY_CFLAGS) -fno-exceptions -std=c++17 \ 180 | debug.cpp util.cpp certificates.cpp certificates_test.cpp -o certificates_test $(STATIC_LIBS) $(LIBS) -lcrypto -ljson-c -lcurl -lz 181 | 182 | aws_kms_pkcs11_test: aws_kms_pkcs11_test.c aws_kms_pkcs11.so 183 | g++ -g -fPIC -Wall -I$(AWS_SDK_PATH)/include $(PKCS11_INC) $(JSON_C_INC) $(PROXY_CFLAGS) -fno-exceptions -std=c++17 \ 184 | aws_kms_pkcs11_test.c -o aws_kms_pkcs11_test -ldl 185 | 186 | aws_kms_pkcs11.so: aws_kms_pkcs11.cpp unsupported.cpp aws_kms_slot.cpp debug.cpp util.cpp attributes.cpp certificates.cpp 187 | g++ -shared -fPIC -Wall -I$(AWS_SDK_PATH)/include $(PKCS11_INC) $(JSON_C_INC) $(PROXY_CFLAGS) -fno-exceptions -std=c++17 $(SRC) \ 188 | -o aws_kms_pkcs11.so $(STATIC_LIBS) $(LIBS) -lcrypto -ljson-c -lcurl -lz 189 | 190 | install: aws_kms_pkcs11.so 191 | mkdir -p $(DESTDIR)$(PKCS11_MOD_PATH) 192 | cp aws_kms_pkcs11.so $(DESTDIR)$(PKCS11_MOD_PATH)/ 193 | 194 | uninstall: 195 | rm -f $(DESTDIR)$(PKCS11_MOD_PATH)/aws_kms_pkcs11.so 196 | -------------------------------------------------------------------------------- /aws_kms_pkcs11_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "pkcs11_compat.h" 5 | 6 | void dump_bytes(const char* name, const unsigned char* bytes, unsigned long len) { 7 | printf("%s=", name); 8 | for (unsigned long i = 0; i < len; i++) { 9 | printf("%.2X ", bytes[i]); 10 | } 11 | printf("\n"); 12 | } 13 | 14 | CK_RV get_and_dump_attribute(CK_FUNCTION_LIST* f, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type, const char* name) { 15 | CK_ATTRIBUTE attrs[1]; 16 | attrs[0].type = type; 17 | attrs[0].pValue = NULL; 18 | attrs[0].ulValueLen = 0; 19 | CK_RV res = f->C_GetAttributeValue(session, obj, attrs, 1); 20 | if (res != CKR_OK) { 21 | printf("Fail C_GetAttributeValue for attribute size, res=%ld\n", res); 22 | return res; 23 | } 24 | attrs[0].pValue = malloc(attrs[0].ulValueLen); 25 | res = f->C_GetAttributeValue(session, obj, attrs, 1); 26 | if (res != CKR_OK) { 27 | printf("Fail C_GetAttributeValue for getting attribute, res=%ld\n", res); 28 | return res; 29 | } 30 | 31 | dump_bytes(name, (const unsigned char*)attrs[0].pValue, attrs[0].ulValueLen); 32 | free(attrs[0].pValue); 33 | 34 | return CKR_OK; 35 | } 36 | 37 | CK_RV test_all_keys_in_slot(CK_FUNCTION_LIST* f, CK_SLOT_ID slotID) { 38 | printf("Testing slot ID: %ld\n", slotID); 39 | 40 | CK_TOKEN_INFO token_info; 41 | CK_RV res = f->C_GetTokenInfo(slotID, &token_info); 42 | if (res != CKR_OK) { 43 | printf("Fail C_GetTokenInfo, res=%ld", res); 44 | return res; 45 | } 46 | token_info.label[31] = '\0'; 47 | printf("Slot=%ld, token=%s\n", slotID, token_info.label); 48 | 49 | CK_SESSION_HANDLE session; 50 | res = f->C_OpenSession(slotID, (CK_FLAGS)0, NULL_PTR, NULL_PTR, &session); 51 | if (res != CKR_OK) { 52 | printf("Fail C_OpenSession, res=%ld", res); 53 | return res; 54 | } 55 | 56 | res = f->C_FindObjectsInit(session, NULL_PTR, 0); 57 | if (res != CKR_OK) { 58 | printf("Fail C_FindObjectsInit, res=%ld\n", res); 59 | return res; 60 | } 61 | 62 | CK_ULONG objectCount = 1; 63 | while (objectCount == 1) { 64 | CK_OBJECT_HANDLE obj; 65 | res = f->C_FindObjects(session, &obj, 1, &objectCount); 66 | if (res != CKR_OK) { 67 | printf("Fail C_FindObjects, res=%ld\n", res); 68 | return res; 69 | } 70 | if (objectCount == 0) { 71 | break; 72 | } 73 | 74 | CK_OBJECT_CLASS object_class; 75 | CK_OBJECT_CLASS key_type; 76 | CK_ATTRIBUTE attrs[1]; 77 | 78 | attrs[0].type = CKA_CLASS; 79 | attrs[0].pValue = &object_class; 80 | attrs[0].ulValueLen = sizeof(CK_OBJECT_CLASS); 81 | CK_RV res = f->C_GetAttributeValue(session, obj, attrs, 1); 82 | if (res != CKR_OK) { 83 | printf("Fail C_GetAttributeValue for CKA_CLASS, res=%ld\n", res); 84 | return res; 85 | } 86 | if (object_class != CKO_PRIVATE_KEY) { 87 | printf("Skipping object because it is not a private key.\n"); 88 | continue; 89 | } 90 | 91 | attrs[0].type = CKA_KEY_TYPE; 92 | attrs[0].pValue = &key_type; 93 | attrs[0].ulValueLen = sizeof(CK_OBJECT_CLASS); 94 | res = f->C_GetAttributeValue(session, obj, attrs, 1); 95 | if (res != CKR_OK) { 96 | printf("Fail C_GetAttributeValue for CKA_KEY_TYPE, res=%ld\n", res); 97 | return res; 98 | } 99 | 100 | if (key_type == CKK_RSA) { 101 | res = get_and_dump_attribute(f, session, obj, CKA_PUBLIC_EXPONENT, "exponent"); 102 | if (res != CKR_OK) { 103 | printf("Fail get_and_dump_attribute for attribute CKA_PUBLIC_EXPONENT\n"); 104 | return res; 105 | } 106 | 107 | res = get_and_dump_attribute(f, session, obj, CKA_MODULUS, "modulus"); 108 | if (res != CKR_OK) { 109 | printf("Fail get_and_dump_attribute for attribute CKA_MODULUS\n"); 110 | return res; 111 | } 112 | } else if (key_type == CKK_ECDSA) { 113 | res = get_and_dump_attribute(f, session, obj, CKA_EC_PARAMS, "group"); 114 | if (res != CKR_OK) { 115 | printf("Fail get_and_dump_attribute for attribute CKA_EC_PARAMS\n"); 116 | return res; 117 | } 118 | 119 | res = get_and_dump_attribute(f, session, obj, CKA_EC_POINT, "point"); 120 | if (res != CKR_OK) { 121 | printf("Fail get_and_dump_attribute for attribute CKA_EC_POINT\n"); 122 | return res; 123 | } 124 | } 125 | 126 | const unsigned char DATA[] = { 127 | 0xb5, 0xbb, 0x9d, 0x80, 0x14, 0xa0, 0xf9, 0xb1, 0xd6, 0x1e, 0x21, 0xe7, 128 | 0x96, 0xd7, 0x8d, 0xcc, 0xdf, 0x13, 0x52, 0xf2, 0x3c, 0xd3, 0x28, 0x12, 129 | 0xf4, 0x85, 0x0b, 0x87, 0x8a, 0xe4, 0x94, 0x4c 130 | }; 131 | 132 | CK_MECHANISM mechanism; 133 | if (key_type == CKK_RSA) { 134 | mechanism.mechanism = CKM_RSA_PKCS; 135 | } else if (key_type == CKK_ECDSA) { 136 | mechanism.mechanism = CKM_ECDSA; 137 | } 138 | res = f->C_SignInit(session, &mechanism, obj); 139 | if (res != CKR_OK) { 140 | printf("Fail C_SignInit, res=%ld\n", res); 141 | return res; 142 | } 143 | 144 | unsigned char *dgst = (unsigned char*)DATA; 145 | CK_ULONG siglen = 0; 146 | res = f->C_Sign(session, dgst, sizeof(DATA), NULL_PTR, &siglen); 147 | if (res != CKR_OK) { 148 | printf("Failed to call C_Sign to get signature size, res=%ld\n", res); 149 | return res; 150 | } 151 | 152 | unsigned char* sig = (unsigned char*)malloc(siglen); 153 | res = f->C_Sign(session, dgst, sizeof(DATA), sig, &siglen); 154 | if (res != CKR_OK) { 155 | printf("Fail C_Sign, res=%ld\n", res); 156 | return res; 157 | } 158 | dump_bytes("sig", sig, siglen); 159 | free(sig); 160 | } 161 | res = f->C_FindObjectsFinal(session); 162 | if (res != CKR_OK) { 163 | printf("Fail C_FindObjectsFinal, res=%ld\n", res); 164 | return res; 165 | } 166 | 167 | res = f->C_CloseSession(session); 168 | if (res != CKR_OK) { 169 | printf("Failed to call C_CloseSession, res=%ld", res); 170 | return res; 171 | } 172 | 173 | return CKR_OK; 174 | } 175 | 176 | int main(int argc, char** argv) { 177 | void* handle; 178 | CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); 179 | CK_FUNCTION_LIST* f; 180 | CK_RV res; 181 | 182 | handle = dlopen("./aws_kms_pkcs11.so", RTLD_NOW); 183 | if (handle == NULL) { 184 | printf("Fail to load aws_kms_pkcs11.so: %s\n", dlerror()); 185 | return 1; 186 | } 187 | getfunctionlist = (CK_RV (*)(CK_FUNCTION_LIST**)) dlsym(handle, "C_GetFunctionList"); 188 | if (getfunctionlist == NULL) { 189 | printf("dlsym(C_GetFunctionList) failed: %s", dlerror()); 190 | return 1; 191 | } 192 | 193 | res = (*getfunctionlist)(&f); 194 | if (res != CKR_OK) { 195 | printf("C_GetFunctionList failed: %lu", res); 196 | return 1; 197 | } 198 | 199 | res = f->C_Initialize(NULL_PTR); 200 | if (res != CKR_OK) { 201 | printf("Fail C_Initialize, res=%ld\n", res); 202 | return 1; 203 | } 204 | 205 | CK_SLOT_ID_PTR pSlotList = NULL_PTR; 206 | CK_ULONG slotCount; 207 | res = f->C_GetSlotList(CK_TRUE, NULL_PTR, &slotCount); 208 | if (res != CKR_OK) { 209 | printf("Fail C_GetSlotList, res=%ld\n", res); 210 | return 1; 211 | } 212 | 213 | pSlotList = (CK_SLOT_ID_PTR) malloc(slotCount * sizeof(CK_SLOT_ID)); 214 | if (pSlotList == NULL) { 215 | printf("Failed to allocate memory to store slot IDs."); 216 | return 1; 217 | } 218 | res = f->C_GetSlotList(CK_TRUE, pSlotList, &slotCount); 219 | if (res != CKR_OK) { 220 | printf("Fail C_GetSlotList, res=%ld\n", res); 221 | return 1; 222 | } 223 | 224 | for (size_t i = 0; i < slotCount; i++) { 225 | res = test_all_keys_in_slot(f, pSlotList[i]); 226 | if (res != CKR_OK) { 227 | printf("Failed to run test on slot %ld\n", pSlotList[i]); 228 | return 1; 229 | } 230 | } 231 | free(pSlotList); 232 | 233 | res = f->C_Finalize(NULL_PTR); 234 | if (res != CKR_OK) { 235 | printf("Fail C_Finalize, res=%ld\n", res); 236 | return 1; 237 | } 238 | 239 | printf("Test successful!\n"); 240 | dlclose(handle); 241 | return 0; 242 | } 243 | -------------------------------------------------------------------------------- /attributes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pkcs11_compat.h" 6 | #include "openssl_compat.h" 7 | #include "aws_kms_slot.h" 8 | 9 | static CK_RV copyAttribute(CK_VOID_PTR pDest, CK_ULONG_PTR pulDestLen, const void *pSrc, CK_ULONG ulSrcLen) 10 | { 11 | if (pulDestLen == NULL) { 12 | return CKR_ARGUMENTS_BAD; 13 | } 14 | if (pDest == NULL) { 15 | *pulDestLen = ulSrcLen; 16 | return CKR_OK; 17 | } 18 | if (*pulDestLen < ulSrcLen) { 19 | *pulDestLen = CK_UNAVAILABLE_INFORMATION; 20 | return CKR_BUFFER_TOO_SMALL; 21 | } 22 | memcpy(pDest, pSrc, ulSrcLen); 23 | *pulDestLen = ulSrcLen; 24 | 25 | return CKR_OK; 26 | } 27 | 28 | static CK_RV copyBoolAttribute(CK_VOID_PTR pDest, CK_ULONG_PTR pulDestLen, CK_BBOOL value) 29 | { 30 | return copyAttribute(pDest, pulDestLen, &value, sizeof(CK_BBOOL)); 31 | } 32 | 33 | static CK_RV copyBNAttribute(CK_VOID_PTR pDest, CK_ULONG_PTR pulDestLen, const BIGNUM *bn) 34 | { 35 | CK_ULONG bnLen = BN_num_bytes(bn); 36 | 37 | if (pulDestLen == NULL) { 38 | return CKR_ARGUMENTS_BAD; 39 | } 40 | if (pDest == NULL) { 41 | *pulDestLen = bnLen; 42 | return CKR_OK; 43 | } 44 | if (*pulDestLen < bnLen) { 45 | *pulDestLen = CK_UNAVAILABLE_INFORMATION; 46 | return CKR_BUFFER_TOO_SMALL; 47 | } 48 | BN_bn2bin(bn, (unsigned char*)pDest); 49 | *pulDestLen = bnLen; 50 | 51 | return CKR_OK; 52 | } 53 | 54 | CK_RV getCommonAttributeValue(AwsKmsSlot& slot, CK_ATTRIBUTE_TYPE attr, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen) { 55 | switch (attr) { 56 | case CKA_TOKEN: 57 | return copyBoolAttribute(pValue, pulValueLen, CK_TRUE); 58 | 59 | case CKA_ID: { 60 | string label = slot.GetKmsKeyId(); 61 | return copyAttribute(pValue, pulValueLen, label.c_str(), label.length()); 62 | } 63 | 64 | case CKA_LABEL: { 65 | string label = slot.GetLabel(); 66 | if (label.length() == 0) { 67 | label = slot.GetKmsKeyId(); 68 | } 69 | return copyAttribute(pValue, pulValueLen, label.c_str(), label.length()); 70 | } 71 | 72 | default: 73 | *pulValueLen = CK_UNAVAILABLE_INFORMATION; 74 | return CKR_ATTRIBUTE_TYPE_INVALID; 75 | } 76 | return CKR_OK; 77 | } 78 | 79 | CK_RV getKmsKeyAttributeValue(AwsKmsSlot& slot, CK_ATTRIBUTE_TYPE attr, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen) { 80 | /* Not *all* attributes need this but most of them do, so do it once here */ 81 | Aws::Utils::ByteBuffer key_data = slot.GetPublicKeyData(); 82 | 83 | switch (attr) { 84 | case CKA_CLASS: { 85 | CK_OBJECT_CLASS obj_class = key_data.GetLength() > 0 ? CKO_PRIVATE_KEY : CKO_DATA; 86 | return copyAttribute(pValue, pulValueLen, &obj_class, sizeof(CK_OBJECT_CLASS)); 87 | } 88 | 89 | case CKA_SENSITIVE: 90 | return copyBoolAttribute(pValue, pulValueLen, CK_TRUE); 91 | 92 | case CKA_EXTRACTABLE: 93 | return copyBoolAttribute(pValue, pulValueLen, CK_FALSE); 94 | 95 | case CKA_SIGN: 96 | if (key_data.GetLength() == 0) { 97 | return CKR_ATTRIBUTE_TYPE_INVALID; 98 | } 99 | return copyBoolAttribute(pValue, pulValueLen, CK_TRUE); 100 | 101 | case CKA_KEY_TYPE: { 102 | if (key_data.GetLength() == 0) { 103 | return CKR_ATTRIBUTE_TYPE_INVALID; 104 | } 105 | const unsigned char* pubkey_bytes = key_data.GetUnderlyingData(); 106 | EVP_PKEY* pkey = d2i_PUBKEY(NULL, &pubkey_bytes, key_data.GetLength()); 107 | if (pkey == NULL) { 108 | return CKR_FUNCTION_FAILED; 109 | } 110 | 111 | CK_OBJECT_CLASS key_type; 112 | switch (EVP_PKEY_base_id(pkey)) { 113 | case EVP_PKEY_RSA: 114 | key_type = CKK_RSA; 115 | break; 116 | case EVP_PKEY_EC: 117 | key_type = CKK_ECDSA; 118 | break; 119 | default: 120 | EVP_PKEY_free(pkey); 121 | return CKR_ATTRIBUTE_TYPE_INVALID; 122 | } 123 | EVP_PKEY_free(pkey); 124 | return copyAttribute(pValue, pulValueLen, &key_type, sizeof(CK_OBJECT_CLASS)); 125 | } 126 | 127 | case CKA_ALWAYS_AUTHENTICATE: 128 | if (key_data.GetLength() == 0) { 129 | return CKR_ATTRIBUTE_TYPE_INVALID; 130 | } 131 | return copyBoolAttribute(pValue, pulValueLen, CK_FALSE); 132 | 133 | case CKA_MODULUS: 134 | case CKA_PUBLIC_EXPONENT: { 135 | if (key_data.GetLength() == 0) { 136 | return CKR_ATTRIBUTE_TYPE_INVALID; 137 | } 138 | const unsigned char* pubkey_bytes = key_data.GetUnderlyingData(); 139 | EVP_PKEY* pkey = d2i_PUBKEY(NULL, &pubkey_bytes, key_data.GetLength()); 140 | if (pkey == NULL) { 141 | return CKR_FUNCTION_FAILED; 142 | } 143 | if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { 144 | EVP_PKEY_free(pkey); 145 | return CKR_ATTRIBUTE_TYPE_INVALID; 146 | } 147 | const RSA* rsa = EVP_PKEY_get0_RSA(pkey); 148 | const BIGNUM* bn; 149 | if (attr == CKA_MODULUS) { 150 | bn = RSA_get0_n(rsa); 151 | } else { 152 | bn = RSA_get0_e(rsa); 153 | } 154 | CK_RV ret = copyBNAttribute(pValue, pulValueLen, bn); 155 | EVP_PKEY_free(pkey); 156 | return ret; 157 | } 158 | 159 | case CKA_EC_POINT: { 160 | if (key_data.GetLength() == 0) { 161 | return CKR_ATTRIBUTE_TYPE_INVALID; 162 | } 163 | const unsigned char* pubkey_bytes = key_data.GetUnderlyingData(); 164 | EVP_PKEY *pkey = d2i_PUBKEY(NULL, &pubkey_bytes, key_data.GetLength()); 165 | if (pkey == NULL) { 166 | return CKR_FUNCTION_FAILED; 167 | } 168 | if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { 169 | EVP_PKEY_free(pkey); 170 | return CKR_ATTRIBUTE_TYPE_INVALID; 171 | } 172 | const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey); 173 | 174 | unsigned char* buffer = NULL; 175 | // ec_key argument isn't const on openssl 1.0.x 176 | size_t len = i2o_ECPublicKey((EC_KEY*)ec_key, &buffer); 177 | 178 | // Wrap the point in an ASN.1 octet string 179 | ASN1_OCTET_STRING* os = ASN1_STRING_new(); 180 | ASN1_OCTET_STRING_set(os, buffer, len); 181 | 182 | unsigned char* buffer2 = NULL; 183 | size_t len2 = i2d_ASN1_OCTET_STRING(os, &buffer2); 184 | 185 | CK_RV ret = copyAttribute(pValue, pulValueLen, buffer2, len2); 186 | 187 | ASN1_STRING_free(os); 188 | free(buffer); 189 | free(buffer2); 190 | EVP_PKEY_free(pkey); 191 | return ret; 192 | } 193 | 194 | case CKA_EC_PARAMS: { 195 | key_data = slot.GetPublicKeyData(); 196 | if (key_data.GetLength() == 0) { 197 | return CKR_ATTRIBUTE_TYPE_INVALID; 198 | } 199 | const unsigned char* pubkey_bytes = key_data.GetUnderlyingData(); 200 | EVP_PKEY *pkey = d2i_PUBKEY(NULL, &pubkey_bytes, key_data.GetLength()); 201 | if (pkey == NULL) { 202 | return CKR_FUNCTION_FAILED; 203 | } 204 | if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { 205 | EVP_PKEY_free(pkey); 206 | return CKR_ATTRIBUTE_TYPE_INVALID; 207 | } 208 | const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey); 209 | const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key); 210 | 211 | unsigned char *buffer = NULL; 212 | size_t len = i2d_ECPKParameters(ec_group, &buffer); 213 | 214 | CK_RV ret = copyAttribute(pValue, pulValueLen, buffer, len); 215 | 216 | free(buffer); 217 | EVP_PKEY_free(pkey); 218 | return ret; 219 | } 220 | default: 221 | return getCommonAttributeValue(slot, attr, pValue, pulValueLen); 222 | } 223 | 224 | return CKR_OK; 225 | } 226 | 227 | CK_RV do_get_raw_cert(const X509* cert, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen) { 228 | CK_BYTE_PTR buffer = NULL; 229 | CK_ULONG len = i2d_X509(OSSL_UNCONST(X509, cert), &buffer); 230 | CK_RV ret = CKR_FUNCTION_FAILED; 231 | if (len > 0) 232 | ret = copyAttribute(pValue, pulValueLen, buffer, len); 233 | OPENSSL_free(buffer); 234 | return ret; 235 | } 236 | 237 | CK_RV do_get_raw_name(const X509_NAME* name, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen) { 238 | CK_BYTE_PTR buffer = NULL; 239 | CK_ULONG len = i2d_X509_NAME(OSSL_UNCONST(X509_NAME, name), &buffer); 240 | CK_RV ret = CKR_FUNCTION_FAILED; 241 | if (len > 0) 242 | ret = copyAttribute(pValue, pulValueLen, buffer, len); 243 | OPENSSL_free(buffer); 244 | return ret; 245 | } 246 | 247 | CK_RV do_get_raw_integer(const ASN1_INTEGER* serial, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen) { 248 | CK_BYTE_PTR buffer = NULL; 249 | CK_ULONG len = i2d_ASN1_INTEGER(OSSL_UNCONST(ASN1_INTEGER, serial), &buffer); 250 | CK_RV ret = CKR_FUNCTION_FAILED; 251 | if (len > 0) 252 | ret = copyAttribute(pValue, pulValueLen, buffer, len); 253 | OPENSSL_free(buffer); 254 | return ret; 255 | } 256 | 257 | CK_RV getCertificateAttributeValue(AwsKmsSlot& slot, CK_ATTRIBUTE_TYPE attr, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen) { 258 | const X509* cert = slot.GetCertificate(); 259 | if (cert == NULL) { 260 | return CKR_OBJECT_HANDLE_INVALID; 261 | } 262 | 263 | switch (attr) { 264 | case CKA_CLASS: { 265 | CK_OBJECT_CLASS obj_class = CKO_CERTIFICATE; 266 | return copyAttribute(pValue, pulValueLen, &obj_class, sizeof(CK_OBJECT_CLASS)); 267 | } 268 | 269 | case CKA_PRIVATE: 270 | return copyBoolAttribute(pValue, pulValueLen, CK_FALSE); 271 | 272 | case CKA_CERTIFICATE_TYPE: { 273 | CK_ULONG type = CKC_X_509; 274 | return copyAttribute(pValue, pulValueLen, &type, sizeof(CK_ULONG)); 275 | } 276 | 277 | case CKA_MODIFIABLE: 278 | return copyBoolAttribute(pValue, pulValueLen, CK_FALSE); 279 | 280 | case CKA_TRUSTED: /* This should probably go into the JSON file */ 281 | return copyBoolAttribute(pValue, pulValueLen, CK_FALSE); 282 | 283 | case CKA_SUBJECT: 284 | return do_get_raw_name(X509_get_subject_name(OSSL_UNCONST(X509, cert)), pValue, pulValueLen); 285 | 286 | case CKA_ISSUER: 287 | return do_get_raw_name(X509_get_issuer_name(OSSL_UNCONST(X509, cert)), pValue, pulValueLen); 288 | 289 | case CKA_SERIAL_NUMBER: 290 | return do_get_raw_integer(X509_get0_serialNumber(OSSL_UNCONST(X509, cert)), pValue, pulValueLen); 291 | 292 | case CKA_VALUE: 293 | return do_get_raw_cert(cert, pValue, pulValueLen); 294 | default: 295 | return getCommonAttributeValue(slot, attr, pValue, pulValueLen); 296 | } 297 | return CKR_OK; 298 | } 299 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-kms-pkcs11 2 | 3 | This repository contains a PKCS#11 implementation that uses AWS KMS as its backend. This allows you to bridge software that requires PKCS#11 plugins (like codesigning or certificate management software) with AWS KMS for key storage and management. 4 | 5 | This implementation is not meant to be complete; it only implements enough of the PKCS#11 interface to enable signing with keys previously created in KMS. Functionality such as creating new keys and listing keys is not supported (you must set the key ID you want to use explicitly as noted in the configuration section below). 6 | 7 | # Examples 8 | 9 | ## PKCS#11 URIs 10 | 11 | This module exposes KMS keys under a single token and slot. You can configure the module to expose all your KMS keys, a select few, or even just one; see the configuration section below. If you are exposing more than one key, and your PKCS#11 consumer supports it, you can use PKCS#11 URIs to specify the key ID that you want to use. For example: 12 | 13 | ``` 14 | export PKCS11_MODULE_PATH=/usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so 15 | openssl pkeyutl -engine pkcs11 -sign -inkey pkcs11:token=my-signing-key -keyform engine -out foo.sig -in foo 16 | ``` 17 | 18 | The token label used in the URI should match the label used in the configuration (see below). If you have not specified a label then the first 32 characters of the key's ID will be used as the label. 19 | 20 | ## Use with libp11 (aka libengine-pkcs11-openssl) 21 | 22 | Note that this PKCS#11 provider allows for use of private keys without a "PIN". Previous versions of libp11 [did not allow](https://github.com/OpenSC/libp11/issues/242) the use of such keys. In particular, this version of libp11 is present in version of Ubuntu before focal, so make sure you are using libp11 >= 0.4.10. 23 | 24 | You can do some simple verification of this module with the pkcs11 engine by following this example: 25 | 26 | ``` 27 | AWS_KMS_PKCS11_DEBUG=1 openssl 28 | OpenSSL> engine pkcs11 -pre VERBOSE -pre MODULE_PATH:/usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so 29 | (pkcs11) pkcs11 engine 30 | [Success]: VERBOSE 31 | [Success]: MODULE_PATH:/build/aws-kms-pkcs11/aws_kms_pkcs11.so 32 | OpenSSL> 33 | OpenSSL> pkeyutl -engine pkcs11 -sign -inkey pkcs11:token=my-signing-key -keyform engine -out foo.sig -in foo 34 | engine "pkcs11" set. 35 | PKCS#11: Initializing the engine 36 | AWS_KMS: Debug enabled. 37 | AWS_KMS: Attempting to load config from path: /home/ihaken/.config/aws-kms-pkcs11/config.json 38 | AWS_KMS: Configured slots: 39 | AWS_KMS: dbafb7de-106e-4277-97fe-a7f5635516a5 40 | Found 1 slot 41 | Loading private key "pkcs11:token=my-signing-key" 42 | Looking in slot -1 for key: 43 | [0] no pin (my-signing-key) 44 | Found slot: 45 | Found token: my-signing-key 46 | AWS_KMS: Getting public key for key dbafb7de-106e-4277-97fe-a7f5635516a5 47 | AWS_KMS: Successfully fetched public key data. 48 | Found 1 private key: 49 | AWS_KMS: Successfully called KMS to do a signing operation. 50 | ``` 51 | 52 | If you have downloaded the public key from KMS to `my-signing-key.pub` you can verify the above signature with 53 | 54 | ``` 55 | openssl pkeyutl -in foo -verify -sigfile foo.sig -inkey my-signing-key.pub -pubin 56 | Signature Verified Successfully 57 | ``` 58 | 59 | This example using `pkeyutl` assumes you are using an EC key. 60 | If you are using an RSA key, append the `-pkeyopt digest:sha256` option to both the sign and verify steps. 61 | 62 | ## Generate a self-signed certificate 63 | 64 | This will create a self-signed certificate in `mycert.pem` using your KMS key. 65 | 66 | ``` 67 | $ CONFIG=" 68 | [req] 69 | distinguished_name=dn 70 | [ dn ] 71 | " 72 | 73 | $ PKCS11_MODULE_PATH=/usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so openssl req -config <(echo "$CONFIG") -x509 -key pkcs11:token=my-signing-key -keyform engine -engine pkcs11 -out mycert.pem -subj '/CN=mycert' -days 366 -addext basicConstraints=critical,CA:FALSE 74 | ``` 75 | 76 | ## Windows code signing 77 | 78 | Using [osslsigncode](https://github.com/mtrojnar/osslsigncode): 79 | 80 | ```bash 81 | osslsigncode sign -h sha256 \ 82 | -pkcs11engine /usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so \ 83 | -pkcs11module /usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so \ 84 | -certs mycert.pem -key 'pkcs11:token=my-signing-key' -in ~/foo.exe -out ~/foo-signed.exe 85 | ``` 86 | 87 | ## Signing RAUC bundles 88 | 89 | Since [RAUC](https://github.com/rauc/rauc) supports PKCS#11 keys, you can use your KMS key to sign RAUC bundles. 90 | 91 | ```bash 92 | RAUC_PKCS11_MODULE=/usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so rauc bundle --cert=mycert.pem --key='pkcs11:token=my-signing-key' input_dir/ my_bundle.raucb 93 | ``` 94 | 95 | ## SSH 96 | 97 | I'm not really sure why you'd want to do this, but you can! 98 | 99 | ```bash 100 | ~$ ssh-add -s /usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so 101 | Enter passphrase for PKCS#11: # Just press enter; no password is used 102 | Card added: /usr/lib/x86_64-linux-gnu/pkcs11/aws_kms_pkcs11.so 103 | ~$ ssh-add -L 104 | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLJqRBbRtYDvgNjK5xK1IcBaahVzbOyZULDjNpQ4VrWfmwthtIm4VEQLINherX8qx2hLaabvUfr7WLC5LDuyX6Q= dbafb7de-106e-4277-97fe-a7f5635516a5 105 | ~$ ssh-add -L >> ~/.ssh/authorized_keys 106 | ~$ ssh localhost 107 | Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-65-generic x86_64) 108 | 109 | Last login: Thu Nov 19 10:35:42 2020 110 | ~$ 111 | ``` 112 | 113 | ## P11Tool Configuration 114 | 115 | p11tool is a useful tool included as part of the gnutls-bin package on Debian-based systems. It can be installed with `apt install gnutls-bin`. After it is installed you can configure it to be aware of the aws-kms-pkcss11 module as follows: 116 | 117 | ``` 118 | mkdir -p "/etc/pkcs11/modules" 119 | touch "/etc/pkcs11/pkcs11.conf" 120 | cat >"/etc/pkcs11/modules/aws-kms-pkcs11.module" < -o -s -n my-cert-db -c my-signing-key -t my-signing-key 163 | ``` 164 | 165 | ## Localstack support 166 | 167 | If you want to use localstack to test, you can do so using the `AWS_ENDPOINT_URL` environment variable. To test that, just make sure localstack is running at and then there's an example of that: 168 | 169 | ```bash 170 | export AWS_ENDPOINT_URL=http://localhost:4566 171 | export AWS_SECRET_ACCESS_KEY=test 172 | export AWS_ACCESS_KEY_ID=test 173 | 174 | # make sure that the localstack KMS key is created 175 | awslocal kms create-key --description "mykey" --key-usage SIGN_VERIFY --key-spec RSA_4096 176 | # make sure the configuration on config.json is pointing to the correct key id 177 | 178 | openssl req -new -key pkcs11:token=MyImportedKey6 -keyform engine -engine pkcs11 -out mycert.csr 179 | ``` 180 | 181 | And you should see in localstack logs something like: 182 | 183 | ``` 184 | 2024-06-13T15:35:28.938 INFO --- [-functhread6] hypercorn.error : Running on https://0.0.0.0:4566 (CTRL + C to quit) 185 | 2024-06-13T15:35:28.938 INFO --- [-functhread6] hypercorn.error : Running on https://0.0.0.0:4566 (CTRL + C to quit) 186 | 2024-06-13T15:35:29.209 INFO --- [ MainThread] localstack.utils.bootstrap : Execution of "start_runtime_components" took 602.18ms 187 | Ready. 188 | 2024-06-13T15:38:04.414 INFO --- [ asgi_gw_0] localstack.request.aws : AWS kms.CreateKey => 200 189 | 2024-06-13T15:38:05.016 INFO --- [ asgi_gw_0] localstack.request.aws : AWS kms.GetPublicKey => 200 190 | 2024-06-13T15:38:05.214 INFO --- [ asgi_gw_0] localstack.request.aws : AWS kms.Sign => 200 191 | ``` 192 | 193 | # Configuration 194 | 195 | AWS credentials are pulled from the usual places (environment variables, ~/.aws/credentials, and IMDS). Further configuration is read from either `/etc/aws-kms-pkcs11/config.json` or `$XDG_CONFIG_HOME/aws-kms-pkcs11/config.json` (note that `XDG_CONFIG_HOME=$HOME/.config` by default). 196 | 197 | If you do not create any configuration, the module will list all KMS keys and make them available as "tokens" in the provider. The label on each token will be the first 32 characters of the key's ID. All requests will use the default AWS region. 198 | 199 | The following is an example configuration file: 200 | 201 | ``` 202 | { 203 | "slots": [ 204 | { 205 | "label": "my-signing-key", 206 | "kms_key_id": "dbafb7de-106e-4277-97fe-a7f5635516a5", 207 | "aws_region": "us-east-1", 208 | "certificate_path": "/etc/aws-kms-pkcs11/cert.pem" 209 | } 210 | ] 211 | } 212 | ``` 213 | 214 | The `slots` key is the only supported top-level attribute at the moment. This is a list of slot objects. The following keys are supported on each slot: 215 | 216 | | Key | Required | Example | Explanation | 217 | | --- | --- | --- | --- | 218 | | kms\_key\_id | Y | dbafb7de-106e-4277-97fe-a7f5635516a5 | The key id to use for this slot. | 219 | | label | N | my-signing-key | The token label to use for this slot; this is usually used when using a PKCS#11 URI. If not specified, the first 32 characters of the KMS key ID will be used as a label. | 220 | | aws\_region | N | us-west-2 | The AWS region where the above key resides. Uses the AWS default if not specified. | 221 | | certificate | N | MIIBMjCB2... | A base64-encoded DER-encoded X.509 certificate to make available as an object on this slot. This is useful for use-cases where a signing library expects both a certificate and key available on the PKCS#11 token. You can generate a certificate with this format with a command such as `openssl x509 -in mycert.pem -outform der \| openssl base64 -A` | 222 | | certificate\_path | N | /etc/aws-kms-pkcs11/mycert.pem | Same as "certificate" but refers to a PEM certificate on disk instead of embedding the certificate value into the config. | 223 | | certificate_arn | N | arn:aws:acm-pca:us-west-2:123456789876:certificate-authority/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx/certificate/xxxxxxxxxxxxxxxxxxxx | Same as "certificate" but refers to a PEM certificate in ACM-PCA. | 224 | | ca_arn | N | arn:aws:acm-pca:us-west-2:123456789876:certificate-authority/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx | Optionally provide the ARN of the CA authority that owns the certificate specified in "certificate_arn". If unspecified, extracted from certificate_arn | 225 | 226 | If you are encountering errors using this provider, try setting the `AWS_KMS_PKCS11_DEBUG` environment variable to a non-empty value. This should enable debug logging to stdout from the module. 227 | 228 | # Installation 229 | 230 | The easiest way to install the provider is to download the binary artifact from the GitHub releases page on this repository. Copy the `.so` to your pkcs11 directory (e.g. `/usr/lib/x86_64-linux-gnu/pkcs11`) and make sure to set it `chmod +x`. You should then create a config file as described above. 231 | 232 | # Building from source 233 | 234 | The Makefile in this repo tries to intuit the location of the various components and libraries it needs. This can be controlled by the following variables: 235 | 236 | `AWS_SDK_PATH` : Path to the AWS sdk 237 | `AWS_SDK_LIB_PATH` : Path to the AWS sdk libraries (optional) 238 | `PKCS11_INC` : Path to the pkcs11.h header file 239 | `JSON_C_INC` : Path to the json-c library headers 240 | 241 | Additionally these variables can be set to control the use of the AWS SDK static vs. dynamic libraries. By default the Makefile will use 242 | the static ones if available, otherwise the dynamic ones: 243 | 244 | `AWS_SDK_STATIC = y` : Force use of static libraries for both C and C++ 245 | `AWS_SDK_STATIC = n` : Force use of dynamic libraries for both C and C++ 246 | `AWS_SDK_C_STATIC = y` : Force use of static libraries for C 247 | `AWS_SDK_C_STATIC = n` : Force use of dynamic libraries for C 248 | `AWS_SDK_CPP_STATIC = y` : Force use of static libraries for C++ 249 | `AWS_SDK_CPP_STATIC = n` : Force use of dynamic libraries for C++ 250 | 251 | The variable `PKCS11_MOD_PATH` can be used to control the destination directory for `make install`. The variable `DESTDIR` can be used to install to a staging directory. 252 | 253 | The variable `AWS_SDK_USE_SYSTEM_PROXY` can be set to `y` to cause aws-kms-pkcs11 to use the HTTP proxy server set in the `HTTPS_PROXY` environment variable. This defaults to `n`, meaning the proxy settings from the environment are ignored by default. See [AWS Command Line Interface - Use an HTTP proxy](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-proxy.html) and [aws-sdk-cpp#2679](https://github.com/aws/aws-sdk-cpp/pull/2679) for further details. Note that this option was, as of this writing, added relatively recently (September 2023) and the library may therefore not compile with `AWS_SDK_USE_SYSTEM_PROXY=y`. 254 | -------------------------------------------------------------------------------- /aws_kms_pkcs11.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "pkcs11_compat.h" 17 | #include "openssl_compat.h" 18 | #include "attributes.h" 19 | #include "aws_kms_slot.h" 20 | #include "certificates.h" 21 | #include "debug.h" 22 | #include "util.h" 23 | #include "unsupported.h" 24 | 25 | using std::string; 26 | using std::vector; 27 | 28 | static_assert(sizeof(CK_SESSION_HANDLE) >= sizeof(void*), "Session handles are not big enough to hold a pointer to the session struct on this architecture"); 29 | static_assert(sizeof(CK_OBJECT_HANDLE) >= sizeof(void*), "Object handles are not big enough to hold a pointer to the session struct on this architecture"); 30 | 31 | static const CK_OBJECT_HANDLE PRIVATE_KEY_HANDLE = 1; 32 | static const CK_OBJECT_HANDLE CERTIFICATE_HANDLE = 2; 33 | static const CK_OBJECT_HANDLE FIRST_OBJECT_HANDLE = PRIVATE_KEY_HANDLE; 34 | static const CK_OBJECT_HANDLE LAST_OBJECT_HANDLE = CERTIFICATE_HANDLE; 35 | 36 | typedef struct _session { 37 | CK_SLOT_ID slot_id; 38 | CK_ATTRIBUTE_PTR find_objects_template; 39 | CK_ULONG find_objects_template_count; 40 | unsigned long find_objects_index; 41 | 42 | CK_MECHANISM_TYPE sign_mechanism; 43 | CK_RSA_PKCS_PSS_PARAMS pss_params; 44 | } CkSession; 45 | 46 | static Aws::SDKOptions options; 47 | static vector* slots = NULL; 48 | static vector* active_sessions = NULL; 49 | 50 | static CK_RV load_config(json_object** config) { 51 | vector config_paths; 52 | config_paths.push_back("/etc/aws-kms-pkcs11/config.json"); 53 | 54 | const char* xdg_config_home_cstr = getenv("XDG_CONFIG_HOME"); 55 | string xdg_config_home; 56 | if (xdg_config_home_cstr != NULL){ 57 | xdg_config_home = string(xdg_config_home_cstr); 58 | } else { 59 | const char* user_home = getenv("HOME"); 60 | if (user_home != NULL) { 61 | xdg_config_home = string(user_home) + "/.config"; 62 | } 63 | } 64 | if (xdg_config_home.length() > 0) { 65 | config_paths.push_back(xdg_config_home + "/aws-kms-pkcs11/config.json"); 66 | } 67 | 68 | std::reverse(config_paths.begin(), config_paths.end()); 69 | for (size_t i = 0; i < config_paths.size(); i++) { 70 | string path = config_paths.at(i); 71 | debug("Attempting to load config from path: %s", path.c_str()); 72 | 73 | FILE* f = fopen(path.c_str(), "r"); 74 | if (f == NULL) { 75 | debug("Skipping config because we couldn't open the file."); 76 | continue; 77 | } 78 | 79 | fseek(f, 0L, SEEK_END); 80 | size_t file_size = ftell(f); 81 | fseek(f, 0L, SEEK_SET); 82 | 83 | char* buffer = (char*)malloc(file_size); 84 | if (buffer == NULL) { 85 | fclose(f); 86 | return CKR_HOST_MEMORY; 87 | } 88 | 89 | size_t actual = fread(buffer, file_size, 1, f); 90 | fclose(f); 91 | if (actual != 1) { 92 | free(buffer); 93 | return CKR_FUNCTION_FAILED; 94 | } 95 | 96 | struct json_tokener* tok = json_tokener_new(); 97 | struct json_object* conf = json_tokener_parse_ex(tok, buffer, file_size); 98 | enum json_tokener_error errval = json_tokener_get_error(tok); 99 | json_tokener_free(tok); 100 | free(buffer); 101 | 102 | if (conf != NULL) { 103 | *config = conf; 104 | return CKR_OK; 105 | } else { 106 | debug("Failed to parse config %s: %s", path.c_str(), json_tokener_error_desc(errval)); 107 | } 108 | } 109 | *config = json_object_new_object(); 110 | return CKR_OK; 111 | } 112 | 113 | CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { 114 | CK_RV result = CKR_OK; 115 | 116 | debug_enabled = CK_FALSE; 117 | const char* debug_env_var = getenv("AWS_KMS_PKCS11_DEBUG"); 118 | if (debug_env_var != NULL) { 119 | if (strlen(debug_env_var) > 0) { 120 | debug_enabled = CK_TRUE; 121 | debug("Debug enabled."); 122 | } 123 | } 124 | 125 | Aws::SDKOptions options; 126 | Aws::InitAPI(options); 127 | 128 | json_object* config = NULL; 129 | CK_RV res = load_config(&config); 130 | if (res != CKR_OK || config == NULL) { 131 | debug("Failed to load config."); 132 | return res; 133 | } 134 | 135 | string glob_aws_region; 136 | struct json_object* val; 137 | 138 | if (json_object_object_get_ex(config, "aws_region", &val) && json_object_is_type(val, json_type_string)) { 139 | glob_aws_region = string(json_object_get_string(val)); 140 | } 141 | 142 | active_sessions = new vector(); 143 | slots = new vector(); 144 | struct json_object* slots_array; 145 | if (json_object_object_get_ex(config, "slots", &slots_array) && json_object_is_type(slots_array, json_type_array)) { 146 | for (size_t i = 0; i < (size_t)json_object_array_length(slots_array); i++) { 147 | struct json_object* slot_item = json_object_array_get_idx(slots_array, i); 148 | if (json_object_is_type(slot_item, json_type_object)) { 149 | string label; 150 | string kms_key_id; 151 | string aws_region = glob_aws_region; 152 | X509* certificate = NULL; 153 | if (json_object_object_get_ex(slot_item, "label", &val) && json_object_is_type(val, json_type_string)) { 154 | label = string(json_object_get_string(val)); 155 | } 156 | if (json_object_object_get_ex(slot_item, "kms_key_id", &val) && json_object_is_type(val, json_type_string)) { 157 | kms_key_id = string(json_object_get_string(val)); 158 | } 159 | if (json_object_object_get_ex(slot_item, "aws_region", &val) && json_object_is_type(val, json_type_string)) { 160 | aws_region = string(json_object_get_string(val)); 161 | } 162 | if (json_object_object_get_ex(slot_item, "certificate", &val) && json_object_is_type(val, json_type_string)) { 163 | debug("Parsing certificate for slot: %s", label.c_str()); 164 | certificate = parseCertificateFromB64Der(json_object_get_string(val)); 165 | if (certificate == NULL) { 166 | debug("Failed to parse certificate for slot: %s", label.c_str()); 167 | } 168 | } 169 | if (json_object_object_get_ex(slot_item, "certificate_path", &val) && json_object_is_type(val, json_type_string)) { 170 | const char* certificate_path = json_object_get_string(val); 171 | debug("Parsing certificate for slot %s from path %s", label.c_str(), certificate_path); 172 | certificate = parseCertificateFromFile(certificate_path); 173 | if (certificate == NULL) { 174 | debug("Failed to parse certificate_path for slot: %s", label.c_str()); 175 | } 176 | } 177 | if (json_object_object_get_ex(slot_item, "certificate_arn", &val) && json_object_is_type(val, json_type_string)) { 178 | const string certificate_arn = json_object_get_string(val); 179 | struct json_object *ca_val; 180 | string ca_arn; 181 | if (json_object_object_get_ex(slot_item, "ca_arn", &ca_val) && 182 | json_object_is_type(ca_val, json_type_string)) { 183 | ca_arn = json_object_get_string(ca_val); 184 | } else { 185 | size_t cert_pos = certificate_arn.find("/certificate/"); 186 | if (cert_pos == string::npos) { 187 | debug("ca_arn unspecified and failed to extract from %s\n", certificate_arn.c_str()); 188 | } else { 189 | ca_arn = certificate_arn.substr(0, cert_pos); 190 | } 191 | } 192 | debug("Parsing certificate for slot: %s ARN %s from CA %s", 193 | label.c_str(), certificate_arn.c_str(), ca_arn.c_str()); 194 | certificate = parseCertificateFromARN(ca_arn, certificate_arn, aws_region); 195 | if (certificate == NULL) { 196 | debug("Failed to parse certificate for slot: %s", label.c_str()); 197 | } 198 | } 199 | slots->push_back(AwsKmsSlot(label, kms_key_id, aws_region, certificate)); 200 | } 201 | } 202 | } 203 | json_object_put(config); 204 | 205 | if (slots->size() == 0) { 206 | debug("No KMS key ids configured; listing all keys."); 207 | Aws::Client::ClientConfiguration awsConfig = create_aws_config(glob_aws_region); 208 | 209 | Aws::KMS::KMSClient kms(awsConfig); 210 | Aws::KMS::Model::ListKeysRequest req; 211 | req.SetLimit(1000); 212 | bool has_more = true; 213 | while (has_more) { 214 | Aws::KMS::Model::ListKeysOutcome res = kms.ListKeys(req); 215 | if (!res.IsSuccess()) { 216 | debug("Got error from AWS list keys: %s", res.GetError().GetMessage().c_str()); 217 | result = CKR_FUNCTION_FAILED; 218 | break; 219 | } 220 | 221 | for (size_t i = 0; i < res.GetResult().GetKeys().size(); i++) { 222 | slots->push_back(AwsKmsSlot(string(), res.GetResult().GetKeys().at(i).GetKeyId(), string(), NULL)); 223 | } 224 | 225 | has_more = res.GetResult().GetTruncated(); 226 | if (has_more) { 227 | req.SetMarker(res.GetResult().GetNextMarker()); 228 | } 229 | } 230 | } 231 | 232 | if (slots->size() == 0) { 233 | debug("No slots were configured and no KMS keys could be listed via an API call."); 234 | result = CKR_FUNCTION_FAILED; 235 | } else { 236 | debug("Configured slots:"); 237 | for (size_t i = 0; i < slots->size(); i++) { 238 | debug(" %s", slots->at(i).GetKmsKeyId().c_str()); 239 | } 240 | } 241 | 242 | if (result != CKR_OK) { 243 | C_Finalize(NULL_PTR); 244 | } 245 | return result; 246 | } 247 | 248 | CK_RV C_Finalize(CK_VOID_PTR pReserved) { 249 | debug("Cleaning PKCS#11 provider."); 250 | 251 | if (slots != NULL) { 252 | for (size_t i = 0; i < slots->size(); i++) { 253 | AwsKmsSlot& slot = slots->at(i); 254 | X509* cert = const_cast (slot.GetCertificate()); 255 | if (cert != NULL) { 256 | X509_free(cert); 257 | } 258 | } 259 | 260 | delete slots; 261 | slots = NULL; 262 | } 263 | if (active_sessions != NULL) { 264 | if (active_sessions->size() > 0) { 265 | debug("There are still active sessions!"); 266 | } 267 | delete active_sessions; 268 | active_sessions = NULL; 269 | } 270 | 271 | Aws::SDKOptions options; 272 | Aws::ShutdownAPI(options); 273 | 274 | return CKR_OK; 275 | } 276 | 277 | CK_RV C_GetInfo(CK_INFO_PTR pInfo) { 278 | if (pInfo == NULL_PTR) { 279 | return CKR_ARGUMENTS_BAD; 280 | } 281 | memset(pInfo, 0, sizeof(*pInfo)); 282 | pInfo->cryptokiVersion.major = 2; 283 | pInfo->cryptokiVersion.minor = 4; 284 | return CKR_OK; 285 | } 286 | 287 | CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { 288 | if (pulCount == NULL_PTR) { 289 | return CKR_ARGUMENTS_BAD; 290 | } 291 | if (pSlotList != NULL_PTR) { 292 | if (*pulCount < slots->size()) { 293 | return CKR_BUFFER_TOO_SMALL; 294 | } 295 | for (size_t i = 0; i < slots->size(); i++) { 296 | pSlotList[i] = i; 297 | } 298 | } 299 | *pulCount = slots->size(); 300 | return CKR_OK; 301 | } 302 | 303 | CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { 304 | if (slotID < 0 || slotID >= slots->size()) { 305 | return CKR_SLOT_ID_INVALID; 306 | } 307 | if (pInfo == NULL_PTR) { 308 | return CKR_ARGUMENTS_BAD; 309 | } 310 | 311 | memset(pInfo, 0, sizeof(*pInfo)); 312 | pInfo->flags = CKF_TOKEN_PRESENT; 313 | return CKR_OK; 314 | } 315 | 316 | CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { 317 | if (slotID < 0 || slotID >= slots->size()) { 318 | return CKR_SLOT_ID_INVALID; 319 | } 320 | if (pInfo == NULL_PTR) { 321 | return CKR_ARGUMENTS_BAD; 322 | } 323 | AwsKmsSlot& slot = slots->at(slotID); 324 | 325 | memset(pInfo, 0, sizeof(*pInfo)); 326 | pInfo->flags = CKF_TOKEN_INITIALIZED; 327 | 328 | string label = slot.GetLabel(); 329 | if (label.length() == 0) { 330 | label = slot.GetKmsKeyId(); 331 | } 332 | static_assert(sizeof(pInfo->label) == 32); 333 | size_t label_len = label.length(); 334 | if (label_len > 32) { 335 | label_len = 32; 336 | } 337 | memset(pInfo->label, ' ', 32); 338 | memcpy(pInfo->label, label.c_str(), label_len); 339 | static_assert(sizeof(pInfo->manufacturerID) == 32); 340 | memset(pInfo->manufacturerID, ' ', 32); 341 | memcpy(pInfo->manufacturerID, "aws_kms", 7); 342 | static_assert(sizeof(pInfo->model) == 16); 343 | memset(pInfo->model, ' ', 16); 344 | memcpy(pInfo->model, "0", 1); 345 | static_assert(sizeof(pInfo->serialNumber) == 16); 346 | memset(pInfo->serialNumber, ' ', 16); 347 | memcpy(pInfo->serialNumber, "0", 1); 348 | return CKR_OK; 349 | } 350 | 351 | CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { 352 | return CKR_OK; 353 | } 354 | 355 | CK_RV C_Logout(CK_SESSION_HANDLE hSession) { 356 | return CKR_OK; 357 | } 358 | 359 | CK_RV C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR phSession) { 360 | if (slotID < 0 || slotID >= slots->size()) { 361 | return CKR_SLOT_ID_INVALID; 362 | } 363 | 364 | CkSession* session = (CkSession*)malloc(sizeof(CkSession)); 365 | if (session == NULL) { 366 | return CKR_HOST_MEMORY; 367 | } 368 | session->slot_id = slotID; 369 | 370 | *phSession = (CK_SESSION_HANDLE)session; 371 | return CKR_OK; 372 | } 373 | 374 | CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { 375 | CkSession *session = (CkSession*)hSession; 376 | if (session == NULL) { 377 | return CKR_SESSION_HANDLE_INVALID; 378 | } 379 | for (auto it = active_sessions->begin(); it != active_sessions->end(); ) { 380 | if (*it == session) { 381 | active_sessions->erase(it); 382 | } else { 383 | it++; 384 | } 385 | } 386 | free(session); 387 | return CKR_OK; 388 | } 389 | 390 | CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { 391 | for (auto it = active_sessions->begin(); it != active_sessions->end(); ) { 392 | CkSession *session = *it; 393 | if (session->slot_id == slotID) { 394 | free(session); 395 | active_sessions->erase(it); 396 | } else { 397 | it++; 398 | } 399 | } 400 | return CKR_FUNCTION_FAILED; 401 | } 402 | 403 | CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { 404 | CkSession *session = (CkSession*)hSession; 405 | if (session == NULL) { 406 | return CKR_SESSION_HANDLE_INVALID; 407 | } 408 | memset(pInfo, 0, sizeof(*pInfo)); 409 | pInfo->slotID = session->slot_id; 410 | pInfo->state = CKS_RW_USER_FUNCTIONS; 411 | pInfo->flags = CKF_RW_SESSION | CKF_SERIAL_SESSION; 412 | return CKR_OK; 413 | } 414 | 415 | CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { 416 | if (pInfo == NULL) { 417 | return CKR_ARGUMENTS_BAD; 418 | } 419 | AwsKmsSlot& slot = slots->at(slotID); 420 | CK_ULONG keySize = 0; 421 | switch (type) { 422 | case CKM_RSA_PKCS: 423 | switch(slot.GetKeySpec()) { 424 | case Aws::KMS::Model::KeySpec::RSA_2048: 425 | keySize = 2048; 426 | break; 427 | case Aws::KMS::Model::KeySpec::RSA_3072: 428 | keySize = 3072; 429 | break; 430 | case Aws::KMS::Model::KeySpec::RSA_4096: 431 | keySize = 4096; 432 | break; 433 | default: 434 | // invalid combination of mechanism and KMS key spec 435 | return CKR_MECHANISM_INVALID; 436 | } 437 | break; 438 | case CKM_ECDSA: 439 | switch(slot.GetKeySpec()) { 440 | case Aws::KMS::Model::KeySpec::ECC_NIST_P256: 441 | keySize = 256; 442 | break; 443 | case Aws::KMS::Model::KeySpec::ECC_NIST_P384: 444 | keySize = 384; 445 | break; 446 | case Aws::KMS::Model::KeySpec::ECC_NIST_P521: 447 | keySize = 521; 448 | break; 449 | default: 450 | // invalid combination of mechanism and KMS key spec 451 | return CKR_MECHANISM_INVALID; 452 | } 453 | break; 454 | default: 455 | return CKR_MECHANISM_INVALID; 456 | } 457 | pInfo->ulMinKeySize = keySize; 458 | pInfo->ulMaxKeySize = keySize; 459 | pInfo->flags = CKF_SIGN; 460 | return CKR_OK; 461 | } 462 | 463 | CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { 464 | if (pulCount == NULL_PTR) { 465 | return CKR_ARGUMENTS_BAD; 466 | } 467 | if (pMechanismList != NULL) { 468 | pMechanismList[0] = CKM_RSA_PKCS; 469 | pMechanismList[1] = CKM_ECDSA; 470 | } 471 | *pulCount = 2; 472 | return CKR_OK; 473 | } 474 | 475 | CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { 476 | CkSession *session = (CkSession*)hSession; 477 | if (session == NULL) { 478 | return CKR_SESSION_HANDLE_INVALID; 479 | } 480 | 481 | session->find_objects_template = (CK_ATTRIBUTE*)malloc(sizeof(CK_ATTRIBUTE) * ulCount); 482 | for (CK_ULONG i = 0; i < ulCount; i++) { 483 | session->find_objects_template[i].type = pTemplate[i].type; 484 | session->find_objects_template[i].pValue = malloc(pTemplate[i].ulValueLen); 485 | memcpy(session->find_objects_template[i].pValue, pTemplate[i].pValue, pTemplate[i].ulValueLen); 486 | session->find_objects_template[i].ulValueLen = pTemplate[i].ulValueLen; 487 | } 488 | session->find_objects_template_count = ulCount; 489 | session->find_objects_index = FIRST_OBJECT_HANDLE ; 490 | 491 | return CKR_OK; 492 | } 493 | 494 | static CK_BBOOL has_object(AwsKmsSlot& slot, CK_OBJECT_HANDLE idx) { 495 | switch (idx) { 496 | case PRIVATE_KEY_HANDLE: 497 | return slot.GetPublicKeyData().GetLength() > 0; 498 | break; 499 | case CERTIFICATE_HANDLE: 500 | return slot.GetCertificate() != NULL; 501 | break; 502 | } 503 | 504 | return CK_FALSE; 505 | } 506 | 507 | static CK_RV getAttributeForObject(AwsKmsSlot& slot, CK_OBJECT_HANDLE idx, CK_ATTRIBUTE_TYPE attr, CK_VOID_PTR pValue, CK_ULONG_PTR pulValueLen) { 508 | switch (idx) { 509 | case PRIVATE_KEY_HANDLE: 510 | return getKmsKeyAttributeValue(slot, attr, pValue, pulValueLen); 511 | case CERTIFICATE_HANDLE: 512 | return getCertificateAttributeValue(slot, attr, pValue, pulValueLen); 513 | } 514 | 515 | return CKR_OBJECT_HANDLE_INVALID; 516 | } 517 | 518 | static CK_BBOOL matches_template(CkSession* session, AwsKmsSlot& slot, CK_OBJECT_HANDLE idx) { 519 | unsigned char* buffer = NULL; 520 | CK_ULONG buffer_size = 0; 521 | CK_RV res; 522 | 523 | for (CK_ULONG i = 0; i < session->find_objects_template_count; i++) { 524 | CK_ATTRIBUTE attr = session->find_objects_template[i]; 525 | 526 | // Pull the real attribute value 527 | res = getAttributeForObject(slot, idx, attr.type, NULL_PTR, &buffer_size); 528 | if (res != CKR_OK) { 529 | return CK_FALSE; 530 | } 531 | if (buffer_size != attr.ulValueLen) { 532 | return CK_FALSE; 533 | } 534 | buffer = (unsigned char*)malloc(buffer_size); 535 | if (buffer == NULL) { 536 | return CKR_HOST_MEMORY; 537 | } 538 | res = getAttributeForObject(slot, idx, attr.type, buffer, &buffer_size); 539 | if (res != CKR_OK) { 540 | return CK_FALSE; 541 | } 542 | 543 | // Special case for CKA_CLASS because we want to match CKO_PUBLIC_KEY even though we have a CKO_PRIVATE_KEY 544 | if (attr.type == CKA_CLASS) { 545 | CK_OBJECT_CLASS match = *((CK_OBJECT_CLASS*)attr.pValue); 546 | CK_OBJECT_CLASS actual = *((CK_OBJECT_CLASS*)buffer); 547 | if (match == CKO_PUBLIC_KEY && (actual == CKO_PUBLIC_KEY || actual == CKO_PRIVATE_KEY)) { 548 | free(buffer); 549 | continue; 550 | } 551 | } 552 | 553 | // Otherwise require exact match 554 | if (memcmp(buffer, attr.pValue, buffer_size) != 0) { 555 | free(buffer); 556 | return CK_FALSE; 557 | } 558 | free(buffer); 559 | } 560 | return CK_TRUE; 561 | } 562 | 563 | CK_RV C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) { 564 | CkSession *session = (CkSession*)hSession; 565 | if (session == NULL) { 566 | return CKR_SESSION_HANDLE_INVALID; 567 | } 568 | AwsKmsSlot& slot = slots->at(session->slot_id); 569 | 570 | if (ulMaxObjectCount == 0 || session->find_objects_index > LAST_OBJECT_HANDLE) { 571 | *pulObjectCount = 0; 572 | return CKR_OK; 573 | } 574 | 575 | size_t found_objects = 0; 576 | while (found_objects < ulMaxObjectCount && session->find_objects_index <= LAST_OBJECT_HANDLE) { 577 | if (has_object(slot, session->find_objects_index)) { 578 | if (matches_template(session, slot, session->find_objects_index)) { 579 | phObject[found_objects] = session->find_objects_index; 580 | found_objects += 1; 581 | } 582 | } 583 | session->find_objects_index += 1; 584 | } 585 | 586 | *pulObjectCount = found_objects; 587 | return CKR_OK; 588 | } 589 | 590 | CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession) { 591 | CkSession *session = (CkSession*)hSession; 592 | if (session == NULL) { 593 | return CKR_SESSION_HANDLE_INVALID; 594 | } 595 | 596 | for (CK_ULONG i = 0; i < session->find_objects_template_count; i++) { 597 | free(session->find_objects_template[i].pValue); 598 | } 599 | free(session->find_objects_template); 600 | 601 | session->find_objects_template = NULL; 602 | session->find_objects_template_count = 0; 603 | session->find_objects_index = 0; 604 | return CKR_OK; 605 | } 606 | 607 | CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { 608 | CkSession *session = (CkSession*)hSession; 609 | if (session == NULL) { 610 | return CKR_SESSION_HANDLE_INVALID; 611 | } 612 | if (pTemplate == NULL_PTR) { 613 | return CKR_ARGUMENTS_BAD; 614 | } 615 | 616 | if (hObject < FIRST_OBJECT_HANDLE || hObject > LAST_OBJECT_HANDLE) { 617 | return CKR_OBJECT_HANDLE_INVALID; 618 | } 619 | AwsKmsSlot& slot = slots->at(session->slot_id); 620 | if (!has_object(slot, hObject)) { 621 | return CKR_OBJECT_HANDLE_INVALID; 622 | } 623 | 624 | for (CK_ULONG i = 0; i < ulCount; i++) { 625 | CK_RV res = getAttributeForObject(slot, hObject, pTemplate[i].type, pTemplate[i].pValue, &pTemplate[i].ulValueLen); 626 | if (res != CKR_OK) { 627 | return res; 628 | } 629 | } 630 | return CKR_OK; 631 | } 632 | 633 | CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { 634 | CkSession *session = (CkSession*)hSession; 635 | if (session == NULL) { 636 | return CKR_SESSION_HANDLE_INVALID; 637 | } 638 | if (pMechanism == NULL_PTR) { 639 | return CKR_ARGUMENTS_BAD; 640 | } 641 | if (hKey != PRIVATE_KEY_HANDLE) { 642 | return CKR_OBJECT_HANDLE_INVALID; 643 | } 644 | session->sign_mechanism = pMechanism->mechanism; 645 | 646 | if (pMechanism->mechanism == CKM_RSA_PKCS_PSS && pMechanism->ulParameterLen == sizeof(CK_RSA_PKCS_PSS_PARAMS)) { 647 | memcpy(&session->pss_params, pMechanism->pParameter, pMechanism->ulParameterLen); 648 | } else { 649 | memset(&session->pss_params, 0, sizeof(CK_RSA_PKCS_PSS_PARAMS)); 650 | } 651 | 652 | return CKR_OK; 653 | } 654 | 655 | CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { 656 | return CKR_OK; 657 | } 658 | 659 | CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { 660 | *pulSignatureLen = 0; 661 | return CKR_OK; 662 | } 663 | 664 | static const unsigned char rsa_id_sha256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; 665 | static const unsigned char rsa_id_sha384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; 666 | static const unsigned char rsa_id_sha512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; 667 | static CK_BBOOL has_prefix(CK_BYTE_PTR pData, CK_ULONG ulDataLen, const unsigned char* prefix, size_t prefixLen) { 668 | if (ulDataLen < sizeof(prefix)) { 669 | return CK_FALSE; 670 | } 671 | for (size_t i = 0; i < sizeof(prefix); i++) { 672 | if (pData[i] != prefix[i]) { 673 | return CK_FALSE; 674 | } 675 | } 676 | return CK_TRUE; 677 | } 678 | 679 | CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { 680 | CkSession *session = (CkSession*)hSession; 681 | if (session == NULL) { 682 | return CKR_SESSION_HANDLE_INVALID; 683 | } 684 | 685 | if (pData == NULL_PTR || pulSignatureLen == NULL_PTR) { 686 | debug("Null pointers in C_Sign"); 687 | return CKR_ARGUMENTS_BAD; 688 | } 689 | 690 | AwsKmsSlot& slot = slots->at(session->slot_id); 691 | Aws::Utils::ByteBuffer key_data = slot.GetPublicKeyData(); 692 | if (key_data.GetLength() == 0) { 693 | debug("0 key length in C_Sign"); 694 | return CKR_ARGUMENTS_BAD; 695 | } 696 | 697 | size_t sig_size; 698 | const EC_KEY* ec_key; 699 | const RSA* rsa; 700 | const unsigned char* pubkey_bytes = key_data.GetUnderlyingData(); 701 | EVP_PKEY* pkey = d2i_PUBKEY(NULL, &pubkey_bytes, key_data.GetLength()); 702 | 703 | int key_type = EVP_PKEY_base_id(pkey); 704 | switch (key_type) { 705 | case EVP_PKEY_RSA: 706 | rsa = EVP_PKEY_get0_RSA(pkey); 707 | sig_size = BN_num_bytes(RSA_get0_n(rsa)); 708 | break; 709 | case EVP_PKEY_EC: 710 | ec_key = EVP_PKEY_get0_EC_KEY(pkey); 711 | sig_size = ECDSA_size(ec_key); 712 | break; 713 | default: 714 | EVP_PKEY_free(pkey); 715 | return CKR_FUNCTION_FAILED; 716 | 717 | } 718 | EVP_PKEY_free(pkey); 719 | pkey = NULL; 720 | 721 | if (pSignature == NULL_PTR) { 722 | *pulSignatureLen = sig_size; 723 | return CKR_OK; 724 | } 725 | 726 | Aws::KMS::Model::SignRequest req; 727 | req.SetKeyId(slot.GetKmsKeyId()); 728 | switch (session->sign_mechanism) { 729 | case CKM_ECDSA: 730 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData, ulDataLen))); 731 | req.SetMessageType(Aws::KMS::Model::MessageType::DIGEST); 732 | switch (slot.GetKeySpec()) { 733 | case Aws::KMS::Model::KeySpec::ECC_NIST_P256: 734 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::ECDSA_SHA_256); 735 | break; 736 | case Aws::KMS::Model::KeySpec::ECC_NIST_P384: 737 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::ECDSA_SHA_384); 738 | break; 739 | case Aws::KMS::Model::KeySpec::ECC_NIST_P521: 740 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::ECDSA_SHA_512); 741 | break; 742 | default: 743 | debug("Unsupported EC key spec: %d", slot.GetKeySpec()); 744 | return CKR_ARGUMENTS_BAD; 745 | } 746 | break; 747 | case CKM_RSA_PKCS_PSS: 748 | if (session->pss_params.hashAlg == CKM_SHA256) { 749 | if (ulDataLen > 32) { 750 | debug("Data too large (%d) for requested PSS hash algorithm %d", ulDataLen, session->pss_params.hashAlg); 751 | return CKR_ARGUMENTS_BAD; 752 | } 753 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PSS_SHA_256); 754 | } else if (session->pss_params.hashAlg == CKM_SHA384) { 755 | if (ulDataLen > 48) { 756 | debug("Data too large (%d) for requested PSS hash algorithm %d", ulDataLen, session->pss_params.hashAlg); 757 | return CKR_ARGUMENTS_BAD; 758 | } 759 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PSS_SHA_384); 760 | } else if (session->pss_params.hashAlg == CKM_SHA512) { 761 | if (ulDataLen > 64) { 762 | debug("Data too large (%d) for requested PSS hash algorithm %d", ulDataLen, session->pss_params.hashAlg); 763 | return CKR_ARGUMENTS_BAD; 764 | } 765 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PSS_SHA_512); 766 | } else { 767 | debug("Unsupported PSS hash algorithm: %d", session->pss_params.hashAlg); 768 | return CKR_ARGUMENTS_BAD; 769 | } 770 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData, ulDataLen))); 771 | req.SetMessageType(Aws::KMS::Model::MessageType::DIGEST); 772 | break; 773 | case CKM_RSA_PKCS: 774 | if (has_prefix(pData, ulDataLen, rsa_id_sha256, sizeof(rsa_id_sha256))) { 775 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData + sizeof(rsa_id_sha256), ulDataLen - sizeof(rsa_id_sha256)))); 776 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PKCS1_V1_5_SHA_256); 777 | } else if (has_prefix(pData, ulDataLen, rsa_id_sha384, sizeof(rsa_id_sha384))) { 778 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData + sizeof(rsa_id_sha384), ulDataLen - sizeof(rsa_id_sha384)))); 779 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PKCS1_V1_5_SHA_384); 780 | } else if (has_prefix(pData, ulDataLen, rsa_id_sha512, sizeof(rsa_id_sha512))) { 781 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData + sizeof(rsa_id_sha512), ulDataLen - sizeof(rsa_id_sha512)))); 782 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PKCS1_V1_5_SHA_512); 783 | } else if (ulDataLen <= 32) { 784 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData, ulDataLen))); 785 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PKCS1_V1_5_SHA_256); 786 | } else if (ulDataLen <= 48) { 787 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData, ulDataLen))); 788 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PKCS1_V1_5_SHA_384); 789 | } else if (ulDataLen <= 64) { 790 | req.SetMessage(Aws::Utils::CryptoBuffer(Aws::Utils::ByteBuffer(pData, ulDataLen))); 791 | req.SetSigningAlgorithm(Aws::KMS::Model::SigningAlgorithmSpec::RSASSA_PKCS1_V1_5_SHA_512); 792 | } else { 793 | debug("Invalid data length for RSA signature: %d", ulDataLen); 794 | return CKR_ARGUMENTS_BAD; 795 | } 796 | req.SetMessageType(Aws::KMS::Model::MessageType::DIGEST); 797 | break; 798 | default: 799 | debug("Bad sign mechanism: %d", session->sign_mechanism); 800 | return CKR_ARGUMENTS_BAD; 801 | } 802 | 803 | Aws::Client::ClientConfiguration awsConfig = create_aws_config(slot.GetAwsRegion()); 804 | 805 | Aws::KMS::KMSClient kms(awsConfig); 806 | Aws::KMS::Model::SignOutcome res = kms.Sign(req); 807 | if (!res.IsSuccess()) { 808 | debug("Error signing: %s", res.GetError().GetMessage().c_str()); 809 | return CKR_FUNCTION_FAILED; 810 | } else { 811 | debug("Successfully called KMS to do a signing operation."); 812 | } 813 | Aws::KMS::Model::SignResult response = res.GetResult(); 814 | 815 | if (key_type == EVP_PKEY_EC) { 816 | const unsigned char* sigbytes = response.GetSignature().GetUnderlyingData(); 817 | ECDSA_SIG* sig = d2i_ECDSA_SIG(NULL, &sigbytes, response.GetSignature().GetLength()); 818 | if (sig == NULL) { 819 | return CKR_FUNCTION_FAILED; 820 | } 821 | const BIGNUM* r = ECDSA_SIG_get0_r(sig); 822 | const BIGNUM* s = ECDSA_SIG_get0_s(sig); 823 | 824 | if ((size_t)BN_num_bytes(r) + (size_t)BN_num_bytes(s) > sig_size) { 825 | return CKR_FUNCTION_FAILED; 826 | } 827 | int pos = BN_bn2bin(r, pSignature); 828 | pos += BN_bn2bin(s, pSignature + pos); 829 | *pulSignatureLen = pos; 830 | ECDSA_SIG_free(sig); 831 | } else { 832 | if (response.GetSignature().GetLength() > sig_size) { 833 | return CKR_FUNCTION_FAILED; 834 | } 835 | memcpy(pSignature, response.GetSignature().GetUnderlyingData(), response.GetSignature().GetLength()); 836 | *pulSignatureLen = response.GetSignature().GetLength(); 837 | } 838 | 839 | return CKR_OK; 840 | } 841 | 842 | CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { 843 | static CK_FUNCTION_LIST function_list = { 844 | version: { 845 | major: 0, 846 | minor: 0 847 | }, 848 | C_Initialize: C_Initialize, 849 | C_Finalize: C_Finalize, 850 | C_GetInfo: C_GetInfo, 851 | C_GetFunctionList: C_GetFunctionList, 852 | C_GetSlotList: C_GetSlotList, 853 | C_GetSlotInfo: C_GetSlotInfo, 854 | C_GetTokenInfo: C_GetTokenInfo, 855 | C_GetMechanismList: C_GetMechanismList, 856 | C_GetMechanismInfo: C_GetMechanismInfo, 857 | C_InitToken: C_InitToken, 858 | C_InitPIN: C_InitPIN, 859 | C_SetPIN: C_SetPIN, 860 | C_OpenSession: C_OpenSession, 861 | C_CloseSession: C_CloseSession, 862 | C_CloseAllSessions: C_CloseAllSessions, 863 | C_GetSessionInfo: C_GetSessionInfo, 864 | C_GetOperationState: C_GetOperationState, 865 | C_SetOperationState: C_SetOperationState, 866 | C_Login: C_Login, 867 | C_Logout: C_Logout, 868 | C_CreateObject: C_CreateObject, 869 | C_CopyObject: C_CopyObject, 870 | C_DestroyObject: C_DestroyObject, 871 | C_GetObjectSize: C_GetObjectSize, 872 | C_GetAttributeValue: C_GetAttributeValue, 873 | C_SetAttributeValue: C_SetAttributeValue, 874 | C_FindObjectsInit: C_FindObjectsInit, 875 | C_FindObjects: C_FindObjects, 876 | C_FindObjectsFinal: C_FindObjectsFinal, 877 | C_EncryptInit: C_EncryptInit, 878 | C_Encrypt: C_Encrypt, 879 | C_EncryptUpdate: C_EncryptUpdate, 880 | C_EncryptFinal: C_EncryptFinal, 881 | C_DecryptInit: C_DecryptInit, 882 | C_Decrypt: C_Decrypt, 883 | C_DecryptUpdate: C_DecryptUpdate, 884 | C_DecryptFinal: C_DecryptFinal, 885 | C_DigestInit: C_DigestInit, 886 | C_Digest: C_Digest, 887 | C_DigestUpdate: C_DigestUpdate, 888 | C_DigestKey: C_DigestKey, 889 | C_DigestFinal: C_DigestFinal, 890 | C_SignInit: C_SignInit, 891 | C_Sign: C_Sign, 892 | C_SignUpdate: C_SignUpdate, 893 | C_SignFinal: C_SignFinal, 894 | C_SignRecoverInit: C_SignRecoverInit, 895 | C_SignRecover: C_SignRecover, 896 | C_VerifyInit: C_VerifyInit, 897 | C_Verify: C_Verify, 898 | C_VerifyUpdate: C_VerifyUpdate, 899 | C_VerifyFinal: C_VerifyFinal, 900 | C_VerifyRecoverInit: C_VerifyRecoverInit, 901 | C_VerifyRecover: C_VerifyRecover, 902 | C_DigestEncryptUpdate: C_DigestEncryptUpdate, 903 | C_DecryptDigestUpdate: C_DecryptDigestUpdate, 904 | C_SignEncryptUpdate: C_SignEncryptUpdate, 905 | C_DecryptVerifyUpdate: C_DecryptVerifyUpdate, 906 | C_GenerateKey: C_GenerateKey, 907 | C_GenerateKeyPair: C_GenerateKeyPair, 908 | C_WrapKey: C_WrapKey, 909 | C_UnwrapKey: C_UnwrapKey, 910 | C_DeriveKey: C_DeriveKey, 911 | C_SeedRandom: C_SeedRandom, 912 | C_GenerateRandom: C_GenerateRandom, 913 | C_GetFunctionStatus: C_GetFunctionStatus, 914 | C_CancelFunction: C_CancelFunction, 915 | C_WaitForSlotEvent: C_WaitForSlotEvent, 916 | }; 917 | 918 | if (ppFunctionList == NULL_PTR) { 919 | return CKR_ARGUMENTS_BAD; 920 | } 921 | 922 | *ppFunctionList = &function_list; 923 | return CKR_OK; 924 | } 925 | --------------------------------------------------------------------------------