writeKeyPair function
Writes a KeyPair to disk using the JSON byte-array format produced by
solana-keygen.
The first 32 bytes are the private key and the last 32 bytes are the public
key. Parent directories are created automatically. Existing files are not
overwritten unless unsafelyOverwriteExistingKeyPair is true.
Implementation
Future<void> writeKeyPair(
KeyPair keyPair,
String path, {
bool unsafelyOverwriteExistingKeyPair = false,
}) async {
final privateKey = keyPair.privateKey;
final publicKey = keyPair.publicKey;
assertIsPrivateKey(privateKey);
final bytes = Uint8List(64)
..setRange(0, 32, privateKey)
..setRange(32, 64, publicKey);
try {
final file = File(path);
final parent = file.parent;
if (parent.path.isNotEmpty) {
await parent.create(recursive: true);
}
if (unsafelyOverwriteExistingKeyPair) {
final type = FileSystemEntity.typeSync(path, followLinks: false);
if (type == FileSystemEntityType.link) {
throw FileSystemException(
'Refusing to overwrite a symbolic link',
path,
);
}
await file.create();
} else {
// `exclusive` makes the existence check and creation one atomic
// operation. A separate exists/open sequence could be raced into
// truncating another file or following an attacker-created symlink.
await file.create(exclusive: true);
}
if (!Platform.isWindows) {
final chmod = await Process.run('chmod', ['600', path]);
// coverage:ignore-start
// A failing system chmod cannot be triggered portably without replacing
// host tooling or writing outside the test sandbox.
if (chmod.exitCode != 0) {
throw FileSystemException(
'Failed to restrict key pair file permissions',
path,
);
}
// coverage:ignore-end
}
final sink = await file.open(mode: FileMode.writeOnly);
try {
await sink.writeString(jsonEncode(bytes.toList()));
} finally {
await sink.close();
}
} finally {
_zeroBytes(privateKey);
_zeroBytes(publicKey);
_zeroBytes(bytes);
}
}