1import {
2  generateKeyPair,
3  generateSelfSignedCodeSigningCertificate,
4  convertCertificateToCertificatePEM,
5  convertKeyPairToPEM,
6} from '@expo/code-signing-certificates';
7import assert from 'assert';
8import { promises as fs } from 'fs';
9import { ensureDir } from 'fs-extra';
10import path from 'path';
11
12import { log } from './utils/log';
13
14type Options = { output?: string; validityDurationYears?: number; commonName?: string };
15
16export async function generateCodeSigningAsync(
17  projectRoot: string,
18  { output, validityDurationYears: validityDurationYearsNumber, commonName }: Options
19) {
20  assert(typeof output === 'string', '--output must be a string');
21  assert(
22    typeof validityDurationYearsNumber === 'number',
23    '--validity-duration-years must be a number'
24  );
25  assert(typeof commonName === 'string', '--common-name must be a string');
26
27  const validityDurationYears = Math.floor(validityDurationYearsNumber);
28
29  const outputDir = path.resolve(projectRoot, output);
30  await ensureDir(outputDir);
31
32  const isDirectoryEmpty = (await fs.readdir(outputDir)).length === 0;
33  assert(isDirectoryEmpty, 'Output directory must be empty');
34
35  const keyPair = generateKeyPair();
36  const validityNotBefore = new Date();
37  const validityNotAfter = new Date();
38  validityNotAfter.setFullYear(validityNotAfter.getFullYear() + validityDurationYears);
39  const certificate = generateSelfSignedCodeSigningCertificate({
40    keyPair,
41    validityNotBefore,
42    validityNotAfter,
43    commonName,
44  });
45
46  const keyPairPEM = convertKeyPairToPEM(keyPair);
47  const certificatePEM = convertCertificateToCertificatePEM(certificate);
48
49  await Promise.all([
50    fs.writeFile(path.join(outputDir, 'public-key.pem'), keyPairPEM.publicKeyPEM),
51    fs.writeFile(path.join(outputDir, 'private-key.pem'), keyPairPEM.privateKeyPEM),
52    fs.writeFile(path.join(outputDir, 'certificate.pem'), certificatePEM),
53  ]);
54
55  log(`Generated keys and certificates output to ${outputDir}`);
56}
57