-
-
Notifications
You must be signed in to change notification settings - Fork 24
add feature to load an account by private key #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
src/lib.rs
Outdated
pub async fn load( | ||
private_key_pkcs8_der: &[u8], | ||
server_url: &str, | ||
http: Option<Box<dyn HttpClient>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure the Option
type really makes sense for http
? If it is Some
you don't want to pull in the DefaultClient
dependendencies; if it is None
you need to. Which one do you want to use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I did not see that was behind a feature gate.
src/lib.rs
Outdated
None => Client::new(server_url, Box::new(DefaultClient::try_new()?)).await?, | ||
}; | ||
|
||
let key = Key::from_pkcs8_der(private_key_pkcs8_der)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should probably externalize this, instead make Key::from_pkcs8_der()
public, and have this function take a Key
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I first did it that way, but wasn't sure you were open to extend the area of public API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But to be honest, if we are making Key part of the public API, it would make more sense to add the Key as the param of the Account::create as well, so that the user has the ability to create an account with a chosen key instead of forcing to generate one for them.
And in that case for_existing_account would probably just be a convenience wrapper around that.
src/lib.rs
Outdated
&ignored_account, // This field is ignored as per rfc8555 7.3.1 | ||
(key, pkcs8), | ||
None, // This field is ignored as per rfc8555 7.3.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These comments don't quite make sense in their current form.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first comment is there to explain why a dummy ignored_account is created and why sending that as part of the request is okay. The second one might need to be removed - agreed.
src/lib.rs
Outdated
/// | ||
/// The returned [`AccountCredentials`] can be serialized and stored for later use. | ||
/// Use [`Account::from_credentials()`] to restore the account from the credentials. | ||
pub async fn load( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this for_existing_account()
and move it up above from_parts()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
It would be nice to see test coverage for this as well. Maybe a Pebble integration test (https://github.com/djc/instant-acme/blob/main/tests/pebble.rs) that:
|
How can I run the pebble test locally? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please squash all your commits.
/// the `DefaultClient::try_new()?` can be used as the http client by default. | ||
/// The returned [`AccountCredentials`] can be serialized and stored for later use. | ||
/// Use [`Account::from_credentials()`] to restore the account from the credentials. | ||
#[cfg(feature = "hyper-rustls")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still don't really want to have both the _http()
and non-_http()
variants. Which one of these do you want to use?
@@ -907,22 +916,22 @@ struct Key { | |||
} | |||
|
|||
impl Key { | |||
fn generate() -> Result<(Self, crypto::pkcs8::Document), Error> { | |||
pub fn generate() -> Result<(Self, crypto::pkcs8::Document), Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay with making generate()
and from_pkcs8_der()
public. I don't think new()
and to_pkcs8_der()
need to become public.
@cpu thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cpu thoughts?
Yeah, in the absence of some additional motivation I agree 👍
/// This is a special value for `NewAccount` that is supplied | ||
/// when loading an account from a private key. | ||
/// According to rfc8555 7.3.1 this field needs to be supplied, | ||
/// but MUST be ignored by the ACME server. | ||
pub(crate) static LOAD_EXISTING_ACCOUNT: NewAccount = NewAccount { | ||
only_return_existing: true, | ||
contact: &[], | ||
terms_of_service_agreed: true, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename this to:
impl NewAccount<'_> {
pub(crate) const EXISTING: Self = Self {
..
};
}
It's a little more tricky than it should be at the moment because we're waiting on an upstream Pebble release. The basic process is:
For step 1, since we're presently waiting on a fresh release of Pebble you'll have to build it from source (which requires Go) for all the tests to pass. You can see how CI does it here: https://github.com/djc/instant-acme/blob/main/.github/workflows/pebble.yml Alternatively, you could download pre-built releases here: https://github.com/letsencrypt/pebble/releases/tag/v2.7.0 but you'll see that one of the Pebble tests fails ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I think this is coming along 👍
mod types; | ||
use crate::types::LOAD_EXISTING_ACCOUNT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this import should be moved into the existing use types::{ ... }
group on L40..43
@@ -389,6 +390,8 @@ impl Deref for ChallengeHandle<'_> { | |||
/// Create an [`Account`] with [`Account::create()`] or restore it from serialized data | |||
/// by passing deserialized [`AccountCredentials`] to [`Account::from_credentials()`]. | |||
/// | |||
/// Alternatively, you can load an account using the private key using [`Account::loadgit ()`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think some details shifted here and Account::loadgit()
isn't a fn?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lol, i guess my cli command got there by mistake
@@ -423,6 +426,48 @@ impl Account { | |||
}) | |||
} | |||
|
|||
/// Load a new account by private key, with a default or custom HTTP client | |||
/// | |||
/// https://www.rfc-editor.org/rfc/rfc8555#section-7.3.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// https://www.rfc-editor.org/rfc/rfc8555#section-7.3.1 | |
/// <https://www.rfc-editor.org/rfc/rfc8555#section-7.3.1> |
@@ -423,6 +426,48 @@ impl Account { | |||
}) | |||
} | |||
|
|||
/// Load a new account by private key, with a default or custom HTTP client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this headline is accurate for this fn: it always uses the default HTTP client.
/// | ||
/// https://www.rfc-editor.org/rfc/rfc8555#section-7.3.1 | ||
/// | ||
/// the `DefaultClient::try_new()?` can be used as the http client by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to my prev comment, for this fn (if both are kept):
/// the `DefaultClient::try_new()?` can be used as the http client by default. | |
/// the `DefaultClient::try_new()?` will be used as the http client. |
let rng = crypto::SystemRandom::new(); | ||
let pkcs8 = | ||
crypto::EcdsaKeyPair::generate_pkcs8(&crypto::ECDSA_P256_SHA256_FIXED_SIGNING, &rng)?; | ||
Self::new(pkcs8.as_ref(), rng).map(|key| (key, pkcs8)) | ||
} | ||
|
||
fn from_pkcs8_der(pkcs8_der: &[u8]) -> Result<Self, Error> { | ||
/// Create a key from a der encoded ES256 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can be more specific here:
/// Create a key from a der encoded ES256 | |
/// Create a `Key` from a DER encoded PKCS8 format ECDSA P-256 private key |
Self::new(pkcs8_der, crypto::SystemRandom::new()) | ||
} | ||
|
||
fn new(pkcs8_der: &[u8], rng: crypto::SystemRandom) -> Result<Self, Error> { | ||
/// Export a key to a der encoded ES256 key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this fn stays I think similar feedback as the from_pkcs8_der()
specificity applies.
Ok(self.inner.to_pkcs8v1()?) | ||
} | ||
|
||
/// Create a key from a der encoded ES256 key and a crypto::SystemRandom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this fn stays I think similar feedback as the from_pkcs8_der()
specificity applies.
/// This is a special value for `NewAccount` that is supplied | ||
/// when loading an account from a private key. | ||
/// According to rfc8555 7.3.1 this field needs to be supplied, | ||
/// but MUST be ignored by the ACME server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can describe this a bit clearer:
/// This is a special value for `NewAccount` that is supplied | |
/// when loading an account from a private key. | |
/// According to rfc8555 7.3.1 this field needs to be supplied, | |
/// but MUST be ignored by the ACME server. | |
/// A `NewAccount` endpoint request payload for fetching an existing account. | |
/// | |
/// `only_return_existing` is set to true to avoid creating a new account if the | |
/// existing account is not known to the ACME server for some reason. |
use serde_json::Value; | ||
use instant_acme::Key; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's import these top-level
Thanks for the feedback I'll reiterate in a few days when i get some free time |
Addresses #93