SshPublicKey.decode constructor

SshPublicKey.decode(
  1. String str, {
  2. int offset = 0,
  3. bool allowPreamble = true,
})

Decode from text.

Throws exception if there is no text encoding block found in the encoding.

Data before the encapsulation boundary is permitted. That data can be identified by examining the SshPublicKey.source in the result and comparing it any offset that was provided.

Implementation

SshPublicKey.decode(String str, {int offset = 0, bool allowPreamble = true}) {
  // Set starting offset

  if (offset < 0) {
    throw ArgumentError.value(offset, 'offset', 'is negative');
  }
  var p = offset;

  // Skip whitespace

  while (p < str.length) {
    final ch = str[p];
    if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
      p++;
    } else {
      break;
    }
  }

  // Find the start of the pre-encapsulation header (i.e. the "-----BEGIN")
  //
  // Note: this implementation does not care if it is at the beginning of a
  // line or not.

  if (allowPreamble) {
    p = str.indexOf(beginMarker, p);
    if (p < 0) {
      throw KeyBad('no RFC 7468 encoding found');
    }
  } else {
    if (!str.startsWith(beginMarker, p)) {
      throw KeyBad('no RFC 7468 encoding');
    }
  }

  final offsetBegin = p; // record where the block starts

  // skip over the begin marker and the CR, LF or CR-LF after it

  p += beginMarker.length;

  if (p < str.length && str[p] == '\r') {
    p++;
  }
  if (p < str.length && str[p] == '\n') {
    p++;
  }

  // Decode the headers (if any)

  final (hdr, posAfterHeaders) = _decodeHeaders(str, p);
  headers = hdr;
  p = posAfterHeaders;

  // Find end of encoding

  final dataBegin = p;

  final dataEnd = str.indexOf(_endMarker, p);
  if (dataEnd < 0) {
    throw KeyBad('missing end marker');
  }

  // Skip end marker and any CR, LF or CR-LF after it

  p = dataEnd + _endMarker.length;

  if (p < str.length && str[p] == '\r') {
    p++;
  }
  if (p < str.length && str[p] == '\n') {
    p++;
  }

  // Decode the encoded text
  //
  // RFC 7468 specifies the encoded text uses the Base-64 encoding defined
  // in section 4 of [RFC 4648](https://tools.ietf.org/html/rfc4648#section-4)
  // (which uses "+" and "/" as the 62nd and 63rd characters, and "=" for
  // padding). The Dart Base64Codec implements Base-64 as defined by RFC 4648,
  // but the decoded does not allow invalid characters and requires the
  // correct padding.

  final encapsulatedData = StringBuffer();
  //final encapsulatedData = encoding.substring(dataBegin, dataEnd);
  for (var q = dataBegin; q < dataEnd; q++) {
    final ch = str.codeUnitAt(q);
    if ('A'.codeUnitAt(0) <= ch && ch <= 'Z'.codeUnitAt(0) ||
        'a'.codeUnitAt(0) <= ch && ch <= 'z'.codeUnitAt(0) ||
        '0'.codeUnitAt(0) <= ch && ch <= '9'.codeUnitAt(0) ||
        '+'.codeUnitAt(0) == ch ||
        '/'.codeUnitAt(0) == ch ||
        '='.codeUnitAt(0) == ch) {
      encapsulatedData.writeCharCode(ch);
    }
  }

  source = PubTextSource(str, offsetBegin, p, PubKeyEncoding.sshPublicKey);

  try {
    bytes = base64.decode(encapsulatedData.toString());
    if (bytes.isEmpty) {
      throw KeyBad('no data');
    }
  } on FormatException catch (e) {
    if (e.message == 'Invalid length, must be multiple of four') {
      throw KeyBad('incomplete encapsulated data');
    } else {
      throw KeyBad('unexpected: ${e.message}');
    }
  }
}