derToRaw static method
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;
}