OpenSshPrivateKey.decode constructor
OpenSshPrivateKey.decode(
- Uint8List bytes, {
- PvtTextSource? source,
Decode from a sequence of bytes.
Implementation
factory OpenSshPrivateKey.decode(Uint8List bytes, {PvtTextSource? source}) {
var p = 0;
// Extract null-terminated magic string
const maxMagicLength = 32; // size limit on the null-terminated magic string
while (bytes[p] != 0 && p < maxMagicLength && p < bytes.length) {
p++;
}
if (p == 0 || p == bytes.length || bytes[p] != 0) {
throw KeyBad('magic string not found');
}
final magicString = latin1.decode(bytes.sublist(0, p), allowInvalid: false);
if (magicString != magicVersionId) {
throw KeyUnsupported('unsupported type: $magicString');
}
p++; // skip over the null '\0'
// Use a binary range to extract out length-value chunks from the rest
//
// From [The OpenSSH Private Key Format](https://coolaj86.com/articles/the-openssh-private-key-format/):
//
// "openssh-key-v1"0x00 # NULL-terminated "Auth Magic" string
// 32-bit length, "none" # ciphername length and string
// 32-bit length, "none" # kdfname length and string
// 32-bit length, nil # kdf (0 length, no kdf)
// 32-bit 0x01 # number of keys, hard-coded to 1 (no length)
// 32-bit length, sshpub # public key in ssh format
// 32-bit length, keytype
// 32-bit length, pub0
// 32-bit length, pub1
// 32-bit length for rnd+prv+comment+pad
// 64-bit dummy checksum? # a random 32-bit int, repeated
// 32-bit length, keytype # the private key (including public)
// 32-bit length, pub0 # Public Key parts
// 32-bit length, pub1
// 32-bit length, prv0 # Private Key parts
// ... # (number varies by type)
// 32-bit length, comment # comment string
// padding bytes 0x010203 # pad to block size (see notes below)
//
// Source code: https://github.com/openssh/openssh-portable/blob/master/sshkey.c
final br = BinaryRange(bytes, begin: p);
final cipherName = br.nextString();
final kdfName = br.nextString();
final kdfRange = br.nextBinary();
final kdf = kdfRange.allRawBytes();
assert(kdfRange.isEmpty);
final numberOfKeys = br.nextUint32();
if (numberOfKeys != 1) {
throw KeyUnsupported('multiple keys not supported');
}
final publicKeyRange = br.nextBinary();
final privateKeyRange = br.nextBinary();
if (br.isNotEmpty) {
throw KeyBad('unexpected extra data in OpenSSH private key');
}
// Save the bytes making up the public and private keys
final publicKeyBytes = publicKeyRange.allRawBytes();
final privateKeyBytes = privateKeyRange.allRawBytes();
if (privateKeyBytes.length % 8 != 0) {
throw KeyBad('private key part is not padded correctly');
}
return OpenSshPrivateKey(
cipherName, kdfName, kdf, publicKeyBytes, privateKeyBytes, source);
}