decodeAndVerify method Null safety

CoseResult decodeAndVerify(
  1. List<int> cose,
  2. Map<String, String> certs
)

Implementation

static CoseResult decodeAndVerify(List<int> cose, Map<String, String> certs) {
  var inst = Cbor();
  inst.decodeFromList(cose);
  List<dynamic>? data = inst.getDecodedData();

  if (null == data) {
    return CoseResult(
        payload: {},
        verified: false,
        errorCode: CoseErrorCode.cbor_decoding_error);
  }

  if (data.length <= 0) {
    return CoseResult(
        payload: {},
        verified: false,
        errorCode: CoseErrorCode.cbor_decoding_error);
  }

  // take the first element
  var element = data.first;

  // check if it is of type List
  if (!(element is List)) {
    return CoseResult(
        payload: {},
        verified: false,
        errorCode: CoseErrorCode.unsupported_format);
  }

  List items = element;
  // check if it has exactly 4 items
  if (4 != items.length) {
    return CoseResult(
        payload: {},
        verified: false,
        errorCode: CoseErrorCode.invalid_format);
  }

  // extract the useful information.
  final protectedHeader = items[0];
  final unprotectedHeader = items[1];
  final payloadBytes = items[2];
  final signers = items[3];

  // parse headers.
  var headers = Cbor();
  headers.decodeFromBuffer(protectedHeader);
  var headerList = headers.getDecodedData();
  var header = {};
  if (headerList != null) {
    if (!(headerList is List)) {
      return CoseResult(
          payload: {},
          verified: false,
          errorCode: CoseErrorCode.unsupported_header_format);
    }

    if (headerList.length <= 0) {
      return CoseResult(
          payload: {},
          verified: false,
          errorCode: CoseErrorCode.cbor_decoding_error);
    }
    header = headerList.first;
  }

  final kidKey = HeaderParameters['kid'];
  // fall back to unprotected header if protected is not provided.
  var kidBuffer = header[kidKey] ?? unprotectedHeader[kidKey];
  var kid = Uint8List.view(kidBuffer.buffer, 0, kidBuffer.length);
  if (kid.length > 8) {
    kid = kid.sublist(0, 8);
  }
  final bkid = base64.encode(kid);

  final algKey = HeaderParameters['alg'];
  final a = header[algKey] ?? unprotectedHeader[algKey];

  //print("kid: ${base64.encode(kid)}");
  //print("alg: $a");

  // parse the payload
  var payloadCbor = Cbor();
  payloadCbor.decodeFromBuffer(payloadBytes);
  //print(payloadCbor.decodedPrettyPrint());

  dynamic payload = {};
  try {
    var data = payloadCbor.getDecodedData();
    if (null == data) {
      return CoseResult(
          payload: {},
          verified: false,
          errorCode: CoseErrorCode.payload_format_error);
    }
    payload = data.first;
  } on Exception catch (e) {
    print(e);
    return CoseResult(
        payload: {},
        verified: false,
        errorCode: CoseErrorCode.payload_format_error);
  }
  if (!certs.containsKey(bkid)) {
    return CoseResult(
        payload: payload,
        verified: false,
        errorCode: CoseErrorCode.key_not_found);
  }

  String derB64 = certs[bkid]!;
  String cert = derB64.trim();

  // add pem header and footer if missing.

  if (!(cert.startsWith(begin_cert) && cert.endsWith(end_cert))) {
    cert = begin_cert + '\n' + cert + '\n' + end_cert;
  } else {
    derB64 = cert.replaceFirst(begin_cert, "");
    derB64 = derB64.replaceFirst(end_cert, "");
    derB64 = derB64.replaceAll("\n", "");
    derB64 = derB64.replaceAll(" ", "");
  }

  // we expect there to be only 1 cert in the pem, so we take the first.
  var x509cert = parsePem(cert).first as X509Certificate;

  //The kid is defined as the first 8 bytes of the SHA256 hash of the certificate.
  var der = base64Decode(derB64);
  var certKid = base64Encode(sha256.convert(der).bytes.sublist(0, 8));

  if (certKid != bkid) {
    return CoseResult(
        payload: payload,
        verified: false,
        errorCode: CoseErrorCode.kid_mismatch);
  }

  var sigStructure = Cbor();
  final sigStructureEncoder = sigStructure.encoder;

  sigStructureEncoder.writeArray([
    'Signature1', // context string
    Uint8List.view(protectedHeader.buffer, 0,
        protectedHeader.length), // protected body (header)
    Uint8List(0),
    Uint8List.view(payloadBytes.buffer, 0, payloadBytes.length)
  ]);

  sigStructure.decodeFromInput();
  final sigStructureBytes = sigStructure.output.getData();

  var publicKey = x509cert.publicKey;

  // -7: {'sign': 'ES256', 'digest': 'SHA-256'},
  Verifier? verifier;
  bool verified = false;
  if (publicKey is EcPublicKey) {
    // primary algorithm
    /// ECDSA using P-256 and SHA-256
    if (-7 == a) {
      verifier = publicKey.createVerifier(algorithms.signing.ecdsa.sha256);
    } else if (-35 == a) {
      verifier = publicKey.createVerifier(algorithms.signing.ecdsa.sha384);
    } else if (-36 == a) {
      verifier = publicKey.createVerifier(algorithms.signing.ecdsa.sha512);
    } else {
      return CoseResult(
          payload: payload,
          verified: false,
          errorCode: CoseErrorCode.unsupported_algorithm);
    }
  } else if (publicKey is RsaPublicKey) {
    // secondary algorithm
    /// RSASSA-PKCS1-v1_5 using SHA-256
    if (-7 == a) {
      verifier = publicKey.createVerifier(algorithms.signing.rsa.sha256);
      ninja.RsaVerifier ninv =
          ninja.RsassaPkcs1v15Verifier(hasher: EmsaHasher.sha256);
      var npk = ninja.RSAPublicKey(publicKey.modulus, publicKey.exponent);
      var verified = ninv.verify(
          npk,
          Uint8List.view(signers.buffer, 0, signers.length),
          sigStructureBytes.buffer.asUint8List());
      print(verified);
    } else if (-35 == a) {
      verifier = publicKey.createVerifier(algorithms.signing.rsa.sha384);
      ninja.RsaVerifier ninv =
          ninja.RsassaPkcs1v15Verifier(hasher: EmsaHasher.sha384);
      var npk = ninja.RSAPublicKey(publicKey.modulus, publicKey.exponent);
      var verified = ninv.verify(
          npk,
          Uint8List.view(signers.buffer, 0, signers.length),
          sigStructureBytes.buffer.asUint8List());
      print(verified);
    } else if (-36 == a) {
      verifier = publicKey.createVerifier(algorithms.signing.rsa.sha512);

      ninja.RsaVerifier ninv =
          ninja.RsassaPkcs1v15Verifier(hasher: EmsaHasher.sha512);
      var npk = ninja.RSAPublicKey(publicKey.modulus, publicKey.exponent);
      var verified = ninv.verify(
          npk,
          Uint8List.view(signers.buffer, 0, signers.length),
          sigStructureBytes.buffer.asUint8List());
      print(verified);
    } else if (-37 == a) {
      ninja.RsaSsaPssVerifier ninv = ninja.RsaSsaPssVerifier(
          hasher: sha256, mgf: Mgf1(hasher: sha256), saltLength: 32);

      var npk = ninja.RSAPublicKey(publicKey.modulus, publicKey.exponent);
      verified = ninv.verify(
          npk,
          Uint8List.view(signers.buffer, 0, signers.length),
          sigStructureBytes.buffer.asUint8List());

      print(verified);
    } else {
      return CoseResult(
          payload: payload,
          verified: false,
          errorCode: CoseErrorCode.unsupported_algorithm);
    }
  } else {
    return CoseResult(
        payload: payload,
        verified: false,
        errorCode: CoseErrorCode.unsupported_algorithm);
  }

  if (!verified && verifier != null) {
    verified = verifier.verify(sigStructureBytes.buffer.asUint8List(),
        Signature(Uint8List.view(signers.buffer, 0, signers.length)));
  }

  return CoseResult(
      payload: payload, verified: verified, errorCode: CoseErrorCode.none);
}