mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
## What changed - Introduce `codex-user-verification` with a provider interface for credential status, creation, deletion, and challenge signing. Include typed errors, shared cancellation guards, and hashed account-user key namespaces. - Add P-256 public-key encoding as unpadded base64url SPKI DER, derive credential IDs from its SHA-256 digest, and redact proof fields in debug output. - Add app-server helpers to validate challenge and display-text bounds and map provider errors to typed RPC errors without exposing provider diagnostics. The platform implementation reports verification as unsupported. App-server requests still return typed unavailability, with the message updated to mention build or account availability. ## Testing Add tests for credential encoding and signature verification, invalid curve points, cancellation across guard clones, stable and distinct account namespaces, and invalid challenge or display values. Update the app-server unavailability test for the revised message. GitOrigin-RevId: fe4a4eb37c68d7fdc547704e76aa257abf3e9c81
87 lines
2.7 KiB
Rust
87 lines
2.7 KiB
Rust
//! Public credential encoding and results; private keys never cross this boundary.
|
|
|
|
use crate::UserVerificationError;
|
|
use crate::UserVerificationFailureReason;
|
|
use crate::UserVerificationUnavailableReason;
|
|
use base64::Engine as _;
|
|
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
|
use p256::pkcs8::EncodePublicKey as _;
|
|
use sha2::Digest as _;
|
|
use sha2::Sha256;
|
|
|
|
/// Validated display text and exact challenge bytes supplied by the trusted calling UI.
|
|
#[derive(Clone)]
|
|
pub struct UserVerificationRequest {
|
|
pub challenge: Vec<u8>,
|
|
pub title: String,
|
|
pub description: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct UserVerificationKeyInfo {
|
|
pub credential_id: String,
|
|
pub algorithm: String,
|
|
/// Unpadded base64url of the P-256 SubjectPublicKeyInfo DER encoding.
|
|
pub public_key: String,
|
|
}
|
|
|
|
impl UserVerificationKeyInfo {
|
|
pub fn from_sec1_public_key(bytes: &[u8]) -> Result<Self, UserVerificationError> {
|
|
let public_key = p256::PublicKey::from_sec1_bytes(bytes)
|
|
.map_err(|_| invalid_public_key())?
|
|
.to_public_key_der()
|
|
.map_err(|_| invalid_public_key())?;
|
|
let bytes = public_key.as_bytes();
|
|
Ok(Self {
|
|
credential_id: URL_SAFE_NO_PAD.encode(Sha256::digest(bytes)),
|
|
algorithm: "ecdsaP256Sha256X962".to_string(),
|
|
public_key: URL_SAFE_NO_PAD.encode(bytes),
|
|
})
|
|
}
|
|
}
|
|
|
|
fn invalid_public_key() -> UserVerificationError {
|
|
UserVerificationError::Failed {
|
|
reason: UserVerificationFailureReason::ProviderError,
|
|
message: "could not encode the user-verification public key".to_string(),
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct UserVerificationKeyCreation {
|
|
pub created: bool,
|
|
pub credential: UserVerificationKeyInfo,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct UserVerificationKeyDeletion {
|
|
pub deleted_credential_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct UserVerificationStatus {
|
|
pub credential: Option<UserVerificationKeyInfo>,
|
|
pub unavailable_reason: Option<UserVerificationUnavailableReason>,
|
|
pub unavailable_message: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub struct UserVerificationProof {
|
|
pub credential_id: String,
|
|
/// Unpadded base64url of an ASN.1 DER ECDSA signature over the challenge, hashed once.
|
|
pub signature: String,
|
|
}
|
|
|
|
impl std::fmt::Debug for UserVerificationProof {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("UserVerificationProof")
|
|
.field("credential_id", &"[REDACTED]")
|
|
.field("signature", &"[REDACTED]")
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "credential_tests.rs"]
|
|
mod tests;
|