claim169_core/
lib.rs

1//! # claim169-core
2//!
3//! A Rust library for encoding and decoding MOSIP Claim 169 QR codes.
4//!
5//! ## Overview
6//!
7//! [MOSIP Claim 169](https://github.com/mosip/id-claim-169/tree/main) defines a standard for encoding identity data
8//! in QR codes, designed for offline verification of digital identity credentials. The format
9//! uses a compact binary encoding optimized for QR code capacity constraints.
10//!
11//! The encoding pipeline:
12//! ```text
13//! Claim169 → CBOR → CWT → COSE_Sign1 → [COSE_Encrypt0] → zlib → Base45 → QR Code
14//! ```
15//!
16//! Key technologies:
17//! - **CBOR**: Compact binary encoding with numeric keys for minimal size
18//! - **CWT**: CBOR Web Token for standard claims (issuer, expiration, etc.)
19//! - **COSE_Sign1**: Digital signature for authenticity (Ed25519 or ECDSA P-256)
20//! - **COSE_Encrypt0**: Optional encryption layer (AES-GCM)
21//! - **zlib + Base45**: Compression and alphanumeric encoding for QR efficiency
22//!
23//! ## Quick Start
24//!
25//! ### Encoding (Creating QR Codes)
26//!
27//! ```rust,ignore
28//! use claim169_core::{Encoder, Claim169, CwtMeta};
29//!
30//! let claim169 = Claim169 {
31//!     id: Some("123456789".to_string()),
32//!     full_name: Some("John Doe".to_string()),
33//!     ..Default::default()
34//! };
35//!
36//! let cwt_meta = CwtMeta::new()
37//!     .with_issuer("https://issuer.example.com")
38//!     .with_expires_at(1800000000);
39//!
40//! // Ed25519 signed (recommended)
41//! let qr_data = Encoder::new(claim169, cwt_meta)
42//!     .sign_with_ed25519(&private_key)?
43//!     .encode()?;
44//!
45//! // Signed and encrypted
46//! let qr_data = Encoder::new(claim169, cwt_meta)
47//!     .sign_with_ed25519(&private_key)?
48//!     .encrypt_with_aes256(&aes_key)?
49//!     .encode()?;
50//!
51//! // Unsigned (testing only - requires explicit opt-in)
52//! let qr_data = Encoder::new(claim169, cwt_meta)
53//!     .allow_unsigned()
54//!     .encode()?;
55//! ```
56//!
57//! ### Decoding (Reading QR Codes)
58//!
59//! ```rust,ignore
60//! use claim169_core::Decoder;
61//!
62//! // With Ed25519 verification (recommended)
63//! let result = Decoder::new(qr_content)
64//!     .verify_with_ed25519(&public_key)?
65//!     .decode()?;
66//!
67//! println!("ID: {:?}", result.claim169.id);
68//! println!("Name: {:?}", result.claim169.full_name);
69//! println!("Issuer: {:?}", result.cwt_meta.issuer);
70//!
71//! // Decrypting encrypted credentials
72//! let result = Decoder::new(qr_content)
73//!     .decrypt_with_aes256(&aes_key)?
74//!     .verify_with_ed25519(&public_key)?
75//!     .decode()?;
76//!
77//! // Without verification (testing only - requires explicit opt-in)
78//! let result = Decoder::new(qr_content)
79//!     .allow_unverified()
80//!     .decode()?;
81//! ```
82//!
83//! ### Using Custom Cryptography (HSM Integration)
84//!
85//! For hardware security modules or custom cryptographic backends:
86//!
87//! ```rust,ignore
88//! use claim169_core::{Encoder, Decoder, Signer, SignatureVerifier};
89//!
90//! // Implement the Signer trait for your HSM
91//! struct HsmSigner { /* ... */ }
92//! impl Signer for HsmSigner { /* ... */ }
93//!
94//! // Encoding with HSM
95//! let qr_data = Encoder::new(claim169, cwt_meta)
96//!     .sign_with(hsm_signer, iana::Algorithm::EdDSA)
97//!     .encode()?;
98//!
99//! // Decoding with HSM
100//! let result = Decoder::new(qr_content)
101//!     .verify_with(hsm_verifier)
102//!     .decode()?;
103//! ```
104//!
105//! ## Security Considerations
106//!
107//! - **Always verify signatures** in production - use `.verify_with_*()` methods
108//! - **Always sign credentials** in production - use `.sign_with_*()` methods
109//! - Unsigned/unverified requires explicit opt-in with `.allow_unsigned()`/`.allow_unverified()`
110//! - Decompression is limited to prevent zip bomb attacks (default: 64KB)
111//! - Timestamps are validated by default; use `.without_timestamp_validation()` to disable
112//! - Weak cryptographic keys (all-zeros, small-order points) are automatically rejected
113//!
114//! ## Features
115//!
116//! | Feature | Default | Description |
117//! |---------|---------|-------------|
118//! | `software-crypto` | ✓ | Software implementations of Ed25519, ECDSA P-256, and AES-GCM |
119//!
120//! Disable default features to integrate with HSMs or custom cryptographic backends:
121//!
122//! ```toml
123//! [dependencies]
124//! claim169-core = { version = "0.1", default-features = false }
125//! ```
126//!
127//! Then implement the [`Signer`], [`SignatureVerifier`], [`Encryptor`], or [`Decryptor`] traits.
128//!
129//! ## Modules
130//!
131//! - [`crypto`]: Cryptographic traits and implementations
132//! - [`error`]: Error types for encoding, decoding, and crypto operations
133//! - [`model`]: Data structures for Claim 169 identity data
134//! - [`pipeline`]: Low-level encoding/decoding pipeline functions
135
136pub mod crypto;
137pub mod decode;
138pub mod encode;
139pub mod error;
140pub mod model;
141pub mod pipeline;
142
143// Re-export builder pattern API (primary interface)
144pub use decode::Decoder;
145pub use encode::Encoder;
146
147// Re-export nonce generation when software-crypto is enabled
148#[cfg(feature = "software-crypto")]
149pub use encode::generate_random_nonce;
150
151// Re-export cryptographic traits (for HSM integration)
152pub use crypto::traits::{Decryptor, Encryptor, KeyResolver, SignatureVerifier, Signer};
153
154// Re-export error types
155pub use error::{Claim169Error, CryptoError, CryptoResult, Result};
156
157// Re-export model types
158pub use model::{
159    Biometric, BiometricFormat, BiometricSubFormat, Claim169, CwtMeta, Gender, MaritalStatus,
160    PhotoFormat, VerificationStatus,
161};
162
163// Re-export software crypto implementations when feature is enabled
164#[cfg(feature = "software-crypto")]
165pub use crypto::{
166    AesGcmDecryptor, AesGcmEncryptor, EcdsaP256Signer, EcdsaP256Verifier, Ed25519Signer,
167    Ed25519Verifier,
168};
169
170/// Result of successfully decoding a Claim 169 QR code.
171///
172/// This struct contains all the data extracted from the QR code:
173/// - The identity data ([`Claim169`])
174/// - CWT metadata like issuer and expiration ([`CwtMeta`])
175/// - The signature verification status
176/// - Any warnings generated during decoding
177///
178/// # Example
179///
180/// ```rust,ignore
181/// let result = Decoder::new(qr_content)
182///     .verify_with_ed25519(&public_key)?
183///     .decode()?;
184///
185/// // Access identity data
186/// if let Some(name) = &result.claim169.full_name {
187///     println!("Welcome, {}!", name);
188/// }
189///
190/// // Check verification status
191/// match result.verification_status {
192///     VerificationStatus::Verified => println!("Signature verified"),
193///     VerificationStatus::Skipped => println!("Verification skipped"),
194///     VerificationStatus::Failed => println!("Verification failed"),
195/// }
196///
197/// // Check for warnings
198/// for warning in &result.warnings {
199///     println!("Warning: {}", warning.message);
200/// }
201/// ```
202#[derive(Debug)]
203pub struct DecodeResult {
204    /// The extracted Claim 169 identity data.
205    ///
206    /// Contains demographic information (name, date of birth, address, etc.)
207    /// and optionally biometric data (fingerprints, iris scans, face images).
208    pub claim169: Claim169,
209
210    /// CWT (CBOR Web Token) metadata.
211    ///
212    /// Contains standard claims like issuer, subject, expiration time,
213    /// and issued-at timestamp.
214    pub cwt_meta: CwtMeta,
215
216    /// Signature verification status.
217    ///
218    /// - `Verified`: Signature was checked and is valid
219    /// - `Skipped`: No verifier was provided (only if `allow_unverified` was set)
220    /// - `Failed`: Signature verification failed (this typically returns an error instead)
221    pub verification_status: VerificationStatus,
222
223    /// Warnings generated during decoding.
224    ///
225    /// Non-fatal issues that don't prevent decoding but may warrant attention,
226    /// such as unknown fields (forward compatibility) or skipped validations.
227    pub warnings: Vec<Warning>,
228}
229
230/// A warning generated during the decoding process.
231///
232/// Warnings represent non-fatal issues that don't prevent successful decoding
233/// but may be relevant for logging or auditing purposes.
234#[derive(Debug, Clone)]
235pub struct Warning {
236    /// The type of warning.
237    pub code: WarningCode,
238    /// Human-readable description of the warning.
239    pub message: String,
240}
241
242/// Types of warnings that can be generated during decoding.
243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
244pub enum WarningCode {
245    /// The credential will expire soon (within a configurable threshold).
246    ExpiringSoon,
247    /// Unknown fields were found in the Claim 169 data.
248    ///
249    /// This supports forward compatibility - new fields added to the spec
250    /// won't break older decoders. The unknown fields are preserved in
251    /// `Claim169::unknown_fields`.
252    UnknownFields,
253    /// Timestamp validation was explicitly disabled via options.
254    TimestampValidationSkipped,
255    /// Biometric data parsing was skipped via options.
256    BiometricsSkipped,
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_warning_code_equality() {
265        assert_eq!(WarningCode::ExpiringSoon, WarningCode::ExpiringSoon);
266        assert_ne!(WarningCode::ExpiringSoon, WarningCode::UnknownFields);
267    }
268
269    #[test]
270    fn test_warning_clone() {
271        let warning = Warning {
272            code: WarningCode::BiometricsSkipped,
273            message: "Test warning".to_string(),
274        };
275        let cloned = warning.clone();
276        assert_eq!(cloned.code, warning.code);
277        assert_eq!(cloned.message, warning.message);
278    }
279}