Skip to main content

signedby_sdk/
lib.rs

1//! SignedByMe SDK - Self-signing digital signatures with zero-knowledge proofs
2//!
3//! This SDK enables server-side verification of SignedByMe authentication tokens
4//! and Groth16 zero-knowledge proofs. Verification is fully decentralized — no
5//! network calls are required if you bundle the verification key.
6//!
7//! # Quick Start
8//!
9//! ```rust,ignore
10//! use signedby_sdk::{Verifier, VerificationKey};
11//!
12//! // Load verification key (can be bundled for offline use)
13//! let vk = VerificationKey::from_json(include_str!("verification_key.json"))?;
14//! let verifier = Verifier::new(vk);
15//!
16//! // Verify a proof
17//! let result = verifier.verify_proof(&proof, &public_inputs)?;
18//! println!("Valid: {}, npub: {}", result.valid, result.npub);
19//! ```
20//!
21//! # Architecture
22//!
23//! SignedByMe is built on three pillars:
24//!
25//! 1. **Self-signing identity (DID)** — Users create their own cryptographic
26//!    identity on-device. No certificate authority, no central issuer.
27//!
28//! 2. **Zero-knowledge membership proofs** — Users prove they belong to a group
29//!    without revealing which member they are. Built on Groth16/BN254.
30//!
31//! 3. **Bitcoin-backed economic proof** — Every authentication is optionally
32//!    backed by a Lightning payment, making auth economically unforgeable.
33
34pub mod verifier;
35pub mod verification_key;
36pub mod proof;
37pub mod error;
38
39#[cfg(feature = "oidc")]
40pub mod oidc;
41
42// Re-exports
43pub use verifier::Verifier;
44pub use verification_key::VerificationKey;
45pub use proof::{Groth16Proof, PublicInputs, VerificationResult};
46pub use error::SdkError;
47
48/// Bech32-encoded npub prefix for NOSTR public keys
49pub const NPUB_PREFIX: &str = "npub";
50
51/// Convert a hex public key to bech32 npub format
52pub fn hex_to_npub(hex_pubkey: &str) -> Result<String, SdkError> {
53    use bech32::{Hrp, Bech32m};
54    
55    let bytes = hex::decode(hex_pubkey)
56        .map_err(|e| SdkError::InvalidInput(format!("Invalid hex: {}", e)))?;
57    
58    if bytes.len() != 32 {
59        return Err(SdkError::InvalidInput("Public key must be 32 bytes".into()));
60    }
61    
62    let hrp = Hrp::parse(NPUB_PREFIX)
63        .map_err(|e| SdkError::InvalidInput(format!("Invalid HRP: {}", e)))?;
64    
65    bech32::encode::<Bech32m>(hrp, &bytes)
66        .map_err(|e| SdkError::InvalidInput(format!("Bech32 encode failed: {}", e)))
67}
68
69/// Convert a bech32 npub to hex format
70pub fn npub_to_hex(npub: &str) -> Result<String, SdkError> {
71    use bech32::Hrp;
72    
73    let (hrp, bytes) = bech32::decode(npub)
74        .map_err(|e| SdkError::InvalidInput(format!("Invalid bech32: {}", e)))?;
75    
76    let expected_hrp = Hrp::parse(NPUB_PREFIX).unwrap();
77    if hrp != expected_hrp {
78        return Err(SdkError::InvalidInput(format!(
79            "Expected npub prefix, got: {}", hrp
80        )));
81    }
82    
83    Ok(hex::encode(bytes))
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    
90    #[test]
91    fn test_npub_roundtrip() {
92        let hex_key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
93        let npub = hex_to_npub(hex_key).unwrap();
94        assert!(npub.starts_with("npub"));
95        let recovered = npub_to_hex(&npub).unwrap();
96        assert_eq!(recovered, hex_key);
97    }
98}