Skip to content

Commit 3f3d1ec

Browse files
committed
[mamcx/openssl_no_more]: Merge branch 'mamcx/openssl_no_more' of github.com:clockworklabs/SpacetimeDB into mamcx/openssl_no_more
2 parents a967537 + 84bd992 commit 3f3d1ec

1 file changed

Lines changed: 117 additions & 2 deletions

File tree

crates/core/src/auth/mod.rs

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ impl EcKeyPair {
9292
// Generate a new key pair for the P-256 curve (equivalent to `Nid::X9_62_PRIME256V1`).
9393
let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256)?;
9494

95-
// Get the private key in PKCS#8 PEM format & write it.
95+
// Get the public key in PEM format.
9696
let public_key_bytes = key_pair.public_key_pem().into_bytes();
9797

98-
// Get the public key in PEM format & write it.
98+
// Get the private key in PKCS#8 PEM format.
9999
let private_key_bytes = key_pair.serialize_pem().into_bytes();
100100

101101
Ok(Self {
@@ -110,3 +110,118 @@ impl EcKeyPair {
110110
Ok(())
111111
}
112112
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
use super::*;
117+
use jsonwebtoken::{Algorithm, Header, Validation};
118+
use serde::{Deserialize, Serialize};
119+
120+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
121+
struct TestClaims {
122+
sub: String,
123+
exp: u64,
124+
}
125+
126+
#[test]
127+
fn generate_produces_valid_pem_headers() {
128+
let pair = EcKeyPair::generate().expect("key generation should succeed");
129+
130+
let pub_str = std::str::from_utf8(&pair.public_key_bytes).expect("public key should be valid UTF-8");
131+
let priv_str = std::str::from_utf8(&pair.private_key_bytes).expect("private key should be valid UTF-8");
132+
133+
assert!(
134+
pub_str.contains("-----BEGIN PUBLIC KEY-----"),
135+
"public key should be SPKI PEM format, got: {pub_str}"
136+
);
137+
assert!(
138+
priv_str.contains("-----BEGIN PRIVATE KEY-----"),
139+
"private key should be PKCS#8 PEM format, got: {priv_str}"
140+
);
141+
}
142+
143+
#[test]
144+
fn generate_roundtrip_sign_verify() {
145+
let pair = EcKeyPair::generate().expect("key generation should succeed");
146+
let jwt_keys = JwtKeys::try_from(pair).expect("JwtKeys conversion should succeed");
147+
148+
let claims = TestClaims {
149+
sub: "test-user".to_string(),
150+
exp: u64::MAX,
151+
};
152+
153+
let token = jsonwebtoken::encode(&Header::new(Algorithm::ES256), &claims, &jwt_keys.private)
154+
.expect("JWT signing should succeed");
155+
156+
let mut validation = Validation::new(Algorithm::ES256);
157+
validation.required_spec_claims.clear();
158+
let decoded = jsonwebtoken::decode::<TestClaims>(&token, &jwt_keys.public, &validation)
159+
.expect("JWT verification should succeed");
160+
161+
assert_eq!(decoded.claims, claims);
162+
}
163+
164+
#[test]
165+
fn generate_produces_unique_keys() {
166+
let pair1 = EcKeyPair::generate().expect("first key generation should succeed");
167+
let pair2 = EcKeyPair::generate().expect("second key generation should succeed");
168+
169+
assert_ne!(
170+
pair1.private_key_bytes, pair2.private_key_bytes,
171+
"two generated key pairs should have different private keys"
172+
);
173+
}
174+
175+
#[test]
176+
fn generated_keys_cross_verify_fails() {
177+
let pair1 = EcKeyPair::generate().expect("first key generation should succeed");
178+
let pair2 = EcKeyPair::generate().expect("second key generation should succeed");
179+
let keys1 = JwtKeys::try_from(pair1).unwrap();
180+
let keys2 = JwtKeys::try_from(pair2).unwrap();
181+
182+
let claims = TestClaims {
183+
sub: "test".to_string(),
184+
exp: u64::MAX,
185+
};
186+
187+
// Sign with key1's private key.
188+
let token = jsonwebtoken::encode(&Header::new(Algorithm::ES256), &claims, &keys1.private).unwrap();
189+
190+
// Verify with key2's public key should fail.
191+
let mut validation = Validation::new(Algorithm::ES256);
192+
validation.required_spec_claims.clear();
193+
let result = jsonwebtoken::decode::<TestClaims>(&token, &keys2.public, &validation);
194+
195+
assert!(result.is_err(), "verification with wrong public key should fail");
196+
}
197+
198+
#[test]
199+
fn write_read_roundtrip() {
200+
let dir = tempfile::tempdir().expect("failed to create temp dir");
201+
let pub_path = dir.path().join("pub.pem");
202+
let priv_path = dir.path().join("priv.pem");
203+
204+
let pair = EcKeyPair::generate().expect("key generation should succeed");
205+
std::fs::write(&pub_path, &pair.public_key_bytes).unwrap();
206+
std::fs::write(&priv_path, &pair.private_key_bytes).unwrap();
207+
208+
// Read back and create JwtKeys.
209+
let pub_bytes = std::fs::read(&pub_path).unwrap();
210+
let priv_bytes = std::fs::read(&priv_path).unwrap();
211+
let reloaded = EcKeyPair::new(pub_bytes, priv_bytes);
212+
let jwt_keys = JwtKeys::try_from(reloaded).expect("reloaded keys should produce valid JwtKeys");
213+
214+
// Verify signing still works after read-back.
215+
let claims = TestClaims {
216+
sub: "roundtrip".to_string(),
217+
exp: u64::MAX,
218+
};
219+
let token =
220+
jsonwebtoken::encode(&Header::new(Algorithm::ES256), &claims, &jwt_keys.private).expect("signing failed");
221+
222+
let mut validation = Validation::new(Algorithm::ES256);
223+
validation.required_spec_claims.clear();
224+
jsonwebtoken::decode::<TestClaims>(&token, &jwt_keys.public, &validation)
225+
.expect("verification after read-back failed");
226+
}
227+
}

0 commit comments

Comments
 (0)