revert(auth): remove RSA support from authentication and related components
Some checks failed
ci/woodpecker/pr/server-audit Pipeline was successful
ci/woodpecker/pr/server-vet Pipeline failed
ci/woodpecker/pr/server-lint Pipeline failed
ci/woodpecker/pr/server-test Pipeline was successful

This commit is contained in:
2026-03-14 13:23:06 +01:00
parent d29bca853b
commit 42760bbd79
11 changed files with 9 additions and 153 deletions

View File

@@ -9,7 +9,6 @@ enum KeyType {
KEY_TYPE_UNSPECIFIED = 0; KEY_TYPE_UNSPECIFIED = 0;
KEY_TYPE_ED25519 = 1; KEY_TYPE_ED25519 = 1;
KEY_TYPE_ECDSA_SECP256K1 = 2; KEY_TYPE_ECDSA_SECP256K1 = 2;
KEY_TYPE_RSA = 3;
} }
message AuthChallengeRequest { message AuthChallengeRequest {

84
server/Cargo.lock generated
View File

@@ -731,12 +731,9 @@ dependencies = [
"rand 0.10.0", "rand 0.10.0",
"rcgen", "rcgen",
"restructed", "restructed",
"rsa",
"rustls", "rustls",
"secrecy", "secrecy",
"sha2 0.10.9",
"smlang", "smlang",
"spki",
"strum", "strum",
"test-log", "test-log",
"thiserror", "thiserror",
@@ -767,11 +764,8 @@ dependencies = [
"k256", "k256",
"kameo", "kameo",
"rand 0.10.0", "rand 0.10.0",
"rsa",
"rustls-webpki", "rustls-webpki",
"sha2 0.10.9",
"smlang", "smlang",
"spki",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
@@ -1739,7 +1733,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [ dependencies = [
"const-oid", "const-oid",
"pem-rfc7468",
"zeroize", "zeroize",
] ]
@@ -2920,9 +2913,6 @@ name = "lazy_static"
version = "1.5.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin",
]
[[package]] [[package]]
name = "leb128fmt" name = "leb128fmt"
@@ -3156,22 +3146,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-bigint-dig"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
dependencies = [
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.5",
"smallvec",
"zeroize",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.2.0" version = "0.2.0"
@@ -3187,17 +3161,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -3367,15 +3330,6 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.2" version = "2.3.2"
@@ -3435,17 +3389,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]] [[package]]
name = "pkcs8" name = "pkcs8"
version = "0.10.2" version = "0.10.2"
@@ -4093,27 +4036,6 @@ dependencies = [
"rustc-hex", "rustc-hex",
] ]
[[package]]
name = "rsa"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
"const-oid",
"digest 0.10.7",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core 0.6.4",
"sha2 0.10.9",
"signature 2.2.0",
"spki",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "rsqlite-vfs" name = "rsqlite-vfs"
version = "0.1.0" version = "0.1.0"
@@ -4661,12 +4583,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "spki" name = "spki"
version = "0.7.3" version = "0.7.3"

View File

@@ -37,6 +37,3 @@ rcgen = { version = "0.14.7", features = [
"zeroize", "zeroize",
], default-features = false } ], default-features = false }
k256 = { version = "0.13.4", features = ["ecdsa", "pkcs8"] } k256 = { version = "0.13.4", features = ["ecdsa", "pkcs8"] }
rsa = { version = "0.9", features = ["sha2"] }
sha2 = "0.10"
spki = "0.7"

View File

