Public key algorithm
This chapter will build on the previous chapter by selecting the public key algorithm more robustly.
Get the list of supported algorithms
We can use the Privkey::pubkey()
method to obtain the Pubkey
from the Privkey
that we have read from PEM in the previous chapter:
// Get the public key from the private key.
let pubkey = privkey.pubkey();
And to obtain the list of algorithms that Makiko supports for this public key, we can use the Pubkey::algos()
method:
// Get the public key algorithms supported by the key.
let available_algos = pubkey.algos();
Next, we need to find out which of these algorithms is also supported by the server.
Check public keys and an algorithms
Armed with the private key and a list of algorithms, we could simply try to call Client::auth_pubkey()
with each algorithm in turn. This is a reasonable approach, but it has two disadvantages:
- SSH servers typically limit the number of failed authentication attempts to a small number and will close the connection when this limit is exceeded.
- The signing operation that is required for authentication might be expensive in terms of CPU time.
Fortunately, the SSH protocol provides a mechanism to ask the server whether it would accept a given combination of public key and algorithm, without actually attempting the authentication. We can use this mechanism by calling Client::check_pubkey()
, which takes the username, public key and public key algorithm, and returns a bool:
// Try the algorithms one by one.
let username: String = "ruth".into();
for pubkey_algo in available_algos.iter().copied() {
// Check whether this combination of a public key and algorithm would be acceptable to the
// server.
let check_ok = client.check_pubkey(username.clone(), &pubkey, pubkey_algo).await
.expect("Error when checking a public key");
...
}
If the server says that it will not accept this public key and algorithm, we can try the next algorithm:
for pubkey_algo in ... {
let check_ok = ...;
// Skip this algorithm if the server rejected it.
if !check_ok {
println!("Server rejected public key and algorithm {:?}", pubkey_algo.name);
continue;
}
...
}
Otherwise, we can try to authenticate:
for pubkey_algo in ... {
...
// Try to authenticate using this algorithm.
let auth_res = client.auth_pubkey(username.clone(), privkey.clone(), pubkey_algo).await
.expect("Error when trying to authenticate");
match auth_res {
makiko::AuthPubkeyResult::Success => {
println!("We have successfully authenticated using algorithm {:?}", pubkey_algo.name);
break;
},
makiko::AuthPubkeyResult::Failure(_) => {
println!("Authentication using public key and algorithm {:?} failed", pubkey_algo.name);
},
}
}
Finally, we can use the Client::is_authenticated()
method to check whether we have been successful:
// Check that we have been authenticated.
if !client.is_authenticated().unwrap() {
panic!("Could not authenticate");
}
Full code for this tutorial can be found in examples/tutorial_4.rs
. The program will print messages about the authentication attemps, and it will panic if authentication fails. If you don’t use the example server for this tutorial, you may need to change the code to use a different username and private key.