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}