parseDer static method

EcKeyPairData parseDer(
  1. Uint8List der, {
  2. required KeyPairType<KeyPairData, PublicKey> type,
})

Parses DER-encoded EC public key.

Currently this is implemented only for very specific inputs: those generated by Apple's CryptoKit. Apple could decide to change their implementation in future (though it has no reason to). Therefore we would like to transition to a proper ASN.1 decoder.

Implementation

static EcKeyPairData parseDer(Uint8List der, {required KeyPairType type}) {
  // Parsing of CryptoKit generated keys:
  // Unfortunately our current solutions is not future proof. Apple may change
  // the format in the future. We want to transition to a proper ASN.1 decoder.
  final config = CupertinoEcDer.get(type);
  final prefix = config.privateKeyPrefix;
  final middle = config.privateKeyMiddle;
  final numberLength = config.numberLength;
  if (!bytesStartsWith(der, prefix, 0)) {
    assert(() {
      // ONLY IN DEBUG MODE (i.e. assertions enabled).
      // In production, we don't want a parsing error to expose a private key.
      throw UnsupportedError(
        'Apple has changed CryptoKit $type key DER format prefix.\n'
        'Got DER (part of the error message in debug mode only):\n'
        '${hexFromBytes(der)}\n'
        'Expected bytes at index 0:\n'
        '${hexFromBytes(prefix)}\n'
        'Actual bytes at 0:\n'
        '${hexFromBytes(der.sublist(0, min(der.length, prefix.length)))}\n',
      );
    }());
    throw UnsupportedError(
      'Your version of package:cryptography supports only specific DER encodings from Apple CryptoKit',
    );
  }
  final middleIndex = prefix.length + numberLength;
  if (!bytesStartsWith(der, middle, middleIndex)) {
    assert(() {
      // ONLY IN DEBUG MODE (i.e. assertions enabled).
      // In production, we don't want a parsing error to expose a private key.
      throw UnsupportedError(
        'Apple has changed CryptoKit $type key DER format middle part.\n'
        'Got DER:\n'
        '${hexFromBytes(der)}\n'
        'Expected bytes at $middleIndex (part of the error message in debug mode only):\n'
        '${hexFromBytes(middle)}\n'
        'Actual bytes at $middleIndex:\n'
        '${hexFromBytes(der.sublist(middleIndex, min(der.length, middleIndex + 12)))}\n',
      );
    }());
    throw UnsupportedError(
      'Your version of package:cryptography supports only specific DER encodings from Apple CryptoKit',
    );
  }
  final dIndex = prefix.length;
  final xIndex = middleIndex + middle.length;
  final yIndex = xIndex + numberLength;
  if (der.length != yIndex + numberLength) {
    throw ArgumentError(
      'Apple has changed CryptoKit $type key DER pattern. DER length should be ${yIndex + numberLength}, not ${der.length}',
    );
  }
  return EcKeyPairData(
    d: Uint8List.fromList(der.sublist(dIndex, dIndex + numberLength)),
    x: Uint8List.fromList(der.sublist(xIndex, xIndex + numberLength)),
    y: Uint8List.fromList(der.sublist(yIndex, yIndex + numberLength)),
    type: type,
  );
}