@@ -203,7 +203,6 @@ pub mod grpc {
/// [`Bi`] adapter backed by a tonic gRPC bidirectional stream. /// [`Bi`] adapter backed by a tonic gRPC bidirectional stream.
/// ///
/// Tonic receive errors are logged and treated as stream closure (`None`). /// Tonic receive errors are logged and treated as stream closure (`None`).
/// The receive converter is only invoked for successful inbound transport /// The receive converter is only invoked for successful inbound transport
/// items. /// items.

View File

@@ -20,7 +20,7 @@ impl Display for ArbiterUrl {
"{ARBITER_URL_SCHEME}://{}:{}?{CERT_QUERY_KEY}={}", "{ARBITER_URL_SCHEME}://{}:{}?{CERT_QUERY_KEY}={}",
self.host, self.host,
self.port, self.port,
BASE64_URL_SAFE.encode(self.ca_cert.to_vec()) BASE64_URL_SAFE.encode(&self.ca_cert)
); );
if let Some(token) = &self.bootstrap_token { if let Some(token) = &self.bootstrap_token {
base.push_str(&format!("&{BOOTSTRAP_TOKEN_QUERY_KEY}={}", token)); base.push_str(&format!("&{BOOTSTRAP_TOKEN_QUERY_KEY}={}", token));

View File

@@ -43,9 +43,6 @@ restructed = "0.2.2"
strum = { version = "0.27.2", features = ["derive"] } strum = { version = "0.27.2", features = ["derive"] }
pem = "3.0.6" pem = "3.0.6"
k256.workspace = true k256.workspace = true
rsa.workspace = true
sha2.workspace = true
spki.workspace = true
alloy.workspace = true alloy.workspace = true
arbiter-tokens-registry.path = "../arbiter-tokens-registry" arbiter-tokens-registry.path = "../arbiter-tokens-registry"

View File

@@ -6,7 +6,8 @@ use tracing::error;
use crate::actors::user_agent::{ use crate::actors::user_agent::{
UserAgentConnection, UserAgentConnection,
auth::state::{AuthContext, AuthPublicKey, AuthStateMachine}, session::UserAgentSession, auth::state::{AuthContext, AuthPublicKey, AuthStateMachine},
session::UserAgentSession,
}; };
#[derive(thiserror::Error, Debug, PartialEq)] #[derive(thiserror::Error, Debug, PartialEq)]
@@ -51,12 +52,6 @@ fn parse_pubkey(key_type: ProtoKeyType, pubkey: Vec<u8>) -> Result<AuthPublicKey
.map_err(|_| Error::InvalidAuthPubkeyEncoding)?; .map_err(|_| Error::InvalidAuthPubkeyEncoding)?;
Ok(AuthPublicKey::EcdsaSecp256k1(key)) Ok(AuthPublicKey::EcdsaSecp256k1(key))
} }
ProtoKeyType::Rsa => {
use rsa::pkcs8::DecodePublicKey as _;
let key = rsa::RsaPublicKey::from_public_key_der(&pubkey)
.map_err(|_| Error::InvalidAuthPubkeyEncoding)?;
Ok(AuthPublicKey::Rsa(key))
}
} }
} }
@@ -131,7 +126,9 @@ pub async fn authenticate(props: &mut UserAgentConnection) -> Result<AuthPublicK
} }
} }
pub async fn authenticate_and_create(mut props: UserAgentConnection) -> Result<UserAgentSession, Error> { pub async fn authenticate_and_create(
mut props: UserAgentConnection,
) -> Result<UserAgentSession, Error> {
let _key = authenticate(&mut props).await?; let _key = authenticate(&mut props).await?;
let session = UserAgentSession::new(props); let session = UserAgentSession::new(props);
Ok(session) Ok(session)

View File

@@ -11,30 +11,22 @@ use crate::{
db::{models::KeyType, schema}, db::{models::KeyType, schema},
}; };
/// Abstraction over Ed25519 / ECDSA-secp256k1 / RSA public keys used during the auth handshake. /// Abstraction over Ed25519 / ECDSA-secp256k1 public keys used during the auth handshake.
#[derive(Clone)] #[derive(Clone)]
pub enum AuthPublicKey { pub enum AuthPublicKey {
Ed25519(ed25519_dalek::VerifyingKey), Ed25519(ed25519_dalek::VerifyingKey),
/// Compressed SEC1 public key; signature bytes are raw 64-byte (r||s). /// Compressed SEC1 public key; signature bytes are raw 64-byte (r||s).
EcdsaSecp256k1(k256::ecdsa::VerifyingKey), EcdsaSecp256k1(k256::ecdsa::VerifyingKey),
/// RSA-2048+ public key; signature bytes are PSS+SHA-256.
Rsa(rsa::RsaPublicKey),
} }
impl AuthPublicKey { impl AuthPublicKey {
/// Canonical bytes stored in DB and echoed back in the challenge. /// Canonical bytes stored in DB and echoed back in the challenge.
/// Ed25519: raw 32 bytes. ECDSA: SEC1 compressed 33 bytes. RSA: DER-encoded SPKI. /// Ed25519: raw 32 bytes. ECDSA: SEC1 compressed 33 bytes.
pub fn to_stored_bytes(&self) -> Vec<u8> { pub fn to_stored_bytes(&self) -> Vec<u8> {
match self { match self {
AuthPublicKey::Ed25519(k) => k.to_bytes().to_vec(), AuthPublicKey::Ed25519(k) => k.to_bytes().to_vec(),
// SEC1 compressed (33 bytes) is the natural compact format for secp256k1 // SEC1 compressed (33 bytes) is the natural compact format for secp256k1
AuthPublicKey::EcdsaSecp256k1(k) => k.to_encoded_point(true).as_bytes().to_vec(), AuthPublicKey::EcdsaSecp256k1(k) => k.to_encoded_point(true).as_bytes().to_vec(),
AuthPublicKey::Rsa(k) => {
use rsa::pkcs8::EncodePublicKey as _;
k.to_public_key_der()
.expect("rsa SPKI encoding is infallible")
.to_vec()
}
} }
} }
@@ -42,7 +34,6 @@ impl AuthPublicKey {
match self { match self {
AuthPublicKey::Ed25519(_) => KeyType::Ed25519, AuthPublicKey::Ed25519(_) => KeyType::Ed25519,
AuthPublicKey::EcdsaSecp256k1(_) => KeyType::EcdsaSecp256k1, AuthPublicKey::EcdsaSecp256k1(_) => KeyType::EcdsaSecp256k1,
AuthPublicKey::Rsa(_) => KeyType::Rsa,
} }
} }
} }
@@ -170,15 +161,6 @@ impl AuthStateMachineContext for AuthContext<'_> {
})?; })?;
vk.verify(&formatted, &sig).is_ok() vk.verify(&formatted, &sig).is_ok()
} }
AuthPublicKey::Rsa(pk) => {
use rsa::signature::Verifier as _;
let verifying_key = rsa::pss::VerifyingKey::<sha2::Sha256>::new(pk.clone());
let sig = rsa::pss::Signature::try_from(solution.as_slice()).map_err(|_| {
error!(?solution, "Invalid RSA signature bytes");
Error::InvalidChallengeSolution
})?;
verifying_key.verify(&formatted, &sig).is_ok()
}
}; };
Ok(valid) Ok(valid)
@@ -284,13 +266,6 @@ impl AuthStateMachineContext for AuthContext<'_> {
.expect("ecdsa key was already validated in parse_auth_event"), .expect("ecdsa key was already validated in parse_auth_event"),
) )
} }
crate::db::models::KeyType::Rsa => {
use rsa::pkcs8::DecodePublicKey as _;
AuthPublicKey::Rsa(
rsa::RsaPublicKey::from_public_key_der(&bytes)
.expect("rsa key was already validated in parse_auth_event"),
)
}
}; };
Ok(rebuilt) Ok(rebuilt)
} }

