biscuit_auth/
error.rs

1/*
2 * Copyright (c) 2019 Geoffroy Couprie <contact@geoffroycouprie.com> and Contributors to the Eclipse Foundation.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5//! error types
6//!
7
8use std::{
9    convert::{From, Infallible},
10    fmt::Display,
11};
12use thiserror::Error;
13
14/// the global error type for Biscuit
15#[derive(Error, Clone, Debug, PartialEq, Eq)]
16#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
17pub enum Token {
18    #[error("internal error")]
19    InternalError,
20    #[error("error deserializing or verifying the token")]
21    Format(Format),
22    #[error("tried to append a block to a sealed token")]
23    AppendOnSealed,
24    #[error("tried to seal an already sealed token")]
25    AlreadySealed,
26    #[error("authorization failed: {0}")]
27    FailedLogic(Logic),
28    #[error("error generating Datalog: {0}")]
29    Language(biscuit_parser::error::LanguageError),
30    #[error("Reached Datalog execution limits")]
31    RunLimit(RunLimit),
32    #[error("Cannot convert from Term: {0}")]
33    ConversionError(String),
34    #[error("Cannot decode base64 token: {0}")]
35    Base64(Base64Error),
36    #[error("Datalog  execution failure: {0}")]
37    Execution(Expression),
38}
39
40impl From<Infallible> for Token {
41    fn from(_: Infallible) -> Self {
42        unreachable!()
43    }
44}
45
46impl From<Format> for Token {
47    fn from(e: Format) -> Self {
48        Token::Format(e)
49    }
50}
51
52impl From<Logic> for Token {
53    fn from(e: Logic) -> Self {
54        Token::FailedLogic(e)
55    }
56}
57
58impl From<biscuit_parser::error::LanguageError> for Token {
59    fn from(e: biscuit_parser::error::LanguageError) -> Self {
60        Token::Language(e)
61    }
62}
63
64impl From<base64::DecodeError> for Token {
65    fn from(e: base64::DecodeError) -> Self {
66        let err = match e {
67            base64::DecodeError::InvalidByte(offset, byte) => {
68                Base64Error::InvalidByte(offset, byte)
69            }
70            base64::DecodeError::InvalidLength => Base64Error::InvalidLength,
71            base64::DecodeError::InvalidLastSymbol(offset, byte) => {
72                Base64Error::InvalidLastSymbol(offset, byte)
73            }
74        };
75
76        Token::Base64(err)
77    }
78}
79
80impl From<Execution> for Token {
81    fn from(e: Execution) -> Self {
82        match e {
83            Execution::RunLimit(limit) => Token::RunLimit(limit),
84            Execution::Expression(e) => Token::Execution(e),
85        }
86    }
87}
88
89#[derive(Clone, Debug, PartialEq, Eq)]
90#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
91pub enum Base64Error {
92    InvalidByte(usize, u8),
93    InvalidLength,
94    InvalidLastSymbol(usize, u8),
95}
96
97impl std::fmt::Display for Base64Error {
98    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
99        match *self {
100            Base64Error::InvalidByte(index, byte) => {
101                write!(f, "Invalid byte {}, offset {}.", byte, index)
102            }
103            Base64Error::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."),
104            Base64Error::InvalidLastSymbol(index, byte) => {
105                write!(f, "Invalid last symbol {}, offset {}.", byte, index)
106            }
107        }
108    }
109}
110
111/// Errors related to the token's serialization format or cryptographic
112/// signature
113#[derive(Error, Clone, Debug, PartialEq, Eq)]
114#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
115pub enum Format {
116    #[error("failed verifying the signature")]
117    Signature(Signature),
118    #[error("failed verifying the signature of a sealed token")]
119    SealedSignature,
120    #[error("the token does not provide intermediate public keys")]
121    EmptyKeys,
122    #[error("the root public key was not recognized")]
123    UnknownPublicKey,
124    #[error("could not deserialize the wrapper object")]
125    DeserializationError(String),
126    #[error("could not serialize the wrapper object")]
127    SerializationError(String),
128    #[error("could not deserialize the block")]
129    BlockDeserializationError(String),
130    #[error("could not serialize the block")]
131    BlockSerializationError(String),
132    #[error("Block format version is higher than supported")]
133    Version {
134        maximum: u32,
135        minimum: u32,
136        actual: u32,
137    },
138    #[error("invalid key size")]
139    InvalidKeySize(usize),
140    #[error("invalid signature size")]
141    InvalidSignatureSize(usize),
142    #[error("invalid key")]
143    InvalidKey(String),
144    #[error("could not deserialize signature")]
145    SignatureDeserializationError(String),
146    #[error("could not deserialize the block signature")]
147    BlockSignatureDeserializationError(String),
148    #[error("invalid block id")]
149    InvalidBlockId(usize),
150    #[error("the public key is already present in previous blocks")]
151    ExistingPublicKey(String),
152    #[error("multiple blocks declare the same symbols")]
153    SymbolTableOverlap,
154    #[error("multiple blocks declare the same public keys")]
155    PublicKeyTableOverlap,
156    #[error("the external public key was not recognized")]
157    UnknownExternalKey,
158    #[error("the symbol id was not in the table")]
159    UnknownSymbol(u64),
160    #[cfg(feature = "pem")]
161    #[error("PKCS8 serialization error")]
162    PKCS8(String),
163}
164
165/// Signature errors
166#[derive(Error, Clone, Debug, PartialEq, Eq)]
167#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
168pub enum Signature {
169    #[error("could not parse the signature elements")]
170    InvalidFormat,
171    #[error("the signature did not match")]
172    InvalidSignature(String),
173    #[error("could not sign")]
174    InvalidSignatureGeneration(String),
175}
176
177/// errors in the Datalog evaluation
178#[derive(Error, Clone, Debug, PartialEq, Eq)]
179#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
180pub enum Logic {
181    #[error("a rule provided by a block is producing a fact with unbound variables")]
182    InvalidBlockRule(u32, String),
183    #[error("{policy}, and the following checks failed: {}", display_failed_checks(.checks))]
184    Unauthorized {
185        /// the policy that matched
186        policy: MatchedPolicy,
187        /// list of checks that failed validation
188        checks: Vec<FailedCheck>,
189    },
190    #[error("the authorizer already contains a token")]
191    AuthorizerNotEmpty,
192    #[error("no matching policy was found, and the following checks failed: {}", display_failed_checks(.checks))]
193    NoMatchingPolicy {
194        /// list of checks that failed validation
195        checks: Vec<FailedCheck>,
196    },
197}
198
199#[derive(Error, Clone, Debug, PartialEq, Eq)]
200#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
201pub enum MatchedPolicy {
202    #[error("an allow policy matched (policy index: {0})")]
203    Allow(usize),
204    #[error("a deny policy matched (policy index: {0})")]
205    Deny(usize),
206}
207
208/// check errors
209#[derive(Error, Clone, Debug, PartialEq, Eq)]
210#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
211pub enum FailedCheck {
212    #[error("{0}")]
213    Block(FailedBlockCheck),
214    #[error("{0}")]
215    Authorizer(FailedAuthorizerCheck),
216}
217
218fn display_failed_checks(c: &[FailedCheck]) -> String {
219    c.iter()
220        .map(|c| c.to_string())
221        .collect::<Vec<_>>()
222        .join(", ")
223}
224
225#[derive(Clone, Debug, PartialEq, Eq)]
226#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
227pub struct FailedBlockCheck {
228    pub block_id: u32,
229    pub check_id: u32,
230    /// pretty print of the rule that failed
231    pub rule: String,
232}
233
234impl Display for FailedBlockCheck {
235    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        write!(
237            f,
238            "Check n°{} in block n°{}: {}",
239            self.check_id, self.block_id, self.rule
240        )
241    }
242}
243
244#[derive(Clone, Debug, PartialEq, Eq)]
245#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
246pub struct FailedAuthorizerCheck {
247    pub check_id: u32,
248    /// pretty print of the rule that failed
249    pub rule: String,
250}
251
252impl Display for FailedAuthorizerCheck {
253    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254        write!(f, "Check n°{} in authorizer: {}", self.check_id, self.rule)
255    }
256}
257
258/// Datalog execution errors
259#[derive(Error, Clone, Debug, PartialEq, Eq)]
260#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
261pub enum Execution {
262    #[error("Reached Datalog execution limits")]
263    RunLimit(RunLimit),
264    #[error("Expression execution failure")]
265    Expression(Expression),
266}
267
268/// Datalog expression execution failure
269#[derive(Error, Clone, Debug, PartialEq, Eq)]
270#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
271pub enum Expression {
272    #[error("Unknown symbol")]
273    UnknownSymbol(u64),
274    #[error("Unknown variable")]
275    UnknownVariable(u32),
276    #[error("Invalid type")]
277    InvalidType,
278    #[error("Overflow")]
279    Overflow,
280    #[error("Division by zero")]
281    DivideByZero,
282    #[error("Wrong number of elements on stack")]
283    InvalidStack,
284    #[error("Shadowed variable")]
285    ShadowedVariable,
286    #[error("Undefined extern func: {0}")]
287    UndefinedExtern(String),
288    #[error("Error while evaluating extern func {0}: {1}")]
289    ExternEvalError(String, String),
290}
291
292/// runtime limits errors
293#[derive(Error, Clone, Debug, PartialEq, Eq)]
294#[cfg_attr(feature = "serde-error", derive(serde::Serialize, serde::Deserialize))]
295pub enum RunLimit {
296    #[error("too many facts generated")]
297    TooManyFacts,
298    #[error("too many engine iterations")]
299    TooManyIterations,
300    #[error("spent too much time verifying")]
301    Timeout,
302    #[error("Unexpected query results, expected {0} got {1}")]
303    UnexpectedQueryResult(usize, usize),
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    #[test]
311    fn error_format_strings() {
312        assert_eq!(
313            format!("{}", Token::ConversionError("test".to_owned())),
314            "Cannot convert from Term: test"
315        );
316
317        assert_eq!(
318            format!("{}", Token::Base64(Base64Error::InvalidLength)),
319            "Cannot decode base64 token: Encoded text cannot have a 6-bit remainder."
320        );
321
322        assert_eq!(
323            format!(
324                "{}",
325                Token::FailedLogic(Logic::Unauthorized {
326                    policy: MatchedPolicy::Allow(0),
327                    checks: vec![
328                        FailedCheck::Authorizer(FailedAuthorizerCheck {
329                            check_id: 0,
330                            rule: "check if false".to_string()
331                        }),
332                        FailedCheck::Block(FailedBlockCheck {
333                            block_id: 0,
334                            check_id: 0,
335                            rule: "check if false".to_string()
336                        })
337                    ]
338                })
339            )
340            .to_string(),
341            "authorization failed: an allow policy matched (policy index: 0), and the following checks failed: Check n°0 in authorizer: check if false, Check n°0 in block n°0: check if false"
342        );
343    }
344}