Files
buzz/crates/buzz-core/src/pairing/mod.rs
Will Pfleger d99ad131f1 refactor: rename sprout backend to buzz (#958)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <wpfleger@block.xyz>
Signed-off-by: Will Pfleger <wpfleger@squareup.com>
Signed-off-by: Will Pfleger <wpfleger96@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1fgdl5qqnh3k3f2xkqrvt7cujalhm623x4s7fdjdj5yrtp5fzjl9qrjpucw <4a1bfa0013bc6d14a8d600d8bf6392efefbd2a26ac3c96c9b2a106b0d12297ca@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub16v54tttfqacx9ycvc3k0ut0npj564ahcuajzy6qjvh57ntmsf4uq4806j2 <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Will Pfleger <wpfleger96@gmail.com>
2026-06-10 19:29:51 -04:00

81 lines
2.6 KiB
Rust

//! NIP-AB device pairing — crypto primitives, message types, and error types.
//!
//! NIP-AB enables two Nostr devices to securely exchange a secret (e.g., an
//! `nsec` or a NIP-46 bunker connection string) over an untrusted relay, using:
//!
//! 1. **HKDF-SHA256** for all key derivation (session ID, SAS code, transcript hash).
//! 2. **ECDH** (via [`nostr::util::generate_shared_key`]) for the shared secret.
//! 3. **NIP-44 v2** for encrypting the message payloads.
//! 4. **Short Authentication String (SAS)** for out-of-band confirmation.
//!
//! # Module layout
//!
//! | Module | Contents |
//! |--------|----------|
//! | [`crypto`] | Pure HKDF derivation functions |
//! | [`types`] | Serde-serializable pairing message types |
//!
//! # Error handling
//!
//! All fallible operations in the pairing flow return [`PairingError`].
pub mod crypto;
pub mod qr;
pub mod session;
pub mod types;
pub use qr::QrPayload;
pub use session::{PairingSession, Role, SessionState};
pub use types::{AbortReason, PairingMessage, PayloadType};
use thiserror::Error;
/// Errors that can occur during a NIP-AB pairing session.
#[derive(Debug, Error)]
pub enum PairingError {
/// The scanned QR URI was not a valid NIP-AB pairing URI.
#[error("invalid QR URI: {0}")]
InvalidQr(String),
/// The session ID extracted from a message was not a valid 32-byte hex string.
#[error("invalid session ID")]
InvalidSessionId,
/// The SAS code shown on both devices did not match — session must be aborted.
#[error("SAS mismatch")]
SasMismatch,
/// The transcript hash received from the peer did not match the locally computed value.
#[error("transcript hash mismatch")]
TranscriptMismatch,
/// A message arrived out of sequence or with the wrong type for the current state.
#[error("unexpected message type: expected {expected}, got {got}")]
UnexpectedMessage {
/// The message type that was expected at this point in the protocol.
expected: String,
/// The message type that was actually received.
got: String,
},
/// The pairing session exceeded its time limit without completing.
#[error("session expired")]
SessionExpired,
/// NIP-44 encryption or decryption failed.
#[error("NIP-44 error: {0}")]
Nip44(#[from] nostr::nips::nip44::Error),
/// JSON serialization or deserialization failed.
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
/// A public key string could not be parsed.
#[error("invalid pubkey: {0}")]
InvalidPubkey(String),
/// Event signing or construction failed.
#[error("event signing failed: {0}")]
SigningError(String),
}