View File

@@ -81,7 +81,6 @@ pub mod types {
pub enum KeyType { pub enum KeyType {
Ed25519 = 1, Ed25519 = 1,
EcdsaSecp256k1 = 2, EcdsaSecp256k1 = 2,
Rsa = 3,
} }
impl ToSql<Integer, Sqlite> for KeyType { impl ToSql<Integer, Sqlite> for KeyType {
@@ -104,7 +103,6 @@ pub mod types {
match bytes.read_long() { match bytes.read_long() {
1 => Ok(KeyType::Ed25519), 1 => Ok(KeyType::Ed25519),
2 => Ok(KeyType::EcdsaSecp256k1), 2 => Ok(KeyType::EcdsaSecp256k1),
3 => Ok(KeyType::Rsa),
other => Err(format!("Unknown KeyType discriminant: {other}").into()), other => Err(format!("Unknown KeyType discriminant: {other}").into()),
} }
} }

View File

@@ -15,9 +15,6 @@ ed25519-dalek.workspace = true
smlang.workspace = true smlang.workspace = true
x25519-dalek.workspace = true x25519-dalek.workspace = true
k256.workspace = true k256.workspace = true
rsa.workspace = true
sha2.workspace = true
spki.workspace = true
rand.workspace = true rand.workspace = true
thiserror.workspace = true thiserror.workspace = true
tokio-stream.workspace = true tokio-stream.workspace = true

View File

@@ -16,10 +16,8 @@ use tracing::{error, info};
/// Signing key variants supported by the user-agent auth protocol. /// Signing key variants supported by the user-agent auth protocol.
pub enum SigningKeyEnum { pub enum SigningKeyEnum {
Ed25519(ed25519_dalek::SigningKey), Ed25519(ed25519_dalek::SigningKey),
/// secp256k1 ECDSA; public key is sent as DER SPKI; signature is raw 64-byte (r||s). /// secp256k1 ECDSA; public key is sent as SEC1 compressed 33 bytes; signature is raw 64-byte (r||s).
EcdsaSecp256k1(k256::ecdsa::SigningKey), EcdsaSecp256k1(k256::ecdsa::SigningKey),
/// RSA; public key is sent as DER SPKI; signature is PSS+SHA-256.
Rsa(rsa::RsaPrivateKey),
} }
impl SigningKeyEnum { impl SigningKeyEnum {
@@ -31,13 +29,6 @@ impl SigningKeyEnum {
SigningKeyEnum::EcdsaSecp256k1(k) => { SigningKeyEnum::EcdsaSecp256k1(k) => {
k.verifying_key().to_encoded_point(true).as_bytes().to_vec() k.verifying_key().to_encoded_point(true).as_bytes().to_vec()
} }
SigningKeyEnum::Rsa(k) => {
use rsa::pkcs8::EncodePublicKey as _;
k.to_public_key()
.to_public_key_der()
.expect("rsa SPKI encoding is infallible")
.to_vec()
}
} }
} }
@@ -46,7 +37,6 @@ impl SigningKeyEnum {
match self { match self {
SigningKeyEnum::Ed25519(_) => ProtoKeyType::Ed25519, SigningKeyEnum::Ed25519(_) => ProtoKeyType::Ed25519,
SigningKeyEnum::EcdsaSecp256k1(_) => ProtoKeyType::EcdsaSecp256k1, SigningKeyEnum::EcdsaSecp256k1(_) => ProtoKeyType::EcdsaSecp256k1,
SigningKeyEnum::Rsa(_) => ProtoKeyType::Rsa,
} }
} }
@@ -62,15 +52,6 @@ impl SigningKeyEnum {
let sig: k256::ecdsa::Signature = k.sign(msg); let sig: k256::ecdsa::Signature = k.sign(msg);
sig.to_bytes().to_vec() sig.to_bytes().to_vec()
} }
SigningKeyEnum::Rsa(k) => {
use rsa::signature::RandomizedSigner as _;
let signing_key = rsa::pss::BlindedSigningKey::<sha2::Sha256>::new(k.clone());
// Use rand_core OsRng from the rsa crate's re-exported rand_core (0.6.x),
// which is the version rsa's signature API expects.
let sig = signing_key.sign_with_rng(&mut rsa::rand_core::OsRng, msg);
use rsa::signature::SignatureEncoding as _;
sig.to_vec()
}
} }
} }
} }