OpenSshPublicKey.decode constructor Null safety
Decode from text
The str
must consist of a line containing:
- key type
- single space
- base-64 encoded OpenSSH format key
- optional: single space followed by a comment
This decoder is less strict and will accept multiple whitespaces where a single space is expected. It will also ignore any white space and blank lines before the key-type (i.e. the key-type does not have to be at the beginning of the line).
https://tools.ietf.org/html/rfc4253#section-6.6
Throws a FormatException if the string does not contain correctly encoded value. Any whitespace at the start of the string is skipped.
Implementation
OpenSshPublicKey.decode(String str, {int offset = 0}) {
// Skip the key type
if (str.isEmpty) {
throw KeyMissing('OpenSSH Public Key: string is empty');
}
var p = offset;
// Skip leading whitespace and blank lines
while (p < str.length) {
final ch = str[p];
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
p++;
} else {
break;
}
}
final keyTypeStart = p;
int? algorithmNameEnd;
while (p < str.length) {
final ch = str[p];
if (ch == ' ') {
if (p != keyTypeStart + 1) {
algorithmNameEnd = p;
p++;
break;
} else {
break;
}
} else {
p++;
}
}
if (algorithmNameEnd == null) {
throw KeyBad('OpenSSH Public Key: key-type missing');
}
final keyType = str.substring(keyTypeStart, algorithmNameEnd);
// Find start of PEM data (by skipping all whitespace)
while (p < str.length && (str[p] == ' ' || str[p] == '\t')) {
p++;
}
final pemStart = p;
// Find end of PEM encoded data
while (p < str.length &&
(str[p] != ' ' && str[p] != '\t' && str[p] != '\r' && str[p] != '\n')) {
p++;
}
if (pemStart == p) {
throw KeyBad('OpenSSH Public Key: base64 missing');
}
final pemEnd = p;
// Parse optional comment
if (p < str.length && (str[p] == ' ')) {
// There is space, so the rest of the line is a comment
p++; // skip over the space
final commentStart = p;
// Find end of comment (which is terminated by the end-of-line or string)
while (p < str.length && (str[p] != '\r' && str[p] != '\n')) {
p++;
}
comment = str.substring(commentStart, p);
} else if (p < str.length && str[p] != '\r' && str[p] != '\n' ||
p == str.length) {
// End of the line or end of string
comment = null; // no comment
} else {
throw KeyBad('OpenSSH Public Key: base64 terminated incorrectly');
}
// Skip over any CR, LF or CR-LF
if (p < str.length && str[p] == '\r') {
p++;
}
if (p < str.length && str[p] == '\n') {
p++;
}
// Source
source = PubTextSource(str, keyTypeStart, p, PubKeyEncoding.openSsh);
// Decode the base-64 text
try {
data = base64.decode(str.substring(pemStart, pemEnd));
final chunks = BinaryRange(data);
// The first chunk of data is the key-type and should be the same as the
// text key-type at the beginning of the line
if (BinaryRange.copy(chunks).nextString() != keyType) {
throw KeyBad('OpenSSH Public Key: algorithm name mismatch');
}
} on FormatException catch (e) {
if (e.message == 'Invalid length, must be multiple of four') {
throw KeyBad('OpenSSH Public Key: base64 invalid');
} else {
throw KeyBad('OpenSSH Public Key: ${e.message}');
}
}
}