cashweb_auth_wrapper/
lib.rs1#![warn(
2 missing_debug_implementations,
3 missing_docs,
4 rust_2018_idioms,
5 unreachable_pub
6)]
7
8#[allow(unreachable_pub)]
13mod models;
14
15use std::convert::TryInto;
16
17use ring::digest::{digest, SHA256};
18use secp256k1::{key::PublicKey, Error as SecpError, Message, Secp256k1, Signature};
19use thiserror::Error;
20
21pub use models::{auth_wrapper::SignatureScheme, AuthWrapper};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct ParsedAuthWrapper {
26 pub public_key: PublicKey,
28 pub signature: Signature,
30 pub scheme: SignatureScheme,
32 pub payload: Vec<u8>,
34 pub payload_digest: [u8; 32],
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Error)]
40pub enum ParseError {
41 #[error(transparent)]
43 PublicKey(SecpError),
44 #[error(transparent)]
46 Signature(SecpError),
47 #[error("unsupported signature scheme")]
49 UnsupportedScheme,
50 #[error("fraudulent digest")]
52 FraudulentDigest,
53 #[error("digest and payload missing")]
55 DigestAndPayloadMissing,
56 #[error("unexpected length digest")]
58 UnexpectedLengthDigest,
59}
60
61impl AuthWrapper {
62 #[inline]
67 pub fn parse(self) -> Result<ParsedAuthWrapper, ParseError> {
68 let public_key = PublicKey::from_slice(&self.public_key).map_err(ParseError::PublicKey)?;
70
71 let scheme = SignatureScheme::from_i32(self.scheme).ok_or(ParseError::UnsupportedScheme)?;
73
74 let signature = Signature::from_compact(&self.signature).map_err(ParseError::Signature)?;
76
77 let payload_digest = match self.payload_digest.len() {
79 0 => {
80 if self.payload.is_empty() {
81 return Err(ParseError::DigestAndPayloadMissing);
82 } else {
83 let payload_digest = digest(&SHA256, &self.payload);
84 let digest_arr: [u8; 32] = payload_digest.as_ref().try_into().unwrap();
85 digest_arr
86 }
87 }
88 32 => {
89 let payload_digest = digest(&SHA256, &self.payload);
90 if *payload_digest.as_ref() != self.payload_digest[..] {
91 return Err(ParseError::FraudulentDigest);
92 }
93 let digest_arr: [u8; 32] = self.payload_digest[..].try_into().unwrap();
94 digest_arr
95 }
96 _ => return Err(ParseError::UnexpectedLengthDigest),
97 };
98
99 Ok(ParsedAuthWrapper {
100 public_key,
101 scheme,
102 signature,
103 payload_digest,
104 payload: self.payload,
105 })
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Error)]
111pub enum VerifyError {
112 #[error(transparent)]
114 InvalidSignature(SecpError),
115 #[error("unsupported signature scheme")]
117 UnsupportedScheme,
118}
119
120impl ParsedAuthWrapper {
121 #[inline]
123 pub fn verify(&self) -> Result<(), VerifyError> {
124 if self.scheme == SignatureScheme::Schnorr {
125 return Err(VerifyError::UnsupportedScheme);
127 }
128 let msg = Message::from_slice(self.payload_digest.as_ref()).unwrap(); let secp = Secp256k1::verification_only();
131 secp.verify(&msg, &self.signature, &self.public_key)
132 .map_err(VerifyError::InvalidSignature)?;
133 Ok(())
134 }
135}