From 327fb0976cff5a9e2aba84391024b11f4311f4d6 Mon Sep 17 00:00:00 2001 From: arsalansufi Date: Thu, 14 Sep 2023 09:48:49 -0400 Subject: [PATCH] Shift from engine API to provider API for Debian 12 / OpenSSL 3 (#3965) * Rename openssl.ts cryptography.ts Since we could one day use something other than OpenSSL for cryptographic operations * Shift from engine API to provider API for Debian 12 / OpenSSL 3 --- ...production_machine_cert_signing_request.ts | 2 +- .../scripts/generate_dev_keys_and_certs.ts | 4 +- libs/auth/scripts/read_java_card_details.ts | 2 +- libs/auth/src/artifact_authentication.ts | 2 +- libs/auth/src/certs.test.ts | 4 +- libs/auth/src/certs.ts | 2 +- ...est.ts => cryptography.end_to_end.test.ts} | 2 +- .../{openssl.test.ts => cryptography.test.ts} | 39 ++++++++----------- libs/auth/src/{openssl.ts => cryptography.ts} | 35 ++--------------- .../src/intermediate-scripts/create_cert.ts | 2 +- .../src/intermediate-scripts/sign_message.ts | 2 +- libs/auth/src/java_card.test.ts | 24 ++++++------ libs/auth/src/java_card.ts | 4 +- libs/auth/src/live_check.ts | 2 +- libs/auth/src/tpm.ts | 30 ++++++++++---- 15 files changed, 69 insertions(+), 87 deletions(-) rename libs/auth/src/{openssl.end_to_end.test.ts => cryptography.end_to_end.test.ts} (98%) rename libs/auth/src/{openssl.test.ts => cryptography.test.ts} (96%) rename libs/auth/src/{openssl.ts => cryptography.ts} (94%) diff --git a/libs/auth/scripts/create_production_machine_cert_signing_request.ts b/libs/auth/scripts/create_production_machine_cert_signing_request.ts index 4da7ee9c68..35ff08fd4c 100644 --- a/libs/auth/scripts/create_production_machine_cert_signing_request.ts +++ b/libs/auth/scripts/create_production_machine_cert_signing_request.ts @@ -2,8 +2,8 @@ import { Buffer } from 'buffer'; import { extractErrorMessage } from '@votingworks/basics'; import { constructMachineCertSubject } from '../src/certs'; +import { createCertSigningRequest } from '../src/cryptography'; import { getRequiredEnvVar } from '../src/env_vars'; -import { createCertSigningRequest } from '../src/openssl'; const machineType = getRequiredEnvVar('VX_MACHINE_TYPE'); const jurisdiction = diff --git a/libs/auth/scripts/generate_dev_keys_and_certs.ts b/libs/auth/scripts/generate_dev_keys_and_certs.ts index 29d09f366d..51f2bbb9ee 100644 --- a/libs/auth/scripts/generate_dev_keys_and_certs.ts +++ b/libs/auth/scripts/generate_dev_keys_and_certs.ts @@ -15,14 +15,14 @@ import { MachineType, STANDARD_CERT_FIELDS, } from '../src/certs'; -import { DEV_JURISDICTION } from '../src/jurisdictions'; import { certPemToDer, createCert, openssl, OPENSSL_CONFIG_FILE_PATH, publicKeyPemToDer, -} from '../src/openssl'; +} from '../src/cryptography'; +import { DEV_JURISDICTION } from '../src/jurisdictions'; import { runCommand } from '../src/shell'; async function generateDevPrivateKey(): Promise { diff --git a/libs/auth/scripts/read_java_card_details.ts b/libs/auth/scripts/read_java_card_details.ts index adb836a117..4f6990ffd2 100644 --- a/libs/auth/scripts/read_java_card_details.ts +++ b/libs/auth/scripts/read_java_card_details.ts @@ -5,9 +5,9 @@ import { PROD_VX_CERT_AUTHORITY_CERT_PATH, } from '../src'; import { CardDetails } from '../src/card'; +import { verifyFirstCertWasSignedBySecondCert } from '../src/cryptography'; import { JavaCard } from '../src/java_card'; import { waitForReadyCardStatus } from './utils'; -import { verifyFirstCertWasSignedBySecondCert } from '../src/openssl'; const ENVS = ['development', 'production'] as const; diff --git a/libs/auth/src/artifact_authentication.ts b/libs/auth/src/artifact_authentication.ts index 707ce3110f..eed7957932 100644 --- a/libs/auth/src/artifact_authentication.ts +++ b/libs/auth/src/artifact_authentication.ts @@ -28,7 +28,7 @@ import { signMessage, verifyFirstCertWasSignedBySecondCert, verifySignature, -} from './openssl'; +} from './cryptography'; import { constructPrefixedMessage } from './signatures'; interface CastVoteRecordsToExport { diff --git a/libs/auth/src/certs.test.ts b/libs/auth/src/certs.test.ts index 8a1aa67f86..f45f34e596 100644 --- a/libs/auth/src/certs.test.ts +++ b/libs/auth/src/certs.test.ts @@ -12,9 +12,9 @@ import { parseCardDetailsFromCert, parseCert, } from './certs'; -import { openssl } from './openssl'; +import { openssl } from './cryptography'; -jest.mock('./openssl'); +jest.mock('./cryptography'); const cert = Buffer.from([]); const electionHash = diff --git a/libs/auth/src/certs.ts b/libs/auth/src/certs.ts index d01c2e0b8f..79cdc7c64d 100644 --- a/libs/auth/src/certs.ts +++ b/libs/auth/src/certs.ts @@ -3,7 +3,7 @@ import { z } from 'zod'; import { assert, throwIllegalValue } from '@votingworks/basics'; import { arePollWorkerCardDetails, CardDetails } from './card'; -import { openssl } from './openssl'; +import { openssl } from './cryptography'; /** * VotingWorks's IANA-assigned enterprise OID diff --git a/libs/auth/src/openssl.end_to_end.test.ts b/libs/auth/src/cryptography.end_to_end.test.ts similarity index 98% rename from libs/auth/src/openssl.end_to_end.test.ts rename to libs/auth/src/cryptography.end_to_end.test.ts index 88ec5852cc..2945ffc5c7 100644 --- a/libs/auth/src/openssl.end_to_end.test.ts +++ b/libs/auth/src/cryptography.end_to_end.test.ts @@ -14,7 +14,7 @@ import { signMessage, verifyFirstCertWasSignedBySecondCert, verifySignature, -} from './openssl'; +} from './cryptography'; /** * Whereas openssl.test.ts focuses on code coverage, openssl.end_to_end.test.ts uses no mocks and diff --git a/libs/auth/src/openssl.test.ts b/libs/auth/src/cryptography.test.ts similarity index 96% rename from libs/auth/src/openssl.test.ts rename to libs/auth/src/cryptography.test.ts index f84dac78e8..3cd61bbe2d 100644 --- a/libs/auth/src/openssl.test.ts +++ b/libs/auth/src/cryptography.test.ts @@ -22,8 +22,7 @@ import { signMessageHelper, SignMessageInput, SignMessageInputExcludingMessage, -} from './openssl'; -import { OPENSSL_TPM_ENGINE_NAME, TPM_KEY_ID, TPM_KEY_PASSWORD } from './tpm'; +} from './cryptography'; jest.mock('child_process'); jest.mock('tmp'); @@ -248,13 +247,11 @@ test('createCertSigningRequest', async () => { '-config', expect.stringContaining('/certs/openssl.cnf'), '-key', - TPM_KEY_ID, - '-keyform', - 'engine', - '-engine', - OPENSSL_TPM_ENGINE_NAME, - '-passin', - `pass:${TPM_KEY_PASSWORD}`, + 'handle:0x81000001', + '-provider', + 'tpm2', + '-provider', + 'default', '-subj', '//', ]); @@ -360,13 +357,11 @@ test.each<{ '-CA', '/path/to/cert-authority-cert.pem', '-CAkey', - TPM_KEY_ID, - '-CAkeyform', - 'engine', - '-engine', - OPENSSL_TPM_ENGINE_NAME, - '-passin', - `pass:${TPM_KEY_PASSWORD}`, + 'handle:0x81000001', + '-provider', + 'tpm2', + '-provider', + 'default', '-CAcreateserial', '-CAserial', '/tmp/serial.txt', @@ -566,13 +561,11 @@ test.each<{ 'dgst', '-sha256', '-sign', - TPM_KEY_ID, - '-keyform', - 'engine', - '-engine', - OPENSSL_TPM_ENGINE_NAME, - '-passin', - `pass:${TPM_KEY_PASSWORD}`, + 'handle:0x81000001', + '-provider', + 'tpm2', + '-provider', + 'default', ], }, ])( diff --git a/libs/auth/src/openssl.ts b/libs/auth/src/cryptography.ts similarity index 94% rename from libs/auth/src/openssl.ts rename to libs/auth/src/cryptography.ts index 7ee91c76ec..5c263ca53a 100644 --- a/libs/auth/src/openssl.ts +++ b/libs/auth/src/cryptography.ts @@ -16,7 +16,7 @@ import { TpmKeySchema, } from './keys'; import { runCommand } from './shell'; -import { OPENSSL_TPM_ENGINE_NAME, TPM_KEY_ID, TPM_KEY_PASSWORD } from './tpm'; +import { tpmOpensslParams } from './tpm'; /** * The path to the OpenSSL config file @@ -220,16 +220,7 @@ export async function createCertSigningRequest({ return ['-key', Buffer.from(certKey.content, 'utf-8')]; } case 'tpm': { - return [ - '-key', - TPM_KEY_ID, - '-keyform', - 'engine', - '-engine', - OPENSSL_TPM_ENGINE_NAME, - '-passin', - `pass:${TPM_KEY_PASSWORD}`, - ]; + return tpmOpensslParams('-key'); } /* istanbul ignore next: Compile-time check for completeness */ default: { @@ -275,16 +266,7 @@ export async function createCertGivenCertSigningRequest({ return ['-CAkey', signingPrivateKey.path]; } case 'tpm': { - return [ - '-CAkey', - TPM_KEY_ID, - '-CAkeyform', - 'engine', - '-engine', - OPENSSL_TPM_ENGINE_NAME, - '-passin', - `pass:${TPM_KEY_PASSWORD}`, - ]; + return tpmOpensslParams('-CAkey'); } /* istanbul ignore next: Compile-time check for completeness */ default: { @@ -474,16 +456,7 @@ export async function signMessageHelper({ return ['-sign', signingPrivateKey.path]; } case 'tpm': { - return [ - '-sign', - TPM_KEY_ID, - '-keyform', - 'engine', - '-engine', - OPENSSL_TPM_ENGINE_NAME, - '-passin', - `pass:${TPM_KEY_PASSWORD}`, - ]; + return tpmOpensslParams('-sign'); } /* istanbul ignore next: Compile-time check for completeness */ default: { diff --git a/libs/auth/src/intermediate-scripts/create_cert.ts b/libs/auth/src/intermediate-scripts/create_cert.ts index d8bc25402c..271cd7e897 100644 --- a/libs/auth/src/intermediate-scripts/create_cert.ts +++ b/libs/auth/src/intermediate-scripts/create_cert.ts @@ -1,7 +1,7 @@ import { Buffer } from 'buffer'; import { extractErrorMessage } from '@votingworks/basics'; -import { createCertHelper, parseCreateCertInput } from '../openssl'; +import { createCertHelper, parseCreateCertInput } from '../cryptography'; /** * An intermediate component of createCert in src/openssl.ts, needed for permissions purposes. See diff --git a/libs/auth/src/intermediate-scripts/sign_message.ts b/libs/auth/src/intermediate-scripts/sign_message.ts index 270dc8f975..aa65199b3e 100644 --- a/libs/auth/src/intermediate-scripts/sign_message.ts +++ b/libs/auth/src/intermediate-scripts/sign_message.ts @@ -4,7 +4,7 @@ import { extractErrorMessage } from '@votingworks/basics'; import { parseSignMessageInputExcludingMessage, signMessageHelper, -} from '../openssl'; +} from '../cryptography'; /** * An intermediate component of signMessage in src/openssl.ts, needed for permissions purposes. See diff --git a/libs/auth/src/java_card.test.ts b/libs/auth/src/java_card.test.ts index 66212cddac..68520e1082 100644 --- a/libs/auth/src/java_card.test.ts +++ b/libs/auth/src/java_card.test.ts @@ -30,6 +30,13 @@ import { Card, CardDetails, CheckPinResponse } from './card'; import { CardReader } from './card_reader'; import { CardType } from './certs'; import { JavaCardConfig } from './config'; +import { + certDerToPem, + createCert, + openssl, + PUBLIC_KEY_IN_DER_FORMAT_HEADER, + publicKeyDerToPem, +} from './cryptography'; import { CARD_VX_ADMIN_CERT, CARD_VX_CERT, @@ -42,13 +49,6 @@ import { PUK, VX_ADMIN_CERT_AUTHORITY_CERT, } from './java_card'; -import { - certDerToPem, - createCert, - openssl, - PUBLIC_KEY_IN_DER_FORMAT_HEADER, - publicKeyDerToPem, -} from './openssl'; import { construct8BytePinBuffer, CRYPTOGRAPHIC_ALGORITHM_IDENTIFIER, @@ -61,11 +61,11 @@ import { } from './piv'; jest.mock('./card_reader'); -jest.mock('./openssl', (): typeof import('./openssl') => ({ - // We use real openssl commands in these tests to ensure end-to-end correctness, the one - // exception being openssl commands for cert creation since two cert creation commands with the - // exact same inputs won't necessarily generate the same outputs, making assertions difficult - ...jest.requireActual('./openssl'), +jest.mock('./cryptography', (): typeof import('./cryptography') => ({ + // We use real cryptographic commands in these tests to ensure end-to-end correctness, the one + // exception being commands for cert creation since two cert creation commands with the exact + // same inputs won't necessarily generate the same outputs, making assertions difficult + ...jest.requireActual('./cryptography'), createCert: jest.fn(), })); diff --git a/libs/auth/src/java_card.ts b/libs/auth/src/java_card.ts index 5201fe45ec..3aa682a20d 100644 --- a/libs/auth/src/java_card.ts +++ b/libs/auth/src/java_card.ts @@ -34,7 +34,6 @@ import { parseCert, } from './certs'; import { constructJavaCardConfig, JavaCardConfig } from './config'; -import { FileKey, TpmKey } from './keys'; import { certDerToPem, certPemToDer, @@ -44,7 +43,8 @@ import { publicKeyDerToPem, verifyFirstCertWasSignedBySecondCert, verifySignature, -} from './openssl'; +} from './cryptography'; +import { FileKey, TpmKey } from './keys'; import { construct8BytePinBuffer, CRYPTOGRAPHIC_ALGORITHM_IDENTIFIER, diff --git a/libs/auth/src/live_check.ts b/libs/auth/src/live_check.ts index 0024841058..eb4b29aacd 100644 --- a/libs/auth/src/live_check.ts +++ b/libs/auth/src/live_check.ts @@ -3,8 +3,8 @@ import { Readable } from 'stream'; import { assert } from '@votingworks/basics'; import { constructLiveCheckConfig, LiveCheckConfig } from './config'; +import { signMessage } from './cryptography'; import { FileKey, TpmKey } from './keys'; -import { signMessage } from './openssl'; import { constructPrefixedMessage } from './signatures'; const LIVE_CHECK_MESSAGE_PAYLOAD_SEPARATOR = '/'; diff --git a/libs/auth/src/tpm.ts b/libs/auth/src/tpm.ts index 25e31c374a..1380a226e9 100644 --- a/libs/auth/src/tpm.ts +++ b/libs/auth/src/tpm.ts @@ -1,15 +1,31 @@ /** - * The name of the OpenSSL TPM engine that we're using + * The ID of the TPM signing key, distinct from the TPM primary key */ -export const OPENSSL_TPM_ENGINE_NAME = 'tpm2tss'; +const TPM_KEY_ID = '0x81000001'; /** - * The ID of the TPM signing key, distinct from the TPM primary key + * The name of the OpenSSL provider that we're using to interface with the TPM */ -export const TPM_KEY_ID = '0x81000001'; +const TPM_OPENSSL_PROVIDER = 'tpm2'; /** - * The password of the TPM signing key. Not required for our security model, just required by - * OpenSSL, hence the dummy password. + * Prepares the OpenSSL params necessary to use the TPM */ -export const TPM_KEY_PASSWORD = 'password'; +export function tpmOpensslParams( + opensslParam: '-CAkey' | '-key' | '-sign' +): string[] { + return [ + opensslParam, + `handle:${TPM_KEY_ID}`, + '-provider', + TPM_OPENSSL_PROVIDER, + // When a provider is explicitly specified, the default OpenSSL provider is not automatically + // loaded. But even when using the TPM OpenSSL provider, we still need the default provider for + // operations outside the scope of the TPM provider. For example, when creating a cert, OpenSSL + // needs to extract the public key from the cert signing request, which the TPM provider doesn't + // support. See https://www.openssl.org/docs/man3.0/man7/OSSL_PROVIDER-default.html for more + // context. + '-provider', + 'default', + ]; +}