Skip to content

Commit 61a2c3e

Browse files
authored
Use codephrases for IDs and signatures (#12)
1 parent 268da85 commit 61a2c3e

19 files changed

Lines changed: 924 additions & 348 deletions

‎Cargo.lock‎

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ resolver = "2"
55
[workspace.dependencies]
66
async-recursion = "1"
77
blake3 = "1"
8-
base64 = "0.22"
98
bytesize = "2"
109
chrono = "0.4"
1110
clap = { version = "4", features = ["derive", "env", "wrap_help"] }
11+
crypto-bigint = "0.5"
1212
dropshot = "0.16"
1313
ed25519-dalek = { version = "2", features = ["rand_core"] }
1414
humantime = "2"
@@ -18,19 +18,19 @@ pem-rfc7468 = { version = "0.7", features = ["std"] }
1818
permission-slip-client = { git = "https://github.com/oxidecomputer/permission-slip" }
1919
permission-slip-common = { git = "https://github.com/oxidecomputer/permission-slip" }
2020
progenitor = "0.11"
21+
rand = "0.8"
2122
rand_core = "0.6"
2223
reqwest = "0.12"
2324
rlimit = "0.10"
24-
rusqlite = { version = "0.37", features = ["blob", "chrono", "limits", "modern_sqlite", "uuid"] }
25+
rusqlite = { version = "0.37", features = ["blob", "chrono", "limits", "modern_sqlite"] }
2526
rustyline = "17"
26-
schemars = { version = "0.8", features = ["chrono", "preserve_order", "uuid1"] }
27+
schemars = { version = "0.8", features = ["chrono", "preserve_order"] }
2728
serde = "1"
2829
serde_json = "1"
2930
sha2 = "0.10"
3031
shlex = "1"
3132
tempfile = "3"
3233
thiserror = "1"
3334
tokio = { version = "1", features = ["macros", "process", "rt", "sync", "time"] }
34-
uuid = { version = "1", features = ["serde", "v4"] }
3535
x509-cert = { version = "0.2", features = ["std"] }
3636
xdg = "3"

‎client/Cargo.toml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ blake3.workspace = true
1515
bytesize.workspace = true
1616
chrono.workspace = true
1717
clap.workspace = true
18+
ed25519-dalek.workspace = true
1819
humantime.workspace = true
1920
libc.workspace = true
21+
p256.workspace = true
2022
pem-rfc7468.workspace = true
2123
permission-slip-client.workspace = true
2224
permission-slip-common.workspace = true
@@ -29,6 +31,5 @@ shlex.workspace = true
2931
sush-common = { path = "../common" }
3032
thiserror.workspace = true
3133
tokio.workspace = true
32-
uuid.workspace = true
3334
x509-cert.workspace = true
3435
xdg.workspace = true

‎client/src/cli.rs‎

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl CommandContext for Cli {
6464
return Err(CommandError::InvalidRootCert);
6565
}
6666
let tbs = root.tbs_certificate.to_der()?;
67-
Signature::new(root.signature.raw_bytes().to_vec()).verify(&tbs, root)?;
67+
Signature::try_from(root)?.verify(&tbs, &root.tbs_certificate.subject_public_key_info)?;
6868
if matches!(self.get_output_format(), OutputFormat::Text) {
6969
println!(
7070
"✅ Verified root certificate for subject `{}`",
@@ -75,7 +75,8 @@ impl CommandContext for Cli {
7575
let mut prev = root;
7676
for cert in rest {
7777
let tbs = cert.tbs_certificate.to_der()?;
78-
Signature::new(cert.signature.raw_bytes().to_vec()).verify(&tbs, prev)?;
78+
Signature::try_from(cert)?
79+
.verify(&tbs, &prev.tbs_certificate.subject_public_key_info)?;
7980
prev = cert;
8081
if matches!(self.get_output_format(), OutputFormat::Text) {
8182
println!(
@@ -117,10 +118,9 @@ impl CommandContext for Cli {
117118
if reserved.is_empty() {
118119
println!("✅ No reserved jobs");
119120
} else {
120-
println!("✅ {} reserved jobs", reserved.len());
121-
println!("{:40}{}", "Job ID", "Time Reserved");
122-
for (job_id, time) in reserved {
123-
println!("{job_id:40}{time}");
121+
println!("✅ {} reserved jobs:", reserved.len());
122+
for job_id in reserved.keys() {
123+
println!("{job_id}");
124124
}
125125
}
126126
}
@@ -132,7 +132,7 @@ impl CommandContext for Cli {
132132
match self.get_output_format() {
133133
OutputFormat::Json => println!("{}", json!(reserved)),
134134
OutputFormat::Text => {
135-
println!("✅ Read {} reserved job IDs", reserved.job_ids.len());
135+
println!("✅ Read {} reserved jobs", reserved.job_ids.len());
136136
}
137137
}
138138
Ok(())
@@ -149,9 +149,9 @@ impl CommandContext for Cli {
149149
let n = job_ids.len();
150150
match n {
151151
0 => println!("✅ No jobs reserved"),
152-
1 => println!("✅ Reserved job ID {} at {}", job_ids[0], time_reserved),
152+
1 => println!("✅ Reserved job `{}` at {}", job_ids[0], time_reserved),
153153
_ => {
154-
println!("✅ Reserved {n} job IDs at {time_reserved}:");
154+
println!("✅ Reserved {n} jobs at {time_reserved}:");
155155
for job_id in &reserved.job_ids {
156156
println!("{job_id}");
157157
}
@@ -162,17 +162,17 @@ impl CommandContext for Cli {
162162
Ok(())
163163
}
164164

165-
fn job_aborted(&mut self, job_id: JobId) -> Result<(), CommandError> {
165+
fn job_aborted(&mut self, job_id: &JobId) -> Result<(), CommandError> {
166166
match self.get_output_format() {
167167
OutputFormat::Json => println!("{job_id}"),
168-
OutputFormat::Text => println!("✅ Aborted job {job_id}"),
168+
OutputFormat::Text => println!("✅ Aborted job `{job_id}`"),
169169
}
170170
Ok(())
171171
}
172172

173173
fn job_stdout(
174174
&mut self,
175-
job_id: JobId,
175+
_job_id: &JobId,
176176
output: &[u8],
177177
binary: bool,
178178
) -> Result<(), CommandError> {
@@ -182,7 +182,7 @@ impl CommandContext for Cli {
182182
OutputFormat::Text if binary => stdout().write(output).map(|_| ())?,
183183
OutputFormat::Text if output.is_empty() => (),
184184
OutputFormat::Text => {
185-
println!("✅ Job {job_id} stdout:");
185+
println!("✅ Job stdout:");
186186
let output = String::from_utf8(output.to_vec())?;
187187
if output.ends_with('\n') {
188188
print!("{output}");
@@ -196,7 +196,7 @@ impl CommandContext for Cli {
196196

197197
fn job_stderr(
198198
&mut self,
199-
job_id: JobId,
199+
_job_id: &JobId,
200200
errors: &[u8],
201201
binary: bool,
202202
) -> Result<(), CommandError> {
@@ -206,7 +206,7 @@ impl CommandContext for Cli {
206206
OutputFormat::Text if binary => stdout().write(errors).map(|_| ())?,
207207
OutputFormat::Text if errors.is_empty() => (),
208208
OutputFormat::Text => {
209-
println!("❌ Job {job_id} stderr:");
209+
println!("❌ Job stderr:");
210210
let errors = String::from_utf8(errors.to_vec())?;
211211
if errors.ends_with('\n') {
212212
print!("{errors}");
@@ -218,11 +218,11 @@ impl CommandContext for Cli {
218218
Ok(())
219219
}
220220

221-
fn job_status(&mut self, job_id: JobId, status: &JobStatus) -> Result<(), CommandError> {
221+
fn job_status(&mut self, job_id: &JobId, status: &JobStatus) -> Result<(), CommandError> {
222222
match self.get_output_format() {
223223
OutputFormat::Json => println!("{}", json!(status)),
224224
OutputFormat::Text => match status {
225-
JobStatus::NotFound => println!("❌ Job {job_id} not found"),
225+
JobStatus::NotFound => println!("❌ Job `{job_id}` not found"),
226226
JobStatus::Reserved {
227227
job_id,
228228
time_reserved,
@@ -291,10 +291,10 @@ impl CommandContext for Cli {
291291
OutputFormat::Text => {
292292
let n = revoked.len();
293293
match n {
294-
0 => println!("✅ No job IDs revoked"),
295-
1 => println!("✅ Revoked job ID {}", revoked[0]),
294+
0 => println!("✅ No jobs revoked"),
295+
1 => println!("✅ Revoked job `{}`", revoked[0]),
296296
_ => {
297-
println!("✅ Revoked {} job IDs:", revoked.len());
297+
println!("✅ Revoked {} jobs:", revoked.len());
298298
for job_id in revoked {
299299
println!("{job_id}");
300300
}
@@ -309,7 +309,7 @@ impl CommandContext for Cli {
309309
match self.get_output_format() {
310310
OutputFormat::Json => println!("{}", serde_json::to_string(&job)?),
311311
OutputFormat::Text => println!(
312-
"✅ Signed request for job {}\n{}",
312+
"✅ Signed request for job `{}`\n{}",
313313
job.job_id(),
314314
serde_json::to_string_pretty(&job)?
315315
),

‎client/src/commands.rs‎

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,10 @@ pub trait CommandContext {
272272
fn ack(&mut self, url: &str, time: DateTime<Utc>) -> Result<(), CommandError>;
273273
fn cert_chain(&mut self, key_id: KeyId, certs: &str) -> Result<(), CommandError>;
274274
fn cert_imported(&mut self, path: &Path, key_id: KeyId) -> Result<(), CommandError>;
275-
fn job_aborted(&mut self, id: JobId) -> Result<(), CommandError>;
276-
fn job_stdout(&mut self, id: JobId, output: &[u8], binary: bool) -> Result<(), CommandError>;
277-
fn job_stderr(&mut self, id: JobId, errors: &[u8], binary: bool) -> Result<(), CommandError>;
278-
fn job_status(&mut self, id: JobId, status: &JobStatus) -> Result<(), CommandError>;
275+
fn job_aborted(&mut self, id: &JobId) -> Result<(), CommandError>;
276+
fn job_stdout(&mut self, id: &JobId, output: &[u8], binary: bool) -> Result<(), CommandError>;
277+
fn job_stderr(&mut self, id: &JobId, errors: &[u8], binary: bool) -> Result<(), CommandError>;
278+
fn job_status(&mut self, id: &JobId, status: &JobStatus) -> Result<(), CommandError>;
279279
fn job_signed(&mut self, job: &SignedJob) -> Result<(), CommandError>;
280280
fn jobs_reserved(&mut self, reserved: &JobsReserved) -> Result<(), CommandError>;
281281
fn reserved_read(&mut self, reserved: &JobsReserved) -> Result<(), CommandError>;
@@ -375,19 +375,19 @@ impl ClientCommand {
375375
}
376376
(ClientCommand::JobStatus { job_id }, Some(client)) => {
377377
let status = client.job_status(&job_id).await?.into_inner();
378-
ctx.job_status(job_id, &status)
378+
ctx.job_status(&job_id, &status)
379379
}
380380
(ClientCommand::JobStdout { job_id, binary }, Some(client)) => {
381381
let stdout = client.job_stdout(&job_id).await?.into_inner();
382-
ctx.job_stdout(job_id, &stdout, binary)
382+
ctx.job_stdout(&job_id, &stdout, binary)
383383
}
384384
(ClientCommand::JobStderr { job_id, binary }, Some(client)) => {
385385
let stderr = client.job_stderr(&job_id).await?.into_inner();
386-
ctx.job_stderr(job_id, &stderr, binary)
386+
ctx.job_stderr(&job_id, &stderr, binary)
387387
}
388388
(ClientCommand::JobAbort { job_id }, Some(client)) => {
389389
client.job_abort(&job_id).await?;
390-
ctx.job_aborted(job_id)
390+
ctx.job_aborted(&job_id)
391391
}
392392
(ClientCommand::Set { args: values }, _) => {
393393
ctx.set_globals(args, values);
@@ -421,16 +421,16 @@ where
421421
max_fsize,
422422
} = limits;
423423
let status = select! {
424-
status = client.job_start(&job_id, max_cpu, max_mem, max_fsize, wait, &job) => status?.into_inner(),
424+
status = client.job_start(job_id, max_cpu, max_mem, max_fsize, wait, &job) => status?.into_inner(),
425425
_ = ctrl_c() => {
426-
client.job_abort(&job_id).await?;
427-
client.job_status(&job_id).await?.into_inner()
426+
client.job_abort(job_id).await?;
427+
client.job_status(job_id).await?.into_inner()
428428
}
429429
};
430430
ctx.job_status(job_id, &status)?;
431431
if wait {
432-
let stdout = client.job_stdout(&job_id).await?.into_inner();
433-
let stderr = client.job_stderr(&job_id).await?.into_inner();
432+
let stdout = client.job_stdout(job_id).await?.into_inner();
433+
let stderr = client.job_stderr(job_id).await?.into_inner();
434434
ctx.job_stdout(job_id, &stdout, binary)?;
435435
ctx.job_stderr(job_id, &stderr, binary)?;
436436
}
@@ -471,9 +471,10 @@ fn read_reserved() -> Result<Reserved, CommandError> {
471471
Ok(Reserved::Batch(JobsReserved {
472472
job_ids: input
473473
.split('\n')
474+
.map(|s| s.trim())
474475
.filter(|s| !s.is_empty())
475-
.map(|s| s.split_whitespace().next().unwrap_or(s).parse())
476-
.collect::<Result<Vec<JobId>, _>>()?,
476+
.map(JobId::from)
477+
.collect::<Vec<JobId>>(),
477478
time_reserved: Utc::now(),
478479
}))
479480
}
@@ -519,7 +520,7 @@ pub enum CommandError {
519520
MissingKeyName,
520521
#[error("❌ Command not supported in offline mode, try `--url`")]
521522
Offline,
522-
#[error("❌ `permslip` error: {0}")]
523+
#[error("❌ permslip error: {0}")]
523524
Permslip(#[from] PermslipError),
524525
#[error("👋 Goodbye!")]
525526
Quit,
@@ -529,8 +530,6 @@ pub enum CommandError {
529530
Recursive(#[from] Box<Self>),
530531
#[error("❌ UTF-8 error: {0}")]
531532
Utf8(#[from] std::string::FromUtf8Error),
532-
#[error("❌ UUID error: {0}")]
533-
Uuid(#[from] uuid::Error),
534533
}
535534

536535
impl From<ClientError<ApiError>> for CommandError {

0 commit comments

Comments
 (0)