derToRaw static method

Uint8List derToRaw(
  1. Uint8List der
)

Decode a DER-encoded ECDSA signature (optionally followed by one sighash byte) into fixed-width raw r||s (64 bytes total). r/s are decoded as plain big-endian integers rather than copied byte-for-byte, so a DER integer that is legitimately shorter than 32 bytes (or carries a sign-disambiguation 0x00 byte) is still re-encoded to the correct fixed width instead of shifting the s offset. Throws FormatException on structurally invalid DER.

Implementation

static Uint8List derToRaw(Uint8List der) {
  if (der.length < 8) throw const FormatException('DER signature too short');
  if (der[0] != 0x30) throw const FormatException('Invalid DER sequence tag');

  final totalLen = der[1];
  final derEnd = 2 + totalLen;
  // Accept either bare DER or DER followed by exactly one sighash byte.
  // Additional trailing data would otherwise be silently ignored.
  if (derEnd != der.length && derEnd + 1 != der.length) {
    throw const FormatException('Invalid DER length');
  }
  if (der[2] != 0x02) {
    throw const FormatException('Invalid DER integer tag for r');
  }

  final rLen = der[3];
  if (rLen == 0 || rLen > 33 || 4 + rLen > derEnd) {
    throw const FormatException('Invalid DER r length');
  }
  final rBytes = der.sublist(4, 4 + rLen);
  _validateDerInteger(rBytes, 'r');

  var offset = 4 + rLen;
  if (offset + 1 >= derEnd || der[offset] != 0x02) {
    throw const FormatException('Invalid DER integer tag for s');
  }
  offset += 1;
  final sLen = der[offset];
  offset += 1;
  if (sLen == 0 || sLen > 33 || offset + sLen != derEnd) {
    throw const FormatException('Invalid DER s length');
  }
  final sBytes = der.sublist(offset, offset + sLen);
  _validateDerInteger(sBytes, 's');

  final r = decodeBigInt(rBytes, endian: Endian.big);
  final s = decodeBigInt(sBytes, endian: Endian.big);

  final raw = Uint8List(64);
  raw.setRange(0, 32, encodeBigIntBe(r, length: 32));
  raw.setRange(32, 64, encodeBigIntBe(s, length: 32));
  return raw;
}