certify_challenge: what a fresh key signs to ask for its certificate 098227d48b1a
Bound to the credential it is asked with, so the proof cannot be replayed with another. Refs andymccall/oxygenhub#241
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
Claude Code <[email protected]> · delegate for changes, until 2026-11-13 19:00Z · key ea5f78160088
- When
- · 2026-10-17 14:11 UTC
- Signature
- ✔ signed by Claude Code
- Hash
098227d48b1a9ec8224e82bc8ddab09b246c0ee1cbe5cfe364a3b0b35d36fcd4- Depends on
- 02e691881a4e
- Depended on by
- a2387d547373
- In bookmarks
- carried-not-removed doctor-trust identical-sides keyless-tags main merge-remote-bookmark paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state
- In tags
- v0.10.0 v0.11.0 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
- Edits
- 2 atoms, 0 tree operations, 1 file
Browse the tree at this change
modified crates/oxy-core/src/cert.rs +26 −0
| … | 134 unchanged lines |
||
| 135 | 135 | } |
|
| 136 | 136 | } |
|
| 137 | 137 | ||
| 138 | + | /// What a fresh key signs (a challenge, §14.3) to show its holder asks for |
|
| 139 | + | /// the certificate: bound to the credential it is asked with, so the |
|
| 140 | + | /// proof cannot be replayed with another. |
|
| 141 | + | pub fn certify_challenge(credential: &str) -> Vec<u8> { |
|
| 142 | + | let mut c = b"oxy1 certify ".to_vec(); |
|
| 143 | + | c.extend_from_slice(blake3::hash(credential.as_bytes()).as_bytes()); |
|
| 144 | + | c |
|
| 145 | + | } |
|
| 146 | + | ||
| 138 | 147 | #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] |
|
| 139 | 148 | pub enum CertError { |
|
| 140 | 149 | #[error("the certificate names issuer {0}, not this one")] |
|
| … | 144 unchanged lines |
||
| 285 | 294 | assert!(!c.covers(Scope::NONE, 2_000)); |
|
| 286 | 295 | let (_, long) = issue(&hub_sk, &hub, 61); |
|
| 287 | 296 | assert_eq!(long.verify(&hub), Err(CertError::BadLifetime)); |
|
| 297 | + | } |
|
| 298 | + | ||
| 299 | + | #[test] |
|
| 300 | + | fn the_proof_of_possession_is_bound_to_its_credential() { |
|
| 301 | + | let sk = SecretKey::generate(); |
|
| 302 | + | let id = sk.identity("job", "[email protected]", 1); |
|
| 303 | + | let proof = sk |
|
| 304 | + | .sign_challenge(&id.hash(), &certify_challenge("flowjob_aaa")) |
|
| 305 | + | .unwrap(); |
|
| 306 | + | assert!( |
|
| 307 | + | crate::identity::verify_challenge(&id, &proof, &certify_challenge("flowjob_aaa")) |
|
| 308 | + | .is_ok() |
|
| 309 | + | ); |
|
| 310 | + | assert!( |
|
| 311 | + | crate::identity::verify_challenge(&id, &proof, &certify_challenge("flowjob_bbb")) |
|
| 312 | + | .is_err() |
|
| 313 | + | ); |
|
| 288 | 314 | } |
|
| 289 | 315 | ||
| 290 | 316 | #[test] |
|
| … | 10 unchanged lines |