Skip to content

Commit

Permalink
Shift from engine API to provider API for Debian 12 / OpenSSL 3 (#3965)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
arsalansufi authored Sep 14, 2023
1 parent 10ee207 commit 327fb09
Show file tree
Hide file tree
Showing 15 changed files with 69 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
4 changes: 2 additions & 2 deletions libs/auth/scripts/generate_dev_keys_and_certs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Buffer> {
Expand Down
2 changes: 1 addition & 1 deletion libs/auth/scripts/read_java_card_details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion libs/auth/src/artifact_authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
signMessage,
verifyFirstCertWasSignedBySecondCert,
verifySignature,
} from './openssl';
} from './cryptography';
import { constructPrefixedMessage } from './signatures';

interface CastVoteRecordsToExport {
Expand Down
4 changes: 2 additions & 2 deletions libs/auth/src/certs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
2 changes: 1 addition & 1 deletion libs/auth/src/certs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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',
'//',
]);
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
],
},
])(
Expand Down
35 changes: 4 additions & 31 deletions libs/auth/src/openssl.ts → libs/auth/src/cryptography.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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: {
Expand Down
2 changes: 1 addition & 1 deletion libs/auth/src/intermediate-scripts/create_cert.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion libs/auth/src/intermediate-scripts/sign_message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 12 additions & 12 deletions libs/auth/src/java_card.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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(),
}));

Expand Down
4 changes: 2 additions & 2 deletions libs/auth/src/java_card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
parseCert,
} from './certs';
import { constructJavaCardConfig, JavaCardConfig } from './config';
import { FileKey, TpmKey } from './keys';
import {
certDerToPem,
certPemToDer,
Expand All @@ -44,7 +43,8 @@ import {
publicKeyDerToPem,
verifyFirstCertWasSignedBySecondCert,
verifySignature,
} from './openssl';
} from './cryptography';
import { FileKey, TpmKey } from './keys';
import {
construct8BytePinBuffer,
CRYPTOGRAPHIC_ALGORITHM_IDENTIFIER,
Expand Down
2 changes: 1 addition & 1 deletion libs/auth/src/live_check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '/';
Expand Down
30 changes: 23 additions & 7 deletions libs/auth/src/tpm.ts
Original file line number Diff line number Diff line change
@@ -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',
];
}

0 comments on commit 327fb09

Please sign in to comment.