aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/access.md2
-rw-r--r--src/access/sshsig.rs19
2 files changed, 18 insertions, 3 deletions
diff --git a/docs/access.md b/docs/access.md
index 402f1ab..94ca195 100644
--- a/docs/access.md
+++ b/docs/access.md
@@ -33,7 +33,7 @@ password hashes: every member reads the document).
doesn't read is left out, and anything but `false` disables.
* **`keys`**: the user's SSH keys, each by a name of its choosing, as an
`authorized_keys` line has it (`"laptop": "ssh-ed25519 AAAA… alice@laptop"`; Ed25519
- only; the comment is kept and checks nothing): what it signs values with (Values signed by their users, below) and logs in with. A user
+ only; the comment, 256 bytes at most, is kept and checks nothing): what it signs values with (Values signed by their users, below) and logs in with. A user
manages its own, whatever else it may do: `GET /auth/keys`, `PUT /auth/keys/<name>`
with the line, `DELETE /auth/keys/<name>`; under a strict policy, by signing its
entry with only its keys changed (`continuum-signed key-add laptop
diff --git a/src/access/sshsig.rs b/src/access/sshsig.rs
index 799cb87..e25d8a7 100644
--- a/src/access/sshsig.rs
+++ b/src/access/sshsig.rs
@@ -22,6 +22,10 @@ const END: &str = "-----END SSH SIGNATURE-----";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct PublicKey(pub(crate) [u8; 32]);
+/// A key's comment kept at most, in bytes (`PublicKey::kept_line`): what says whose and
+/// where the key is, not a place for text.
+pub(crate) const MAX_COMMENT: usize = 256;
+
impl PublicKey {
/// From an `authorized_keys` line: `ssh-ed25519 <base64> [comment]`.
pub(crate) fn parse(line: &str) -> Result<Self, String> {
@@ -70,11 +74,20 @@ impl PublicKey {
/// made one). The comment is for whoever reads it: the key alone checks a signature.
pub(crate) fn kept_line(line: &str) -> Result<String, String> {
let key = Self::parse(line)?;
- let comment: Vec<&str> = line.split_whitespace().skip(2).collect();
+ let comment = line
+ .split_whitespace()
+ .skip(2)
+ .collect::<Vec<&str>>()
+ .join(" ");
+ if comment.len() > MAX_COMMENT {
+ return Err(format!(
+ "the key's comment is longer than {MAX_COMMENT} bytes"
+ ));
+ }
Ok(if comment.is_empty() {
key.line()
} else {
- format!("{} {}", key.line(), comment.join(" "))
+ format!("{} {comment}", key.line())
})
}
@@ -263,6 +276,8 @@ DNg4vBWp1j1gLRiBMOF+gwYNegDg==
assert_eq!(commented, format!("{} alice@laptop 2026", key.line()));
assert_eq!(PublicKey::parse(&commented), Ok(key));
assert_eq!(PublicKey::kept_line(&key.line()), Ok(key.line()));
+ let long = format!("{} {}", key.line(), "x".repeat(MAX_COMMENT + 1));
+ assert!(PublicKey::kept_line(&long).is_err());
}
/// What `sign` makes checks, and reads as `ssh-keygen` writes it.