parseValidatorKeys function

List<ValidatorSecretKey> parseValidatorKeys(
  1. String pemText
)

Parses every ValidatorSecretKey from a validator PEM file.

Iterates over each -----BEGIN PRIVATE KEY for <label>----- / -----END PRIVATE KEY for <label>----- block, base64-decodes the body to recover the hex-encoded secret key bytes, then hex-decodes those bytes into a 32-byte BLS secret key.

Parameters

  • pemText - Validator PEM file contents (one or many entries)

Returns

List<ValidatorSecretKey> - All validator keys, in file order

Throws

  • PemException - When pemText is empty, contains no valid validator entries, or has malformed base64/hex content

Implementation

List<ValidatorSecretKey> parseValidatorKeys(String pemText) {
  if (pemText.trim().isEmpty) {
    throw const PemException('Empty validator PEM input');
  }

  /// Capture the BLS public-key label on both BEGIN and END markers so we
  /// can assert they match (M2-17).
  final RegExp blockPattern = RegExp(
    r'-----BEGIN PRIVATE KEY for ([^-]+)-----\s*([A-Za-z0-9+/=\s]+?)\s*-----END PRIVATE KEY for ([^-]+)-----',
    multiLine: true,
  );

  final Iterable<RegExpMatch> matches = blockPattern.allMatches(pemText);
  if (matches.isEmpty) {
    throw const PemException(
      'No validator PEM entries found; expected BEGIN/END PRIVATE KEY headers',
    );
  }

  /// 192 lowercase-hex chars = 96 bytes (BLS public key length).
  final RegExp validatorPubKeyLabel = RegExp(r'^[0-9a-f]{192}$');

  final List<ValidatorSecretKey> result = <ValidatorSecretKey>[];
  for (final RegExpMatch match in matches) {
    final String beginLabel = match.group(1)!.trim();
    final String body = match.group(2)!.replaceAll(RegExp(r'\s+'), '');
    final String endLabel = match.group(3)!.trim();

    if (beginLabel != endLabel) {
      throw PemException(
        'Validator PEM BEGIN/END labels do not match: '
        '"$beginLabel" vs "$endLabel"',
      );
    }
    if (!validatorPubKeyLabel.hasMatch(beginLabel)) {
      throw PemException(
        'Validator PEM label must be exactly 192 lowercase hex chars '
        '(BLS public key, 96 bytes); got "${beginLabel.length}" chars',
      );
    }

    final Uint8List hexBytes;
    try {
      hexBytes = base64.decode(body);
    } on FormatException catch (error, stackTrace) {
      throw PemException(
        'Invalid base64 in validator PEM body',
        cause: error,
        stackTrace: stackTrace,
      );
    }
    final String hexString = utf8.decode(hexBytes);
    final Uint8List secretBytes;
    try {
      secretBytes = Uint8List.fromList(convert.hex.decode(hexString));
    } on FormatException catch (error, stackTrace) {
      throw PemException(
        'Invalid hex in validator PEM body',
        cause: error,
        stackTrace: stackTrace,
      );
    }
    if (secretBytes.length != validatorSecretKeyLength) {
      throw PemException(
        'Invalid validator secret length: expected $validatorSecretKeyLength, got ${secretBytes.length}',
      );
    }
    result.add(ValidatorSecretKey(secretBytes));
  }
  return result;
}