summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src/helpers/convertX509PublicKeyToCOSE.ts
blob: 0f87f38e97adf20e74f44150b381a36cc1599e79 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import {
  AsnParser,
  Certificate,
  ECParameters,
  id_ecPublicKey,
  id_secp256r1,
  id_secp384r1,
  RSAPublicKey,
} from '../deps.ts';
import {
  COSECRV,
  COSEKEYS,
  COSEKTY,
  COSEPublicKey,
  COSEPublicKeyEC2,
  COSEPublicKeyRSA,
} from './cose.ts';
import { mapX509SignatureAlgToCOSEAlg } from './mapX509SignatureAlgToCOSEAlg.ts';

export function convertX509PublicKeyToCOSE(
  x509Certificate: Uint8Array,
): COSEPublicKey {
  let cosePublicKey: COSEPublicKey = new Map();

  /**
   * Time to extract the public key from an X.509 certificate
   */
  const x509 = AsnParser.parse(x509Certificate, Certificate);

  const { tbsCertificate } = x509;
  const { subjectPublicKeyInfo, signature: _tbsSignature } = tbsCertificate;

  const signatureAlgorithm = _tbsSignature.algorithm;
  const publicKeyAlgorithmID = subjectPublicKeyInfo.algorithm.algorithm;

  if (publicKeyAlgorithmID === id_ecPublicKey) {
    /**
     * EC2 Public Key
     */
    if (!subjectPublicKeyInfo.algorithm.parameters) {
      throw new Error('Certificate public key was missing parameters (EC2)');
    }

    const ecParameters = AsnParser.parse(
      new Uint8Array(subjectPublicKeyInfo.algorithm.parameters),
      ECParameters,
    );

    let crv = -999;
    const { namedCurve } = ecParameters;

    if (namedCurve === id_secp256r1) {
      crv = COSECRV.P256;
    } else if (namedCurve === id_secp384r1) {
      crv = COSECRV.P384;
    } else {
      throw new Error(
        `Certificate public key contained unexpected namedCurve ${namedCurve} (EC2)`,
      );
    }

    const subjectPublicKey = new Uint8Array(
      subjectPublicKeyInfo.subjectPublicKey,
    );

    let x: Uint8Array;
    let y: Uint8Array;
    if (subjectPublicKey[0] === 0x04) {
      // Public key is in "uncompressed form", so we can split the remaining bytes in half
      let pointer = 1;
      const halfLength = (subjectPublicKey.length - 1) / 2;
      x = subjectPublicKey.slice(pointer, pointer += halfLength);
      y = subjectPublicKey.slice(pointer);
    } else {
      throw new Error(
        'TODO: Figure out how to handle public keys in "compressed form"',
      );
    }

    const coseEC2PubKey: COSEPublicKeyEC2 = new Map();
    coseEC2PubKey.set(COSEKEYS.kty, COSEKTY.EC2);
    coseEC2PubKey.set(
      COSEKEYS.alg,
      mapX509SignatureAlgToCOSEAlg(signatureAlgorithm),
    );
    coseEC2PubKey.set(COSEKEYS.crv, crv);
    coseEC2PubKey.set(COSEKEYS.x, x);
    coseEC2PubKey.set(COSEKEYS.y, y);

    cosePublicKey = coseEC2PubKey;
  } else if (publicKeyAlgorithmID === '1.2.840.113549.1.1.1') {
    /**
     * RSA public key
     */
    const rsaPublicKey = AsnParser.parse(
      subjectPublicKeyInfo.subjectPublicKey,
      RSAPublicKey,
    );

    const coseRSAPubKey: COSEPublicKeyRSA = new Map();
    coseRSAPubKey.set(COSEKEYS.kty, COSEKTY.RSA);
    coseRSAPubKey.set(
      COSEKEYS.alg,
      mapX509SignatureAlgToCOSEAlg(signatureAlgorithm),
    );
    coseRSAPubKey.set(COSEKEYS.n, new Uint8Array(rsaPublicKey.modulus));
    coseRSAPubKey.set(COSEKEYS.e, new Uint8Array(rsaPublicKey.publicExponent));

    cosePublicKey = coseRSAPubKey;
  } else {
    throw new Error(
      `Certificate public key contained unexpected algorithm ID ${publicKeyAlgorithmID}`,
    );
  }

  return cosePublicKey;
}