fromRawBytes static method

Future<PrivateKey> fromRawBytes(
  1. Uint8List bytes
)

Implementation

static Future<p2pkeys.PrivateKey> fromRawBytes(Uint8List bytes) async {
  if (bytes.isEmpty) {
    throw FormatException('Empty byte array provided');
  }

  try {

    // Debug information
    print('Parsing DER bytes of length: ${bytes.length}');
    print('First few bytes: ${bytes.take(10).map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}');

    final parser = pc.ASN1Parser(bytes);
    final asn1Object = parser.nextObject();

    if (asn1Object == null) {
      throw FormatException('Failed to parse ASN.1 object from bytes');
    }

    if (asn1Object is! pc.ASN1Sequence) {
      throw FormatException('Expected ASN.1 SEQUENCE but got: ${asn1Object.runtimeType}');
    }

    final asn1Sequence = asn1Object;

    // Parse the ASN.1 structure according to PKCS#1
    // RSAPrivateKey ::= SEQUENCE {
    //   version           Version,
    //   modulus           INTEGER,  -- n
    //   publicExponent    INTEGER,  -- e
    //   privateExponent   INTEGER,  -- d
    //   prime1            INTEGER,  -- p
    //   prime2            INTEGER,  -- q
    //   exponent1         INTEGER,  -- d mod (p-1)
    //   exponent2         INTEGER,  -- d mod (q-1)
    //   coefficient       INTEGER,  -- (inverse of q) mod p
    //   otherPrimeInfos   OtherPrimeInfos OPTIONAL
    // }

    if (asn1Sequence.elements == null || asn1Sequence.elements!.length < 9) {
      throw FormatException('RSA private key sequence does not contain required elements. Found: ${asn1Sequence.elements?.length ?? 0}');
    }

    // Validate and extract each element with proper type checking
    if (asn1Sequence.elements![0] is! pc.ASN1Integer) {
      throw FormatException('Expected version to be ASN1Integer');
    }
    final version = (asn1Sequence.elements![0] as pc.ASN1Integer).integer;
    if (version != BigInt.from(0)) {
      throw FormatException('Unsupported RSA private key version: $version');
    }

    // Extract and check all the required integers
    final elements = <BigInt?>[];
    for (int i = 0; i < 9; i++) {
      if (asn1Sequence.elements![i] is! pc.ASN1Integer) {
        throw FormatException('Expected ASN1Integer at position $i but got ${asn1Sequence.elements![i].runtimeType}');
      }
      elements.add((asn1Sequence.elements![i] as pc.ASN1Integer).integer);
    }

    final modulus = elements[1];
    final publicExponent = elements[2];
    final privateExponent = elements[3];
    final p = elements[4];
    final q = elements[5];
    final dP = elements[6];
    final dQ = elements[7];
    final qInv = elements[8];

    // Validate that no required values are null
    if (modulus == null || publicExponent == null || privateExponent == null ||
        p == null || q == null || dP == null || dQ == null || qInv == null) {
      throw FormatException('One or more required RSA parameters are null');
    }

    final privateKey = pc.RSAPrivateKey(
      modulus,
      privateExponent,
      p,
      q
    );

    final publicKey = pc.RSAPublicKey(
      modulus,
      publicExponent,
    );

    return RsaPrivateKey(privateKey, RsaPublicKey(publicKey));
  } catch (e) {
    print('Error parsing RSA private key: $e');
    if (e is FormatException) {
      rethrow;
    }
    throw FormatException('Failed to parse RSA private key: $e');
  }
}