1import {
2  convertKeyPairPEMToKeyPair,
3  convertCertificatePEMToCertificate,
4  validateSelfSignedCertificate,
5} from '@expo/code-signing-certificates';
6import { ExpoConfig, getConfig } from '@expo/config';
7import { promises as fs } from 'fs';
8import path from 'path';
9
10import { log } from './utils/log';
11import { attemptModification } from './utils/modifyConfigAsync';
12
13type Options = { certificateInput: string; keyInput: string; keyid: string | undefined };
14
15export async function configureCodeSigningAsync(
16  projectRoot: string,
17  { certificateInput, keyInput, keyid }: Options
18) {
19  const certificateInputDir = path.resolve(projectRoot, certificateInput);
20  const keyInputDir = path.resolve(projectRoot, keyInput);
21
22  const [certificatePEM, privateKeyPEM, publicKeyPEM] = await Promise.all([
23    fs.readFile(path.join(certificateInputDir, 'certificate.pem'), 'utf8'),
24    fs.readFile(path.join(keyInputDir, 'private-key.pem'), 'utf8'),
25    fs.readFile(path.join(keyInputDir, 'public-key.pem'), 'utf8'),
26  ]);
27
28  const certificate = convertCertificatePEMToCertificate(certificatePEM);
29  const keyPair = convertKeyPairPEMToKeyPair({ privateKeyPEM, publicKeyPEM });
30  validateSelfSignedCertificate(certificate, keyPair);
31
32  const { exp } = getConfig(projectRoot, { skipSDKVersionRequirement: true });
33
34  const fields: ExpoConfig['updates'] = {
35    codeSigningCertificate: `./${path.relative(projectRoot, certificateInputDir)}/certificate.pem`,
36    codeSigningMetadata: {
37      keyid: keyid ?? 'main',
38      alg: 'rsa-v1_5-sha256',
39    },
40  };
41  await attemptModification(
42    projectRoot,
43    {
44      updates: {
45        ...exp.updates,
46        ...fields,
47      },
48    },
49    {
50      updates: {
51        ...fields,
52      },
53    }
54  );
55
56  log(`Code signing configuration written to app configuration.`);
57}
58