Skip to main content

walletkit_core/flamingo/
errors.rs

1use flamingo_verifier_sealed_types::{
2    ComparisonRole, FailureReason, ImageFailureReason, ImageRole,
3};
4use thiserror::Error;
5
6/// A rejection reported inside encryption; not a signed statement.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Enum)]
8pub enum FlamingoMatchRejection {
9    /// Malformed encrypted request.
10    MalformedInputs,
11    /// Invalid PCP hashes file.
12    InvalidHashesJson,
13    /// Orb image did not match its PCP commitment.
14    ThumbnailHashMismatch,
15    /// Threshold was not a finite normalized cosine value.
16    InvalidThreshold,
17    /// An image was empty.
18    EmptyImage,
19    /// An image or total input exceeded the limit.
20    InputTooLarge,
21    /// The backend does not implement this capture variant.
22    UnsupportedCapture,
23    /// The backend does not implement this operation.
24    UnsupportedOperation,
25    /// A comparison did not meet the threshold.
26    MatchBelowThreshold {
27        /// The comparison that failed.
28        comparison: FlamingoComparison,
29    },
30    /// A named image could not pass analysis.
31    ImageRejected {
32        /// Image bytes or semantic image role.
33        image: FlamingoImageRole,
34        /// Approved validation reason.
35        reason: FlamingoImageFailureReason,
36    },
37    /// A named comparison failed.
38    MatchingFailed {
39        /// The comparison that failed.
40        comparison: FlamingoComparison,
41    },
42    /// An infrastructure failure, not a biological rejection.
43    Internal,
44}
45/// Comparison names match the worker protocol.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Enum)]
47pub enum FlamingoComparison {
48    /// Orb credential versus live selfie.
49    OrbSelfie,
50    /// Orb credential versus RTMS challenge.
51    OrbChallenge,
52    /// Live selfie versus RTMS challenge.
53    SelfieChallenge,
54}
55/// Image roles match the worker protocol.
56#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Enum)]
57pub enum FlamingoImageRole {
58    /// Orb credential image.
59    OrbCredential,
60    /// Live capture.
61    LiveSelfie,
62    /// RTMS challenge image.
63    RtmsChallenge,
64}
65/// Failures while configuring or performing a match request.
66#[derive(Debug, Error, uniffi::Error)]
67pub enum FlamingoError {
68    /// A caller-supplied value cannot form a valid match request.
69    #[error("invalid {attribute}: {reason}")]
70    InvalidInput {
71        /// Name of the invalid field.
72        attribute: String,
73        /// Why the value was rejected.
74        reason: String,
75    },
76    /// The verifier configuration was not valid.
77    #[error("invalid Flamingo verifier configuration: {0}")]
78    Configuration(String),
79    /// Assignment, attestation, transport, channel opening, or token verification failed.
80    #[error("Flamingo verifier request failed: {0}")]
81    Verifier(String),
82}
83
84impl From<FailureReason> for FlamingoMatchRejection {
85    fn from(value: FailureReason) -> Self {
86        match value {
87            FailureReason::MalformedInputs => Self::MalformedInputs,
88            FailureReason::InvalidHashesJson => Self::InvalidHashesJson,
89            FailureReason::ThumbnailHashMismatch => Self::ThumbnailHashMismatch,
90            FailureReason::InvalidThreshold => Self::InvalidThreshold,
91            FailureReason::EmptyImage => Self::EmptyImage,
92            FailureReason::InputTooLarge => Self::InputTooLarge,
93            FailureReason::UnsupportedCapture => Self::UnsupportedCapture,
94            FailureReason::UnsupportedOperation => Self::UnsupportedOperation,
95            FailureReason::Internal => Self::Internal,
96            FailureReason::MatchBelowThreshold(comparison) => {
97                Self::MatchBelowThreshold {
98                    comparison: comparison.into(),
99                }
100            }
101            FailureReason::MatchingFailed(comparison) => Self::MatchingFailed {
102                comparison: comparison.into(),
103            },
104            FailureReason::ImageRejected { image, reason } => Self::ImageRejected {
105                image: image.into(),
106                reason: reason.into(),
107            },
108        }
109    }
110}
111impl From<ComparisonRole> for FlamingoComparison {
112    fn from(value: ComparisonRole) -> Self {
113        match value {
114            ComparisonRole::OrbSelfie => Self::OrbSelfie,
115            ComparisonRole::OrbChallenge => Self::OrbChallenge,
116            ComparisonRole::SelfieChallenge => Self::SelfieChallenge,
117        }
118    }
119}
120impl From<ImageRole> for FlamingoImageRole {
121    fn from(value: ImageRole) -> Self {
122        match value {
123            ImageRole::OrbCredential => Self::OrbCredential,
124            ImageRole::LiveSelfie => Self::LiveSelfie,
125            ImageRole::RtmsChallenge => Self::RtmsChallenge,
126        }
127    }
128}
129/// Image rejection reasons, without raw engine diagnostics.
130#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Enum)]
131pub enum FlamingoImageFailureReason {
132    /// Image decoding or dimension validation failed.
133    InvalidImage,
134    /// Embedding generation failed.
135    TemplateFailed,
136    /// Too many faces.
137    TooManyFaces,
138    /// Image too dark.
139    ImageTooDark,
140    /// Image too bright.
141    ImageTooBright,
142    /// Illumination variance.
143    IlluminationVariance,
144    /// Face too small.
145    FaceTooSmall,
146    /// Face too big.
147    FaceTooBig,
148    /// Face resolution too low.
149    FaceResolutionTooLow,
150    /// Face too high.
151    FaceTooHigh,
152    /// Face too low.
153    FaceTooLow,
154    /// Face too far left.
155    FaceTooFarLeft,
156    /// Face too far right.
157    FaceTooFarRight,
158    /// Head pose yaw.
159    HeadPoseYaw,
160    /// Head pose pitch too high.
161    HeadPosePitchTooHigh,
162    /// Head pose pitch too low.
163    HeadPosePitchTooLow,
164    /// Head pose roll.
165    HeadPoseRoll,
166    /// Low quality.
167    LowQuality,
168    /// Sunglasses occlusion detected.
169    SunglassesOcclusionDetected,
170    /// Glasses occlusion detected.
171    GlassesOcclusionDetected,
172    /// Mask occlusion detected.
173    MaskOcclusionDetected,
174    /// Other occlusion detected.
175    OtherOcclusionDetected,
176    /// Hair occlusion detected.
177    HairOcclusionDetected,
178    /// Fas occlusion detected.
179    FasOcclusionDetected,
180    /// Spoof detected.
181    SpoofDetected,
182    /// Depth spoof detected.
183    DepthSpoofDetected,
184    /// Thermal spoof detected.
185    ThermalSpoofDetected,
186    /// Age below threshold.
187    AgeBelowThreshold,
188    /// No face detected.
189    NoFaceDetected,
190    /// Eyes closed.
191    EyesClosed,
192    /// Non neutral expression.
193    NonNeutralExpression,
194    /// Landmarks alignment.
195    LandmarksAlignment,
196    /// Face overexposed.
197    FaceOverexposed,
198    /// Face underexposed.
199    FaceUnderexposed,
200    /// Segmentation occlusion proportion.
201    SegmentationOcclusionProportion,
202    /// Bright artifacts.
203    BrightArtifacts,
204    /// Light guard score too low.
205    LightGuardScoreTooLow,
206    /// Low contrast.
207    LowContrast,
208    /// Mesh expression score.
209    MeshExpressionScore,
210    /// High color distortion.
211    HighColorDistortion,
212    /// Uneven lighting.
213    UnevenLighting,
214    /// Blurry face.
215    BlurryFace,
216    /// Noisy thermal image.
217    NoisyThermalImage,
218}
219impl From<ImageFailureReason> for FlamingoImageFailureReason {
220    fn from(value: ImageFailureReason) -> Self {
221        match value {
222            ImageFailureReason::InvalidImage => Self::InvalidImage,
223            ImageFailureReason::TemplateFailed => Self::TemplateFailed,
224            ImageFailureReason::TooManyFaces => Self::TooManyFaces,
225            ImageFailureReason::ImageTooDark => Self::ImageTooDark,
226            ImageFailureReason::ImageTooBright => Self::ImageTooBright,
227            ImageFailureReason::IlluminationVariance => Self::IlluminationVariance,
228            ImageFailureReason::FaceTooSmall => Self::FaceTooSmall,
229            ImageFailureReason::FaceTooBig => Self::FaceTooBig,
230            ImageFailureReason::FaceResolutionTooLow => Self::FaceResolutionTooLow,
231            ImageFailureReason::FaceTooHigh => Self::FaceTooHigh,
232            ImageFailureReason::FaceTooLow => Self::FaceTooLow,
233            ImageFailureReason::FaceTooFarLeft => Self::FaceTooFarLeft,
234            ImageFailureReason::FaceTooFarRight => Self::FaceTooFarRight,
235            ImageFailureReason::HeadPoseYaw => Self::HeadPoseYaw,
236            ImageFailureReason::HeadPosePitchTooHigh => Self::HeadPosePitchTooHigh,
237            ImageFailureReason::HeadPosePitchTooLow => Self::HeadPosePitchTooLow,
238            ImageFailureReason::HeadPoseRoll => Self::HeadPoseRoll,
239            ImageFailureReason::LowQuality => Self::LowQuality,
240            ImageFailureReason::SunglassesOcclusionDetected => {
241                Self::SunglassesOcclusionDetected
242            }
243            ImageFailureReason::GlassesOcclusionDetected => {
244                Self::GlassesOcclusionDetected
245            }
246            ImageFailureReason::MaskOcclusionDetected => Self::MaskOcclusionDetected,
247            ImageFailureReason::OtherOcclusionDetected => Self::OtherOcclusionDetected,
248            ImageFailureReason::HairOcclusionDetected => Self::HairOcclusionDetected,
249            ImageFailureReason::FasOcclusionDetected => Self::FasOcclusionDetected,
250            ImageFailureReason::SpoofDetected => Self::SpoofDetected,
251            ImageFailureReason::DepthSpoofDetected => Self::DepthSpoofDetected,
252            ImageFailureReason::ThermalSpoofDetected => Self::ThermalSpoofDetected,
253            ImageFailureReason::AgeBelowThreshold => Self::AgeBelowThreshold,
254            ImageFailureReason::NoFaceDetected => Self::NoFaceDetected,
255            ImageFailureReason::EyesClosed => Self::EyesClosed,
256            ImageFailureReason::NonNeutralExpression => Self::NonNeutralExpression,
257            ImageFailureReason::LandmarksAlignment => Self::LandmarksAlignment,
258            ImageFailureReason::FaceOverexposed => Self::FaceOverexposed,
259            ImageFailureReason::FaceUnderexposed => Self::FaceUnderexposed,
260            ImageFailureReason::SegmentationOcclusionProportion => {
261                Self::SegmentationOcclusionProportion
262            }
263            ImageFailureReason::BrightArtifacts => Self::BrightArtifacts,
264            ImageFailureReason::LightGuardScoreTooLow => Self::LightGuardScoreTooLow,
265            ImageFailureReason::LowContrast => Self::LowContrast,
266            ImageFailureReason::MeshExpressionScore => Self::MeshExpressionScore,
267            ImageFailureReason::HighColorDistortion => Self::HighColorDistortion,
268            ImageFailureReason::UnevenLighting => Self::UnevenLighting,
269            ImageFailureReason::BlurryFace => Self::BlurryFace,
270            ImageFailureReason::NoisyThermalImage => Self::NoisyThermalImage,
271        }
272    }
273}