Split authorization functionality out of sq pki link add.
- Split authorization functionality out of `sq pki link add` into a new command, `sq pki link authorize`. - Align `sq pki link authorize`'s arguments with `sq pki authorize` arguments.
This commit is contained in:
parent
cd7b79dbae
commit
609c5aab16
4
NEWS
4
NEWS
@ -85,6 +85,10 @@
|
|||||||
pki certify`.
|
pki certify`.
|
||||||
- Removed `sq pki link add`'s `--petname` argument. Use `--userid`
|
- Removed `sq pki link add`'s `--petname` argument. Use `--userid`
|
||||||
in conjunction with `--add-userid` instead.
|
in conjunction with `--add-userid` instead.
|
||||||
|
- Previously `sq pki link certify` could create certifications, and
|
||||||
|
mark a certificate as a trusted introducer (when the user set
|
||||||
|
`--depth` to be greater than zero). The latter functionality has
|
||||||
|
been split off to the new subcommand `sq pki link authorize`.
|
||||||
|
|
||||||
* Changes in 0.38.0
|
* Changes in 0.38.0
|
||||||
** Notable changes
|
** Notable changes
|
||||||
|
@ -17,7 +17,7 @@ use crate::cli::types::TrustAmount;
|
|||||||
long_about =
|
long_about =
|
||||||
"Manage authenticated certificate and User ID links
|
"Manage authenticated certificate and User ID links
|
||||||
|
|
||||||
Link a certificate and User ID is one way of making `sq` consider a \
|
Linking a certificate and User ID is one way of making `sq` consider a \
|
||||||
binding to be authentic. Another way is to use `sq pki certify` to \
|
binding to be authentic. Another way is to use `sq pki certify` to \
|
||||||
certify the binding with an explicitly configured trust root. The \
|
certify the binding with an explicitly configured trust root. The \
|
||||||
linking functionality is often easier to work with, and the \
|
linking functionality is often easier to work with, and the \
|
||||||
@ -35,8 +35,7 @@ authenticated certificate to be authentic.
|
|||||||
|
|
||||||
Users can create a link using `sq pki link add`. That link can later be \
|
Users can create a link using `sq pki link add`. That link can later be \
|
||||||
retracted using `sq pki link retract`. A certificate can also be \
|
retracted using `sq pki link retract`. A certificate can also be \
|
||||||
accepted as a trusted introducer by passing the `--ca` option to \
|
accepted as a trusted introducer by using `sq pki link authorize`.
|
||||||
`sq pki link add`.
|
|
||||||
|
|
||||||
`sq` implements linking using non-exportable certifications, and an \
|
`sq` implements linking using non-exportable certifications, and an \
|
||||||
implicit trust root. An OpenPGP certificate directory, the default \
|
implicit trust root. An OpenPGP certificate directory, the default \
|
||||||
@ -94,10 +93,9 @@ with all of its self-signed user IDs as a trusted certification \
|
|||||||
authority constrained to the domain example.org. That is, the \
|
authority constrained to the domain example.org. That is, the \
|
||||||
certificate is considered a trusted introducer for example.org.",
|
certificate is considered a trusted introducer for example.org.",
|
||||||
command: &[
|
command: &[
|
||||||
"sq", "pki", "link", "add",
|
"sq", "pki", "link", "authorize",
|
||||||
"--ca=example.org",
|
"--domain=example.org",
|
||||||
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
|
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
|
||||||
"--all",
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -124,6 +122,7 @@ test_examples!(sq_pki_link, LINK_EXAMPLES);
|
|||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub enum Subcommands {
|
pub enum Subcommands {
|
||||||
Add(AddCommand),
|
Add(AddCommand),
|
||||||
|
Authorize(AuthorizeCommand),
|
||||||
Retract(RetractCommand),
|
Retract(RetractCommand),
|
||||||
List(ListCommand),
|
List(ListCommand),
|
||||||
}
|
}
|
||||||
@ -131,16 +130,16 @@ pub enum Subcommands {
|
|||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(
|
#[clap(
|
||||||
name = "add",
|
name = "add",
|
||||||
about = "Link a certificate and a User ID",
|
about = "Link a certificate and a user ID",
|
||||||
long_about =
|
long_about =
|
||||||
"Link a certificate and a User ID
|
"Link a certificate and a user ID
|
||||||
|
|
||||||
This cause `sq` to considers the certificate and User ID binding to be \
|
This causes `sq` to consider the certificate and user ID binding to be \
|
||||||
authentic.
|
authentic. You would do this if you are confident that a particular \
|
||||||
|
certificate should be associated with Alice, for example. Note: this \
|
||||||
A certificate can also be accepted as a certification authority, which \
|
does not consider the certificate to be a trusted introducer; it only \
|
||||||
is also known as a trusted introducer, by using the `--ca` or \
|
considers the binding to be authentic. To authorize a certificate to \
|
||||||
`--depth` option.
|
be a trusted introducer use `sq pki link authorize`.
|
||||||
|
|
||||||
A link can be retracted using `sq pki link retract`.
|
A link can be retracted using `sq pki link retract`.
|
||||||
|
|
||||||
@ -152,13 +151,12 @@ especially when the user's certification key is offline. And the \
|
|||||||
latter improves the user's privacy, by reducing the chance that parts \
|
latter improves the user's privacy, by reducing the chance that parts \
|
||||||
of the user's social graph is leaked when a certificate is shared.
|
of the user's social graph is leaked when a certificate is shared.
|
||||||
|
|
||||||
By default a link never expires. \
|
By default a link never expires. This can be overridden using \
|
||||||
Using the `--expiration` argument specific validity periods may be defined. \
|
`--expiration` argument.
|
||||||
It allows for providing a point in time for validity to end or a validity \
|
|
||||||
duration.
|
|
||||||
|
|
||||||
`sq pki link` respects the reference time set by the top-level `--time` \
|
`sq pki link add` respects the reference time set by the top-level \
|
||||||
argument. It sets the link's creation time to the reference time.
|
`--time` argument. It sets the link's creation time to the reference \
|
||||||
|
time.
|
||||||
",
|
",
|
||||||
after_help = ADD_EXAMPLES,
|
after_help = ADD_EXAMPLES,
|
||||||
)]
|
)]
|
||||||
@ -187,49 +185,6 @@ pub struct AddCommand {
|
|||||||
)]
|
)]
|
||||||
pub amount: TrustAmount<u8>,
|
pub amount: TrustAmount<u8>,
|
||||||
|
|
||||||
#[clap(
|
|
||||||
long = "depth",
|
|
||||||
value_name = "TRUST_DEPTH",
|
|
||||||
help = "Set the trust depth",
|
|
||||||
long_help =
|
|
||||||
"Set the trust depth (sometimes referred to as the trust level). \
|
|
||||||
0 means a normal certification of <CERTIFICATE, USERID>. \
|
|
||||||
1 means CERTIFICATE is also a trusted introducer, 2 means \
|
|
||||||
CERTIFICATE is a meta-trusted introducer, etc.",
|
|
||||||
)]
|
|
||||||
pub depth: Option<u8>,
|
|
||||||
#[clap(
|
|
||||||
long = "ca",
|
|
||||||
value_name = "*|DOMAIN",
|
|
||||||
help = "Mark the certificate as a certification authority for a domain",
|
|
||||||
long_help =
|
|
||||||
"Mark the certificate as a certification authority for a \
|
|
||||||
domain. Use `*` to make the certificate a certification
|
|
||||||
authority for any User ID.
|
|
||||||
|
|
||||||
A certification authority is also referred to as a trusted \
|
|
||||||
introducer. This command is equivalent to making the trust \
|
|
||||||
depth unconstrained, i.e., setting the depth to 255. See \
|
|
||||||
`--depth` for more information.",
|
|
||||||
)]
|
|
||||||
pub ca: Vec<String>,
|
|
||||||
#[clap(
|
|
||||||
long = "regex",
|
|
||||||
value_name = "REGEX",
|
|
||||||
help = "Add a regular expression to constrain \
|
|
||||||
what a trusted introducer can certify",
|
|
||||||
long_help =
|
|
||||||
"Add a regular expression to constrain \
|
|
||||||
what a trusted introducer can certify. \
|
|
||||||
The regular expression must match \
|
|
||||||
the certified User ID in all intermediate \
|
|
||||||
introducers, and the certified certificate. \
|
|
||||||
Multiple regular expressions may be \
|
|
||||||
specified. In that case, at least \
|
|
||||||
one must match.",
|
|
||||||
)]
|
|
||||||
pub regex: Vec<String>,
|
|
||||||
|
|
||||||
#[clap(
|
#[clap(
|
||||||
long = "temporary",
|
long = "temporary",
|
||||||
conflicts_with_all = &[ "amount" ],
|
conflicts_with_all = &[ "amount" ],
|
||||||
@ -336,50 +291,224 @@ user IDs.",
|
|||||||
"--all",
|
"--all",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
test_examples!(sq_pki_link_add, ADD_EXAMPLES);
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[clap(
|
||||||
|
name = "authorize",
|
||||||
|
about = "Make a certificate a trusted introducer",
|
||||||
|
long_about = "\
|
||||||
|
Make a certificate a trusted introducer.
|
||||||
|
|
||||||
|
This causes `sq` to consider the certificate to be a be a trusted \
|
||||||
|
introducer. Trusted introducer is another word for certification \
|
||||||
|
authority (CA). When you link a trusted introducer, you consider \
|
||||||
|
certifications made by the trusted introducer to be valid. A trusted \
|
||||||
|
introducer can also designate further trusted introducers.
|
||||||
|
|
||||||
|
As is, a trusted introducer has a lot of power. This power can be \
|
||||||
|
limited in several ways.
|
||||||
|
|
||||||
|
- The ability to specify further introducers can be constrained \
|
||||||
|
using the `--depth` parameter.
|
||||||
|
|
||||||
|
- The degree to which an introducer is trusted can be changed using \
|
||||||
|
the `--amount` parameter.
|
||||||
|
|
||||||
|
- The user IDs that an introducer can certify can be constrained by \
|
||||||
|
domain using the `--domain` parameter or a regular expression using \
|
||||||
|
the `--regex` parameter.
|
||||||
|
|
||||||
|
These mechanisms allow you to say that you are willing to rely on the \
|
||||||
|
CA for example.org, but only for user IDs that have an email address \
|
||||||
|
for example.org, for instance.
|
||||||
|
|
||||||
|
A link can be retracted using `sq pki link retract`.
|
||||||
|
|
||||||
|
This command is similar to `sq pki authorize`, but the certifications \
|
||||||
|
it makes are done using the certificate directory's trust root, not an \
|
||||||
|
arbitrary key. Further, the certificates are marked as \
|
||||||
|
non-exportable. The former makes it easier to manage certifications, \
|
||||||
|
especially when your certification key is offline. And the latter \
|
||||||
|
improves your privacy, by reducing the chance that parts of your \
|
||||||
|
social graph are leaked when a certificate is shared.
|
||||||
|
|
||||||
|
By default a link never expires. Using the `--expiration` argument \
|
||||||
|
specific validity periods may be defined. It allows for providing a \
|
||||||
|
point in time for validity to end or a validity duration.
|
||||||
|
|
||||||
|
`sq pki link authorize` respects the reference time set by the \
|
||||||
|
top-level `--time` argument. It sets the link's creation time to the \
|
||||||
|
reference time.
|
||||||
|
",
|
||||||
|
after_help = AUTHORIZE_EXAMPLES,
|
||||||
|
)]
|
||||||
|
#[clap(group(ArgGroup::new("constraint").args(&["regex", "domain", "unconstrained"]).required(true).multiple(true)))]
|
||||||
|
pub struct AuthorizeCommand {
|
||||||
|
#[command(flatten)]
|
||||||
|
pub cert: CertDesignators<
|
||||||
|
cert_designator::CertArg,
|
||||||
|
cert_designator::CertPrefix,
|
||||||
|
cert_designator::OneValue>,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub userids: UserIDDesignators<
|
||||||
|
userid_designator::MaybeSelfSignedUserIDEmailAllArgs,
|
||||||
|
userid_designator::OptionalValue>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long = "amount",
|
||||||
|
value_name = "AMOUNT",
|
||||||
|
default_value = "full",
|
||||||
|
help = "Set the amount of trust",
|
||||||
|
long_help =
|
||||||
|
"Set the amount of trust. Values between 1 and 120 are meaningful. \
|
||||||
|
120 means fully trusted. Values less than 120 indicate the degree \
|
||||||
|
of trust. 60 is usually used for partially trusted.",
|
||||||
|
)]
|
||||||
|
pub amount: TrustAmount<u8>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long = "depth",
|
||||||
|
value_name = "TRUST_DEPTH",
|
||||||
|
default_value = "255",
|
||||||
|
help = "Set the trust depth",
|
||||||
|
long_help = "\
|
||||||
|
Set the trust depth (sometimes referred to as the trust level). 1 \
|
||||||
|
means CERTIFICATE is a trusted introducer (default), 2 means \
|
||||||
|
CERTIFICATE is a meta-trusted introducer and can authorize another \
|
||||||
|
trusted introducer, etc.",
|
||||||
|
)]
|
||||||
|
pub depth: u8,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long = "domain",
|
||||||
|
value_name = "DOMAIN",
|
||||||
|
help = "Add a domain constraint to the introducer",
|
||||||
|
long_help = "\
|
||||||
|
Add a domain constraint to the introducer.
|
||||||
|
|
||||||
|
Add a domain to constrain what certifications are respected. A \
|
||||||
|
certification made by the certificate is only respected if it is over \
|
||||||
|
a user ID with an email address in the specified domain. Multiple \
|
||||||
|
domains may be specified. In that case, one must match.",
|
||||||
|
)]
|
||||||
|
pub domain: Vec<String>,
|
||||||
|
#[clap(
|
||||||
|
long = "regex",
|
||||||
|
value_name = "REGEX",
|
||||||
|
help = "Add a regular expression to constrain the introducer",
|
||||||
|
long_help = "\
|
||||||
|
Add a regular expression to constrain the introducer.
|
||||||
|
|
||||||
|
Add a regular expression to constrain what certifications are \
|
||||||
|
respected. A certification made by the certificate is only respected \
|
||||||
|
if it is over a user ID that matches one of the specified regular \
|
||||||
|
expression. Multiple regular expressions may be specified. In that \
|
||||||
|
case, at least one must match.",
|
||||||
|
)]
|
||||||
|
pub regex: Vec<String>,
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
conflicts_with = "regex",
|
||||||
|
help = "Don't constrain the introducer",
|
||||||
|
long_help = "\
|
||||||
|
Don't constrain the introducer.
|
||||||
|
|
||||||
|
Normally an introducer is constrained so that only certain user IDs \
|
||||||
|
are respected, e.g., those that have an email address for a certain \
|
||||||
|
domain name. This option authorizes an introducer without \
|
||||||
|
constraining it in this way. Because this grants the introducer a lot \
|
||||||
|
of power, you have to opt in to this behavior explicitly.",
|
||||||
|
)]
|
||||||
|
pub unconstrained: bool,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long = "expiration",
|
||||||
|
value_name = "EXPIRATION",
|
||||||
|
default_value_t =
|
||||||
|
Expiration::Never,
|
||||||
|
help =
|
||||||
|
"Define EXPIRATION for the acceptance as ISO 8601 formatted string or \
|
||||||
|
custom duration.",
|
||||||
|
long_help =
|
||||||
|
"Define EXPIRATION for the acceptance as ISO 8601 formatted string or \
|
||||||
|
custom duration. \
|
||||||
|
If an ISO 8601 formatted string is provided, the validity period \
|
||||||
|
reaches from the reference time (may be set using `--time`) to \
|
||||||
|
the provided time. \
|
||||||
|
Custom durations starting from the reference time may be set using \
|
||||||
|
`N[ymwds]`, for N years, months, weeks, days, or seconds. \
|
||||||
|
The special keyword `never` sets an unlimited expiry.",
|
||||||
|
)]
|
||||||
|
pub expiration: Expiration,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long = "recreate",
|
||||||
|
help = "Recreate the signature even if the parameters did not change",
|
||||||
|
long_help = "\
|
||||||
|
Recreate the signature even if the parameters did not change
|
||||||
|
|
||||||
|
If the link parameters did not change, and thus creating a signature \
|
||||||
|
should not be necessary, we omit the operation. This flag can be given \
|
||||||
|
to force the signature to be recreated anyway.",
|
||||||
|
)]
|
||||||
|
pub recreate: bool,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
value_names = &["NAME", "VALUE"],
|
||||||
|
number_of_values = 2,
|
||||||
|
help = "Add a notation to the certification.",
|
||||||
|
long_help = "Add a notation to the certification. \
|
||||||
|
A user-defined notation's name must be of the form \
|
||||||
|
`name@a.domain.you.control.org`. If the notation's name starts \
|
||||||
|
with a `!`, then the notation is marked as being critical. If a \
|
||||||
|
consumer of a signature doesn't understand a critical notation, \
|
||||||
|
then it will ignore the signature. The notation is marked as \
|
||||||
|
being human readable."
|
||||||
|
)]
|
||||||
|
pub notation: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AUTHORIZE_EXAMPLES: Actions = Actions {
|
||||||
|
actions: &[
|
||||||
Action::Example(Example {
|
Action::Example(Example {
|
||||||
comment: "\
|
comment: "\
|
||||||
Accept the certificate EB28F26E2739A4870ECC47726F0073F60FD0CBF0 \
|
Add an unconstrained trusted introducer.",
|
||||||
with all of its self-signed user IDs as a trusted certification \
|
|
||||||
authority. That is, the certificate is considered a trust root.",
|
|
||||||
command: &[
|
command: &[
|
||||||
"sq", "pki", "link", "add",
|
"sq", "pki", "link", "authorize",
|
||||||
"--ca=*",
|
"--unconstrained",
|
||||||
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
|
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0"
|
||||||
"--all",
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Action::Example(Example {
|
Action::Example(Example {
|
||||||
comment: "\
|
comment: "\
|
||||||
Accept the certificate EB28F26E2739A4870ECC47726F0073F60FD0CBF0 \
|
Add a trusted introducer for example.org and example.com.",
|
||||||
with all of its self-signed user IDs as a trusted certification \
|
|
||||||
authority constrained to the domain example.org. That is, the \
|
|
||||||
certificate is considered a trusted introducer for example.org.",
|
|
||||||
command: &[
|
command: &[
|
||||||
"sq", "pki", "link", "add",
|
"sq", "pki", "link", "authorize",
|
||||||
"--ca=example.org",
|
"--domain=example.org",
|
||||||
|
"--domain=example.com",
|
||||||
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
|
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
|
||||||
"--all",
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Action::Example(Example {
|
Action::Example(Example {
|
||||||
comment: "\
|
comment: "\
|
||||||
Accept the certificate EB28F26E2739A4870ECC47726F0073F60FD0CBF0 \
|
Add a partially trusted introducer.",
|
||||||
with all of its self-signed user IDs as a partially trusted \
|
|
||||||
certification authority.",
|
|
||||||
command: &[
|
command: &[
|
||||||
"sq", "pki", "link", "add",
|
"sq", "pki", "link", "authorize",
|
||||||
"--ca=*",
|
"--unconstrained",
|
||||||
"--amount=60",
|
"--amount=60",
|
||||||
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
|
"--cert", "EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
|
||||||
"--all",
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
test_examples!(sq_pki_link_add, ADD_EXAMPLES);
|
test_examples!(sq_pki_link_authorize, AUTHORIZE_EXAMPLES);
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(
|
#[clap(
|
||||||
@ -389,7 +518,8 @@ test_examples!(sq_pki_link_add, ADD_EXAMPLES);
|
|||||||
"Retract links
|
"Retract links
|
||||||
|
|
||||||
This command retracts links that were previously created using `sq \
|
This command retracts links that were previously created using `sq \
|
||||||
pki link add`. See that subcommand's documentation for more details. \
|
pki link add` or `sq pki link authorize`. See that subcommand's \
|
||||||
|
documentation for more details. \
|
||||||
Note: this is called `retract` and not `remove`, because the \
|
Note: this is called `retract` and not `remove`, because the \
|
||||||
certifications are not removed. Instead a new certification is added, \
|
certifications are not removed. Instead a new certification is added, \
|
||||||
which says that the binding has not been authenticated.
|
which says that the binding has not been authenticated.
|
||||||
|
@ -256,8 +256,8 @@ pub fn generate(
|
|||||||
// Writing to key store. Provide some guidance.
|
// Writing to key store. Provide some guidance.
|
||||||
sq.hint(format_args!("If this is your key, you should mark it as a \
|
sq.hint(format_args!("If this is your key, you should mark it as a \
|
||||||
fully trusted introducer:"))
|
fully trusted introducer:"))
|
||||||
.sq().arg("pki").arg("link").arg("add")
|
.sq().arg("pki").arg("link").arg("authorize")
|
||||||
.arg_value("--ca", "*")
|
.arg("--unconstrained")
|
||||||
.arg("--cert").arg(cert.fingerprint())
|
.arg("--cert").arg(cert.fingerprint())
|
||||||
.arg("--all")
|
.arg("--all")
|
||||||
.done();
|
.done();
|
||||||
|
@ -639,7 +639,7 @@ impl Method {
|
|||||||
wprintln!(
|
wprintln!(
|
||||||
"Created the local CA {} for certifying \
|
"Created the local CA {} for certifying \
|
||||||
certificates downloaded from this service. \
|
certificates downloaded from this service. \
|
||||||
Use `sq pki link add --ca '*' --amount N {}` \
|
Use `sq pki link authorize --unconstrained --amount N {}` \
|
||||||
to change how much it is trusted. Or \
|
to change how much it is trusted. Or \
|
||||||
`sq pki link retract {}` to disable it.",
|
`sq pki link retract {}` to disable it.",
|
||||||
if let Ok(cert) = cert.to_cert() {
|
if let Ok(cert) = cert.to_cert() {
|
||||||
|
@ -18,6 +18,7 @@ pub fn link(sq: Sq, c: link::Command) -> Result<()> {
|
|||||||
use link::Subcommands::*;
|
use link::Subcommands::*;
|
||||||
match c.subcommand {
|
match c.subcommand {
|
||||||
Add(c) => add(sq, c)?,
|
Add(c) => add(sq, c)?,
|
||||||
|
Authorize(c) => authorize(sq, c)?,
|
||||||
Retract(c) => retract(sq, c)?,
|
Retract(c) => retract(sq, c)?,
|
||||||
List(c) => list(sq, c)?,
|
List(c) => list(sq, c)?,
|
||||||
}
|
}
|
||||||
@ -36,34 +37,6 @@ pub fn add(sq: Sq, c: link::AddCommand)
|
|||||||
let vc = cert.with_policy(sq.policy, Some(sq.time))?;
|
let vc = cert.with_policy(sq.policy, Some(sq.time))?;
|
||||||
let userids = c.userids.resolve(&vc)?;
|
let userids = c.userids.resolve(&vc)?;
|
||||||
|
|
||||||
let trust_depth: u8 = if let Some(depth) = c.depth {
|
|
||||||
depth
|
|
||||||
} else if ! c.ca.is_empty() {
|
|
||||||
255
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut regex = c.regex;
|
|
||||||
if trust_depth == 0 && !regex.is_empty() {
|
|
||||||
return Err(
|
|
||||||
anyhow::format_err!("A regex only makes sense \
|
|
||||||
if the trust depth is greater than 0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut star = false;
|
|
||||||
for domain in c.ca.iter() {
|
|
||||||
if domain == "*" {
|
|
||||||
star = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's a catch all, we don't need to add any regular
|
|
||||||
// expressions.
|
|
||||||
if star {
|
|
||||||
regex = Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let notations = parse_notations(c.notation)?;
|
let notations = parse_notations(c.notation)?;
|
||||||
|
|
||||||
let templates = if c.temporary {
|
let templates = if c.temporary {
|
||||||
@ -93,13 +66,59 @@ pub fn add(sq: Sq, c: link::AddCommand)
|
|||||||
true, // Add userid.
|
true, // Add userid.
|
||||||
true, // User-supplied user IDs.
|
true, // User-supplied user IDs.
|
||||||
&templates,
|
&templates,
|
||||||
trust_depth,
|
0, // Trust depth.
|
||||||
if star {
|
&[][..], // Domain.
|
||||||
&[][..]
|
&[][..], // Regex.
|
||||||
} else {
|
true, // Local.
|
||||||
&c.ca[..]
|
false, // Non-revocable.
|
||||||
},
|
¬ations[..],
|
||||||
®ex[..],
|
None, // Output.
|
||||||
|
false) // Binary.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn authorize(sq: Sq, c: link::AuthorizeCommand)
|
||||||
|
-> Result<()>
|
||||||
|
{
|
||||||
|
let trust_root = sq.local_trust_root()?;
|
||||||
|
let trust_root = trust_root.to_cert()?;
|
||||||
|
|
||||||
|
let (cert, _from_file)
|
||||||
|
= sq.resolve_cert(&c.cert, sequoia_wot::FULLY_TRUSTED)?;
|
||||||
|
|
||||||
|
let vc = cert.with_policy(sq.policy, Some(sq.time))?;
|
||||||
|
let mut userids = c.userids.resolve(&vc)?;
|
||||||
|
let user_supplied_userids = if userids.is_empty() {
|
||||||
|
// Use all self-signed User IDs.
|
||||||
|
userids = vc.userids()
|
||||||
|
.map(|ua| ua.userid().clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if userids.is_empty() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"{} has no self-signed user IDs, and you didn't provide \
|
||||||
|
an alternate user ID",
|
||||||
|
vc.fingerprint()));
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
let notations = parse_notations(c.notation)?;
|
||||||
|
|
||||||
|
crate::common::pki::certify::certify(
|
||||||
|
&sq,
|
||||||
|
c.recreate, // Recreate.
|
||||||
|
&trust_root,
|
||||||
|
&cert,
|
||||||
|
&userids[..],
|
||||||
|
c.userids.add_userid().unwrap_or(false),
|
||||||
|
user_supplied_userids,
|
||||||
|
&[(c.amount, c.expiration)][..],
|
||||||
|
c.depth,
|
||||||
|
&c.domain[..],
|
||||||
|
&c.regex[..],
|
||||||
true, // Local.
|
true, // Local.
|
||||||
false, // Non-revocable.
|
false, // Non-revocable.
|
||||||
¬ations[..],
|
¬ations[..],
|
||||||
|
@ -23,6 +23,7 @@ mod integration {
|
|||||||
mod sq_pki_certify;
|
mod sq_pki_certify;
|
||||||
mod sq_pki_authorize;
|
mod sq_pki_authorize;
|
||||||
mod sq_pki_link;
|
mod sq_pki_link;
|
||||||
|
mod sq_pki_link_authorize;
|
||||||
mod sq_sign;
|
mod sq_sign;
|
||||||
mod sq_toolbox_keyring_filter;
|
mod sq_toolbox_keyring_filter;
|
||||||
mod sq_toolbox_packet_decrypt;
|
mod sq_toolbox_packet_decrypt;
|
||||||
|
@ -1520,6 +1520,74 @@ impl Sq {
|
|||||||
self.pki_link_add_maybe(args, cert, userids).expect("success")
|
self.pki_link_add_maybe(args, cert, userids).expect("success")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a link for the binding.
|
||||||
|
pub fn pki_link_retract_maybe(&self, extra_args: &[&str],
|
||||||
|
cert: KeyHandle,
|
||||||
|
userids: &[&str])
|
||||||
|
-> Result<()>
|
||||||
|
{
|
||||||
|
let mut cmd = self.command();
|
||||||
|
cmd.args([ "pki", "link", "retract" ]);
|
||||||
|
for arg in extra_args {
|
||||||
|
cmd.arg(arg);
|
||||||
|
}
|
||||||
|
cmd.arg("--cert").arg(cert.to_string());
|
||||||
|
for userid in userids {
|
||||||
|
cmd.arg("--userid").arg(userid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = self.run(cmd, None);
|
||||||
|
if output.status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!(format!(
|
||||||
|
"Command failed:\n{}",
|
||||||
|
String::from_utf8_lossy(&output.stderr))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a link for the binding.
|
||||||
|
pub fn pki_link_retract(&self, args: &[&str],
|
||||||
|
cert: KeyHandle, userids: &[&str])
|
||||||
|
{
|
||||||
|
self.pki_link_retract_maybe(args, cert, userids)
|
||||||
|
.expect("success")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a link for the binding.
|
||||||
|
pub fn pki_link_authorize_maybe(&self, extra_args: &[&str],
|
||||||
|
cert: KeyHandle,
|
||||||
|
userids: &[&str])
|
||||||
|
-> Result<()>
|
||||||
|
{
|
||||||
|
let mut cmd = self.command();
|
||||||
|
cmd.args([ "pki", "link", "authorize" ]);
|
||||||
|
for arg in extra_args {
|
||||||
|
cmd.arg(arg);
|
||||||
|
}
|
||||||
|
cmd.arg("--cert").arg(cert.to_string());
|
||||||
|
for userid in userids {
|
||||||
|
cmd.arg("--userid").arg(userid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = self.run(cmd, None);
|
||||||
|
if output.status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!(format!(
|
||||||
|
"Command failed:\n{}",
|
||||||
|
String::from_utf8_lossy(&output.stderr))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a link for the binding.
|
||||||
|
pub fn pki_link_authorize(&self, args: &[&str],
|
||||||
|
cert: KeyHandle, userids: &[&str])
|
||||||
|
{
|
||||||
|
self.pki_link_authorize_maybe(args, cert, userids)
|
||||||
|
.expect("success")
|
||||||
|
}
|
||||||
|
|
||||||
/// Authenticate a binding.
|
/// Authenticate a binding.
|
||||||
pub fn pki_authenticate(&self, extra_args: &[&str],
|
pub fn pki_authenticate(&self, extra_args: &[&str],
|
||||||
cert: &str, userid: &str)
|
cert: &str, userid: &str)
|
||||||
|
@ -321,7 +321,9 @@ fn sq_pki_link_add_retract() -> Result<()> {
|
|||||||
|
|
||||||
// Accept Alice as a trusted introducer. We should be able to
|
// Accept Alice as a trusted introducer. We should be able to
|
||||||
// verify Alice, Bob, and Carol's signatures.
|
// verify Alice, Bob, and Carol's signatures.
|
||||||
sq_link(&sq, &alice_fpr, &[ &alice_userid ], &[], &["--ca", "*"], true);
|
sq.pki_link_authorize(&["--time", &tick(), "--unconstrained"],
|
||||||
|
alice.cert.key_handle(),
|
||||||
|
&[ &alice_userid ]);
|
||||||
|
|
||||||
sq_verify(&sq, None, &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(&sq, None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(&sq, None, &[], &[], &bob.sig_file, 1, 0);
|
sq_verify(&sq, None, &[], &[], &bob.sig_file, 1, 0);
|
||||||
@ -338,7 +340,9 @@ fn sq_pki_link_add_retract() -> Result<()> {
|
|||||||
|
|
||||||
// Accept Alice as a trusted introducer again. We should be able
|
// Accept Alice as a trusted introducer again. We should be able
|
||||||
// to verify Alice, Bob, and Carol's signatures.
|
// to verify Alice, Bob, and Carol's signatures.
|
||||||
sq_link(&sq, &alice_fpr, &[ &alice_userid ], &[], &["--ca", "*"], true);
|
sq.pki_link_authorize(&["--time", &tick(), "--unconstrained"],
|
||||||
|
alice.cert.key_handle(),
|
||||||
|
&[ &alice_userid ]);
|
||||||
|
|
||||||
sq_verify(&sq, None, &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(&sq, None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(&sq, None, &[], &[], &bob.sig_file, 1, 0);
|
sq_verify(&sq, None, &[], &[], &bob.sig_file, 1, 0);
|
||||||
@ -367,8 +371,9 @@ fn sq_pki_link_add_retract() -> Result<()> {
|
|||||||
|
|
||||||
// Change Alice's acceptance to be a ca, but only for example.org,
|
// Change Alice's acceptance to be a ca, but only for example.org,
|
||||||
// i.e., not for Dave.
|
// i.e., not for Dave.
|
||||||
sq_link(&sq, &alice_fpr, &[ &alice_userid ], &[], &["--ca", "example.org"],
|
sq.pki_link_authorize(&["--time", &tick(), "--domain", "example.org"],
|
||||||
true);
|
alice.cert.key_handle(),
|
||||||
|
&[ &alice_userid ]);
|
||||||
|
|
||||||
sq_verify(&sq, None, &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(&sq, None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(&sq, None, &[], &[], &bob.sig_file, 1, 0);
|
sq_verify(&sq, None, &[], &[], &bob.sig_file, 1, 0);
|
||||||
@ -493,16 +498,14 @@ fn sq_pki_link_update_detection() -> Result<()> {
|
|||||||
let bytes = compare(bytes, &alice_cert_pgp, true);
|
let bytes = compare(bytes, &alice_cert_pgp, true);
|
||||||
|
|
||||||
// Make Alice a CA.
|
// Make Alice a CA.
|
||||||
let output = sq_link(&sq, &alice_fpr, &[], &[],
|
sq.pki_link_authorize(&["--time", &tick(), "--unconstrained"],
|
||||||
&["--ca", "*", "--all"], true);
|
alice.key_handle(),
|
||||||
assert!(output.2.contains("was previously"),
|
&[]);
|
||||||
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
|
||||||
let bytes = compare(bytes, &alice_cert_pgp, false);
|
let bytes = compare(bytes, &alice_cert_pgp, false);
|
||||||
|
|
||||||
let output = sq_link(&sq, &alice_fpr, &[], &[],
|
sq.pki_link_authorize(&["--time", &tick(), "--unconstrained", "--all"],
|
||||||
&["--ca", "*", "--all"], true);
|
alice.key_handle(),
|
||||||
assert!(output.2.contains("Certification parameters are unchanged"),
|
&[]);
|
||||||
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
|
||||||
let bytes = compare(bytes, &alice_cert_pgp, true);
|
let bytes = compare(bytes, &alice_cert_pgp, true);
|
||||||
|
|
||||||
// Make her a partially trusted CA.
|
// Make her a partially trusted CA.
|
||||||
@ -532,13 +535,13 @@ fn sq_pki_link_update_detection() -> Result<()> {
|
|||||||
|
|
||||||
// Link it again.
|
// Link it again.
|
||||||
let output = sq_link(&sq, &alice_fpr, &[], &[],
|
let output = sq_link(&sq, &alice_fpr, &[], &[],
|
||||||
&["--depth", "10", "--amount", "10", "--all"], true);
|
&["--amount", "10", "--all"], true);
|
||||||
assert!(output.2.contains("was retracted"),
|
assert!(output.2.contains("was retracted"),
|
||||||
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
||||||
let bytes = compare(bytes, &alice_cert_pgp, false);
|
let bytes = compare(bytes, &alice_cert_pgp, false);
|
||||||
|
|
||||||
let output = sq_link(&sq, &alice_fpr, &[], &[],
|
let output = sq_link(&sq, &alice_fpr, &[], &[],
|
||||||
&["--depth", "10", "--amount", "10", "--all"], true);
|
&["--amount", "10", "--all"], true);
|
||||||
assert!(output.2.contains("Certification parameters are unchanged"),
|
assert!(output.2.contains("Certification parameters are unchanged"),
|
||||||
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
||||||
let bytes = compare(bytes, &alice_cert_pgp, true);
|
let bytes = compare(bytes, &alice_cert_pgp, true);
|
||||||
|
455
tests/integration/sq_pki_link_authorize.rs
Normal file
455
tests/integration/sq_pki_link_authorize.rs
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
use super::common::Sq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sq_pki_link_authorize_then_authenticate() {
|
||||||
|
let ca_example_org = "<ca@example.org>";
|
||||||
|
|
||||||
|
for (e, userids) in &[
|
||||||
|
(&[][..], &[ca_example_org][..]),
|
||||||
|
// Implicitly use all self-signed user IDs.
|
||||||
|
(&[], &[]),
|
||||||
|
// Use a non-self signed user ID.
|
||||||
|
(&["--add-userid"], &["frank"]),
|
||||||
|
] {
|
||||||
|
let mut sq = Sq::new();
|
||||||
|
|
||||||
|
let (ca, ca_pgp, _ca_rev)
|
||||||
|
= sq.key_generate(&[], &[ca_example_org]);
|
||||||
|
sq.key_import(&ca_pgp);
|
||||||
|
|
||||||
|
let alice_example_org = "<alice@example.org>";
|
||||||
|
let (alice, alice_pgp, _alice_rev)
|
||||||
|
= sq.key_generate(&[], &[alice_example_org]);
|
||||||
|
sq.key_import(&alice_pgp);
|
||||||
|
|
||||||
|
let bob_example_org = "<bob@example.org>";
|
||||||
|
let bob_other_org = "<bob@other.org>";
|
||||||
|
let (bob, bob_pgp, _bob_rev)
|
||||||
|
= sq.key_generate(&[], &[bob_example_org, bob_other_org]);
|
||||||
|
sq.key_import(&bob_pgp);
|
||||||
|
|
||||||
|
sq.tick(1);
|
||||||
|
|
||||||
|
// The ca certifies alice's and bob's certificates for each of
|
||||||
|
// their user IDs.
|
||||||
|
let certification = sq.scratch_file(None);
|
||||||
|
sq.pki_certify(&[],
|
||||||
|
ca.key_handle(), alice.key_handle(),
|
||||||
|
&[ alice_example_org ],
|
||||||
|
certification.as_path());
|
||||||
|
sq.cert_import(&certification);
|
||||||
|
|
||||||
|
let certification = sq.scratch_file(None);
|
||||||
|
sq.pki_certify(&[],
|
||||||
|
ca.key_handle(), bob.key_handle(),
|
||||||
|
&[ bob_example_org, bob_other_org ],
|
||||||
|
certification.as_path());
|
||||||
|
sq.cert_import(certification);
|
||||||
|
|
||||||
|
// Check whether we can authenticate alice's and bob's
|
||||||
|
// certificates for their user ID.
|
||||||
|
let check = |sq: &Sq,
|
||||||
|
can_authenticate_alice,
|
||||||
|
can_authenticate_bob1,
|
||||||
|
can_authenticate_bob2|
|
||||||
|
{
|
||||||
|
for (cert, userid, can_authenticate) in &[
|
||||||
|
(&alice, alice_example_org, can_authenticate_alice),
|
||||||
|
(&bob, bob_example_org, can_authenticate_bob1),
|
||||||
|
(&bob, bob_other_org, can_authenticate_bob2),
|
||||||
|
]
|
||||||
|
{
|
||||||
|
let r = sq.pki_authenticate(
|
||||||
|
&[],
|
||||||
|
&cert.fingerprint().to_string(),
|
||||||
|
userid);
|
||||||
|
|
||||||
|
match (can_authenticate, r.is_ok()) {
|
||||||
|
(true, false) => {
|
||||||
|
panic!("Expected to authenticated {}, but didn't.",
|
||||||
|
userid);
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
panic!("Expected to NOT authenticated {}, but did.",
|
||||||
|
userid);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn concat<'a>(args1: &[&'a str], args2: &[&'a str])
|
||||||
|
-> Vec<&'a str>
|
||||||
|
{
|
||||||
|
let mut args = args1.to_vec();
|
||||||
|
args.extend(args2.into_iter());
|
||||||
|
args
|
||||||
|
}
|
||||||
|
|
||||||
|
// No delegation yet.
|
||||||
|
println!("CA: not authorized");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
false, // Alice.
|
||||||
|
false, // bob@example.org
|
||||||
|
false);// bob@other.org
|
||||||
|
|
||||||
|
// The user completely authorizes the CA.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--unconstrained"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized, and unconstrained");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
true, // alice@example.org
|
||||||
|
true, // bob@example.org
|
||||||
|
true);// bob@other.org
|
||||||
|
|
||||||
|
// The user authorizes the CA with the regex contraint "example".
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--regex", "example"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for \"example\"");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
true, // alice@example.org
|
||||||
|
true, // bob@example.org
|
||||||
|
false);// bob@other.org
|
||||||
|
|
||||||
|
// The user authorizes the CA with the domain contraint
|
||||||
|
// "example.org".
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--domain", "example.org"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for \"example.org\"");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
true, // alice@example.org
|
||||||
|
true, // bob@example.org
|
||||||
|
false);// bob@other.org
|
||||||
|
|
||||||
|
// The user authorizes the CA with the regex contraint "other".
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--regex", "other"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for \"other\"");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
false, // alice@example.org
|
||||||
|
false, // bob@example.org
|
||||||
|
true); // bob@other.org
|
||||||
|
|
||||||
|
// The user authorizes the CA with the regex contraint "bob".
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--regex", "bob"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for \"bob\"");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
false, // alice@example.org
|
||||||
|
true, // bob@example.org
|
||||||
|
true); // bob@other.org
|
||||||
|
|
||||||
|
// The user authorizes the CA with the regex contraint "bob" or
|
||||||
|
// "alice".
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--regex", "bob",
|
||||||
|
"--regex", "alice"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for \"bob\" or \"alice\"");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
true, // alice@example.org
|
||||||
|
true, // bob@example.org
|
||||||
|
true);// bob@other.org
|
||||||
|
|
||||||
|
|
||||||
|
// The user authorizes the CA for the domains example.org and
|
||||||
|
// other.org.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--domain", "example.org",
|
||||||
|
"--domain", "other.org"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for the domains example.org and other.org");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
true, // alice@example.org
|
||||||
|
true, // bob@example.org
|
||||||
|
true);// bob@other.org
|
||||||
|
|
||||||
|
|
||||||
|
// The user authorizes the CA for the domain example.com and the
|
||||||
|
// regex alice.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--domain", "other.org",
|
||||||
|
"--regex", "alice"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for the domains example.org and other.org");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
true, // alice@example.org
|
||||||
|
false, // bob@example.org
|
||||||
|
true); // bob@other.org
|
||||||
|
|
||||||
|
|
||||||
|
// The user authorizes the CA with the regex contraint "zoo".
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--regex", "zoo"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for \"zoo\"");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
false, // alice@example.org
|
||||||
|
false, // bob@example.org
|
||||||
|
false);// bob@other.org
|
||||||
|
|
||||||
|
// The user authorizes the CA with the domain contraint "example".
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&concat(e, &["--domain", "example"]),
|
||||||
|
ca.key_handle(),
|
||||||
|
userids);
|
||||||
|
|
||||||
|
println!("CA: authorized for \"zoo\"");
|
||||||
|
check(
|
||||||
|
&sq,
|
||||||
|
false, // alice@example.org
|
||||||
|
false, // bob@example.org
|
||||||
|
false);// bob@other.org
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn retract_explicit() {
|
||||||
|
// Authorize a CA via multiple user IDs. Retract one
|
||||||
|
// authorization at a time. Make sure the CA remains a trusted
|
||||||
|
// introducer until all authorizations are retracted.
|
||||||
|
|
||||||
|
let ca_example_org = "<ca@example.org>";
|
||||||
|
let ca_example_com = "<ca@example.com>";
|
||||||
|
let self_signed_userids = &[ca_example_org, ca_example_com][..];
|
||||||
|
|
||||||
|
// When authorizing the CA we can either authorize implicitly
|
||||||
|
// (i.e., all self-signed user IDs) or explicitly. Try both.
|
||||||
|
for implicit_all in [true, false] {
|
||||||
|
eprintln!("implicit all = {}", implicit_all);
|
||||||
|
|
||||||
|
let mut sq = Sq::new();
|
||||||
|
let (ca, ca_pgp, _ca_rev)
|
||||||
|
= sq.key_generate(&[], self_signed_userids);
|
||||||
|
sq.key_import(&ca_pgp);
|
||||||
|
|
||||||
|
let alice_example_org = "<alice@example.org>";
|
||||||
|
let (alice, alice_pgp, _alice_rev)
|
||||||
|
= sq.key_generate(&[], &[alice_example_org]);
|
||||||
|
sq.key_import(&alice_pgp);
|
||||||
|
|
||||||
|
sq.tick(1);
|
||||||
|
|
||||||
|
// The ca certifies alice's certificate
|
||||||
|
let certification = sq.scratch_file(None);
|
||||||
|
sq.pki_certify(&[],
|
||||||
|
ca.key_handle(), alice.key_handle(),
|
||||||
|
&[ alice_example_org ],
|
||||||
|
certification.as_path());
|
||||||
|
sq.cert_import(&certification);
|
||||||
|
|
||||||
|
let check = |sq: &Sq, can_authenticate: bool| {
|
||||||
|
let r = sq.pki_authenticate(
|
||||||
|
&[],
|
||||||
|
&alice.fingerprint().to_string(),
|
||||||
|
alice_example_org);
|
||||||
|
|
||||||
|
match (can_authenticate, r.is_ok()) {
|
||||||
|
(true, false) => {
|
||||||
|
panic!("Expected to authenticated {}, but didn't.",
|
||||||
|
alice_example_org);
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
panic!("Expected to NOT authenticated {}, but did.",
|
||||||
|
alice_example_org);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The ca is not yet authorized so we shouldn't be able to
|
||||||
|
// authenticate alice.
|
||||||
|
check(&sq, false);
|
||||||
|
|
||||||
|
// Authorize via all user IDs.
|
||||||
|
sq.tick(1);
|
||||||
|
let userids = self_signed_userids;
|
||||||
|
sq.pki_link_authorize(&["--unconstrained"],
|
||||||
|
ca.key_handle(),
|
||||||
|
if implicit_all {
|
||||||
|
&[]
|
||||||
|
} else {
|
||||||
|
userids
|
||||||
|
});
|
||||||
|
check(&sq, true);
|
||||||
|
|
||||||
|
for (i, userid) in userids.iter().enumerate() {
|
||||||
|
// Retract the authorization of one user ID. It should
|
||||||
|
// still be a trusted introducer.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_retract(&[], ca.key_handle(), &[userid]);
|
||||||
|
|
||||||
|
if i == userids.len() - 1 {
|
||||||
|
// We've retracted all authorizations. This should
|
||||||
|
// now fail.
|
||||||
|
check(&sq, false);
|
||||||
|
} else {
|
||||||
|
check(&sq, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn retract_non_self_signed() {
|
||||||
|
// Make sure we can retract non-self-signed user IDs.
|
||||||
|
|
||||||
|
let self_signed = "<ca@example.org>";
|
||||||
|
let non_self_signed = "<ca@example.com>";
|
||||||
|
|
||||||
|
let mut sq = Sq::new();
|
||||||
|
let (ca, ca_pgp, _ca_rev)
|
||||||
|
= sq.key_generate(&[], &[self_signed]);
|
||||||
|
sq.key_import(&ca_pgp);
|
||||||
|
|
||||||
|
let alice_example_org = "<alice@example.org>";
|
||||||
|
let (alice, alice_pgp, _alice_rev)
|
||||||
|
= sq.key_generate(&[], &[alice_example_org]);
|
||||||
|
sq.key_import(&alice_pgp);
|
||||||
|
|
||||||
|
sq.tick(1);
|
||||||
|
|
||||||
|
// The ca certifies alice's certificate
|
||||||
|
let certification = sq.scratch_file(None);
|
||||||
|
sq.pki_certify(&[],
|
||||||
|
ca.key_handle(), alice.key_handle(),
|
||||||
|
&[ alice_example_org ],
|
||||||
|
certification.as_path());
|
||||||
|
sq.cert_import(&certification);
|
||||||
|
|
||||||
|
let check = |sq: &Sq, can_authenticate: bool| {
|
||||||
|
let r = sq.pki_authenticate(
|
||||||
|
&[],
|
||||||
|
&alice.fingerprint().to_string(),
|
||||||
|
alice_example_org);
|
||||||
|
|
||||||
|
match (can_authenticate, r.is_ok()) {
|
||||||
|
(true, false) => {
|
||||||
|
panic!("Expected to authenticated {}, but didn't.",
|
||||||
|
alice_example_org);
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
panic!("Expected to NOT authenticated {}, but did.",
|
||||||
|
alice_example_org);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The ca is not yet authorized so we shouldn't be able to
|
||||||
|
// authenticate alice.
|
||||||
|
check(&sq, false);
|
||||||
|
|
||||||
|
// Authorize the CA via a non-self-signed user ID. Now we can
|
||||||
|
// authenticate alice.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&["--unconstrained", "--add-userid"],
|
||||||
|
ca.key_handle(),
|
||||||
|
&[non_self_signed]);
|
||||||
|
check(&sq, true);
|
||||||
|
|
||||||
|
// Retract the authorization. It should no longer be considered a
|
||||||
|
// trusted introducer.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_retract(&[], ca.key_handle(), &[non_self_signed]);
|
||||||
|
check(&sq, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn retract_all() {
|
||||||
|
// Link all self-signed user IDs and a non-self-signed user ID.
|
||||||
|
// When we retract all, make sure they are all retracted.
|
||||||
|
|
||||||
|
let self_signed = "<ca@example.org>";
|
||||||
|
let non_self_signed = "<ca@example.com>";
|
||||||
|
|
||||||
|
let mut sq = Sq::new();
|
||||||
|
let (ca, ca_pgp, _ca_rev)
|
||||||
|
= sq.key_generate(&[], &[self_signed]);
|
||||||
|
sq.key_import(&ca_pgp);
|
||||||
|
|
||||||
|
let alice_example_org = "<alice@example.org>";
|
||||||
|
let (alice, alice_pgp, _alice_rev)
|
||||||
|
= sq.key_generate(&[], &[alice_example_org]);
|
||||||
|
sq.key_import(&alice_pgp);
|
||||||
|
|
||||||
|
sq.tick(1);
|
||||||
|
|
||||||
|
// The ca certifies alice's certificate
|
||||||
|
let certification = sq.scratch_file(None);
|
||||||
|
sq.pki_certify(&[],
|
||||||
|
ca.key_handle(), alice.key_handle(),
|
||||||
|
&[ alice_example_org ],
|
||||||
|
certification.as_path());
|
||||||
|
sq.cert_import(&certification);
|
||||||
|
|
||||||
|
let check = |sq: &Sq, can_authenticate: bool| {
|
||||||
|
let r = sq.pki_authenticate(
|
||||||
|
&[],
|
||||||
|
&alice.fingerprint().to_string(),
|
||||||
|
alice_example_org);
|
||||||
|
|
||||||
|
match (can_authenticate, r.is_ok()) {
|
||||||
|
(true, false) => {
|
||||||
|
panic!("Expected to authenticated {}, but didn't.",
|
||||||
|
alice_example_org);
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
panic!("Expected to NOT authenticated {}, but did.",
|
||||||
|
alice_example_org);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The ca is not yet authorized so we shouldn't be able to
|
||||||
|
// authenticate alice.
|
||||||
|
check(&sq, false);
|
||||||
|
|
||||||
|
// Authorize the CA via a non-self-signed user ID. Now we can
|
||||||
|
// authenticate alice.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_authorize(&["--unconstrained", "--add-userid"],
|
||||||
|
ca.key_handle(),
|
||||||
|
&[self_signed, non_self_signed]);
|
||||||
|
check(&sq, true);
|
||||||
|
|
||||||
|
// Retract all authorizations. It should no longer be considered
|
||||||
|
// a trusted introducer.
|
||||||
|
sq.tick(1);
|
||||||
|
sq.pki_link_retract(&[], ca.key_handle(), &[]);
|
||||||
|
check(&sq, false);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user