biscuit_auth/token/
unverified.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 */
5use prost::Message;
6
7use super::{default_symbol_table, Biscuit, Block};
8use crate::{
9    builder::BlockBuilder,
10    crypto::{self, PublicKey, Signature},
11    datalog::SymbolTable,
12    error,
13    format::{
14        convert::proto_block_to_token_block,
15        schema::{self, public_key::Algorithm},
16        SerializedBiscuit,
17    },
18    token::{ThirdPartyBlockContents, ThirdPartyRequest},
19    KeyPair, RootKeyProvider,
20};
21
22/// A token that was parsed without cryptographic signature verification
23///
24/// Use this if you want to attenuate or print the content of a token
25/// without verifying it.
26///
27/// It can be converted to a [Biscuit] using [UnverifiedBiscuit::verify],
28/// and then used for authorization
29#[derive(Clone, Debug)]
30pub struct UnverifiedBiscuit {
31    pub(crate) authority: schema::Block,
32    pub(crate) blocks: Vec<schema::Block>,
33    pub(crate) symbols: SymbolTable,
34    container: SerializedBiscuit,
35}
36
37impl UnverifiedBiscuit {
38    /// deserializes a token from raw bytes
39    pub fn from<T>(slice: T) -> Result<Self, error::Token>
40    where
41        T: AsRef<[u8]>,
42    {
43        Self::from_with_symbols(slice.as_ref(), default_symbol_table())
44    }
45
46    /// deserializes a token from raw bytes
47    ///
48    /// This allows the deprecated 3rd party block format
49    pub fn unsafe_deprecated_deserialize<T>(slice: T) -> Result<Self, error::Token>
50    where
51        T: AsRef<[u8]>,
52    {
53        let container = SerializedBiscuit::deserialize(
54            slice.as_ref(),
55            crate::format::ThirdPartyVerificationMode::UnsafeLegacy,
56        )?;
57        let mut symbols = default_symbol_table();
58
59        let (authority, blocks) = container.extract_blocks(&mut symbols)?;
60
61        Ok(UnverifiedBiscuit {
62            authority,
63            blocks,
64            symbols,
65            container,
66        })
67    }
68
69    /// deserializes a token from base64
70    pub fn from_base64<T>(slice: T) -> Result<Self, error::Token>
71    where
72        T: AsRef<[u8]>,
73    {
74        Self::from_base64_with_symbols(slice, default_symbol_table())
75    }
76
77    #[deprecated(since = "4.1.0", note = "please use `verify` instead")]
78    /// checks the signature of the token and convert it to a [Biscuit] for authorization
79    pub fn check_signature<F>(self, f: F) -> Result<Biscuit, error::Format>
80    where
81        F: Fn(Option<u32>) -> PublicKey,
82    {
83        self.verify(|kid| Ok(f(kid)))
84    }
85
86    /// checks the signature of the token and convert it to a [Biscuit] for authorization
87    pub fn verify<KP>(self, key_provider: KP) -> Result<Biscuit, error::Format>
88    where
89        KP: RootKeyProvider,
90    {
91        let key = key_provider.choose(self.root_key_id())?;
92        self.container.verify(&key)?;
93
94        Ok(Biscuit {
95            root_key_id: self.container.root_key_id,
96            authority: self.authority,
97            blocks: self.blocks,
98            symbols: self.symbols,
99            container: self.container,
100        })
101    }
102
103    /// adds a new block to the token
104    ///
105    /// since the public key is integrated into the token, the keypair can be
106    /// discarded right after calling this function
107    pub fn append(&self, block_builder: BlockBuilder) -> Result<Self, error::Token> {
108        let keypair =
109            KeyPair::new_with_rng(super::builder::Algorithm::Ed25519, &mut rand::rngs::OsRng);
110        self.append_with_keypair(&keypair, block_builder)
111    }
112
113    /// serializes the token
114    pub fn to_vec(&self) -> Result<Vec<u8>, error::Token> {
115        self.container.to_vec().map_err(error::Token::Format)
116    }
117
118    /// serializes the token and encode it to a (URL safe) base64 string
119    pub fn to_base64(&self) -> Result<String, error::Token> {
120        self.container
121            .to_vec()
122            .map_err(error::Token::Format)
123            .map(|v| base64::encode_config(v, base64::URL_SAFE))
124    }
125
126    /// deserializes from raw bytes with a custom symbol table
127    pub fn from_with_symbols(slice: &[u8], mut symbols: SymbolTable) -> Result<Self, error::Token> {
128        let container = SerializedBiscuit::deserialize(
129            slice,
130            crate::format::ThirdPartyVerificationMode::PreviousSignatureHashing,
131        )?;
132
133        let (authority, blocks) = container.extract_blocks(&mut symbols)?;
134
135        Ok(UnverifiedBiscuit {
136            authority,
137            blocks,
138            symbols,
139            container,
140        })
141    }
142
143    /// deserializes a token from base64 with a custom symbol table
144    pub fn from_base64_with_symbols<T>(slice: T, symbols: SymbolTable) -> Result<Self, error::Token>
145    where
146        T: AsRef<[u8]>,
147    {
148        let decoded = base64::decode_config(slice, base64::URL_SAFE)?;
149        Self::from_with_symbols(&decoded, symbols)
150    }
151
152    /// adds a new block to the token
153    ///
154    /// since the public key is integrated into the token, the keypair can be
155    /// discarded right after calling this function
156    pub fn append_with_keypair(
157        &self,
158        keypair: &KeyPair,
159        block_builder: BlockBuilder,
160    ) -> Result<Self, error::Token> {
161        let block = block_builder.build(self.symbols.clone());
162
163        if !self.symbols.is_disjoint(&block.symbols) {
164            return Err(error::Token::Format(error::Format::SymbolTableOverlap));
165        }
166
167        let authority = self.authority.clone();
168        let mut blocks = self.blocks.clone();
169        let mut symbols = self.symbols.clone();
170
171        let container = self.container.append(keypair, &block, None)?;
172
173        symbols.extend(&block.symbols)?;
174        symbols.public_keys.extend(&block.public_keys)?;
175
176        let deser = schema::Block::decode(
177            &container
178                .blocks
179                .last()
180                .expect("a new block was just added so the list is not empty")
181                .data[..],
182        )
183        .map_err(|e| {
184            error::Token::Format(error::Format::BlockDeserializationError(format!(
185                "error deserializing block: {:?}",
186                e
187            )))
188        })?;
189        blocks.push(deser);
190
191        Ok(UnverifiedBiscuit {
192            authority,
193            blocks,
194            symbols,
195            container,
196        })
197    }
198
199    /// returns an (optional) root key identifier. It provides a hint for public key selection during verification
200    pub fn root_key_id(&self) -> Option<u32> {
201        self.container.root_key_id
202    }
203
204    /// returns a list of revocation identifiers for each block, in order
205    ///
206    /// revocation identifiers are unique: tokens generated separately with
207    /// the same contents will have different revocation ids
208    pub fn revocation_identifiers(&self) -> Vec<Vec<u8>> {
209        let mut res = vec![self.container.authority.signature.to_bytes().to_vec()];
210
211        for block in self.container.blocks.iter() {
212            res.push(block.signature.to_bytes().to_vec());
213        }
214
215        res
216    }
217
218    /// returns a list of external key for each block, in order
219    ///
220    /// Blocks carrying an external public key are _third-party blocks_
221    /// and their contents can be trusted as coming from the holder of
222    /// the corresponding private key
223    pub fn external_public_keys(&self) -> Vec<Option<PublicKey>> {
224        let mut res = vec![None];
225
226        for block in self.container.blocks.iter() {
227            res.push(block.external_signature.as_ref().map(|sig| sig.public_key));
228        }
229
230        res
231    }
232
233    /// returns the number of blocks (at least 1)
234    pub fn block_count(&self) -> usize {
235        1 + self.container.blocks.len()
236    }
237
238    /// prints the content of a block as Datalog source code
239    pub fn print_block_source(&self, index: usize) -> Result<String, error::Token> {
240        self.block(index).map(|block| {
241            let symbols = if block.external_key.is_some() {
242                &block.symbols
243            } else {
244                &self.symbols
245            };
246            block.print_source(symbols)
247        })
248    }
249
250    /// gets the datalog version for a given block
251    pub fn block_version(&self, index: usize) -> Result<u32, error::Token> {
252        self.block(index).map(|block| block.version)
253    }
254
255    pub(crate) fn block(&self, index: usize) -> Result<Block, error::Token> {
256        let mut block = if index == 0 {
257            proto_block_to_token_block(
258                &self.authority,
259                self.container
260                    .authority
261                    .external_signature
262                    .as_ref()
263                    .map(|ex| ex.public_key),
264            )
265            .map_err(error::Token::Format)?
266        } else {
267            if index > self.blocks.len() + 1 {
268                return Err(error::Token::Format(
269                    error::Format::BlockDeserializationError("invalid block index".to_string()),
270                ));
271            }
272
273            proto_block_to_token_block(
274                &self.blocks[index - 1],
275                self.container.blocks[index - 1]
276                    .external_signature
277                    .as_ref()
278                    .map(|ex| ex.public_key),
279            )
280            .map_err(error::Token::Format)?
281        };
282
283        // we have to add the entire list of public keys here because
284        // they are used to validate 3rd party tokens
285        block.symbols.public_keys = self.symbols.public_keys.clone();
286        Ok(block)
287    }
288
289    /// creates a sealed version of the token
290    ///
291    /// sealed tokens cannot be attenuated
292    pub fn seal(&self) -> Result<UnverifiedBiscuit, error::Token> {
293        let container = self.container.seal()?;
294        let mut token = self.clone();
295        token.container = container;
296        Ok(token)
297    }
298
299    pub fn third_party_request(&self) -> Result<ThirdPartyRequest, error::Token> {
300        ThirdPartyRequest::from_container(&self.container)
301    }
302
303    pub fn append_third_party(&self, slice: &[u8]) -> Result<Self, error::Token> {
304        let next_keypair =
305            KeyPair::new_with_rng(super::builder::Algorithm::Ed25519, &mut rand::rngs::OsRng);
306        self.append_third_party_with_keypair(slice, next_keypair)
307    }
308
309    pub fn append_third_party_with_keypair(
310        &self,
311        slice: &[u8],
312        next_keypair: KeyPair,
313    ) -> Result<Self, error::Token> {
314        let ThirdPartyBlockContents {
315            payload,
316            external_signature,
317        } = schema::ThirdPartyBlockContents::decode(slice).map_err(|e| {
318            error::Format::DeserializationError(format!("deserialization error: {:?}", e))
319        })?;
320
321        let algorithm =
322            Algorithm::from_i32(external_signature.public_key.algorithm).ok_or_else(|| {
323                error::Format::DeserializationError(
324                    "deserialization error: invalid external key algorithm".to_string(),
325                )
326            })?;
327        let external_key =
328            PublicKey::from_bytes(&external_signature.public_key.key, algorithm.into()).map_err(
329                |e| {
330                    error::Format::BlockSignatureDeserializationError(format!(
331                        "block external public key deserialization error: {:?}",
332                        e
333                    ))
334                },
335            )?;
336
337        let signature = Signature::from_vec(external_signature.signature);
338
339        let block = schema::Block::decode(&payload[..]).map_err(|e| {
340            error::Token::Format(error::Format::DeserializationError(format!(
341                "deserialization error: {:?}",
342                e
343            )))
344        })?;
345
346        let external_signature = crypto::ExternalSignature {
347            public_key: external_key,
348            signature,
349        };
350
351        let mut symbols = self.symbols.clone();
352        let mut blocks = self.blocks.clone();
353
354        let container =
355            self.container
356                .append_serialized(&next_keypair, payload, Some(external_signature))?;
357
358        let token_block = proto_block_to_token_block(&block, Some(external_key)).unwrap();
359        for key in &token_block.public_keys.keys {
360            symbols.public_keys.insert_fallible(key)?;
361        }
362
363        blocks.push(block);
364
365        Ok(UnverifiedBiscuit {
366            authority: self.authority.clone(),
367            blocks,
368            symbols,
369            container,
370        })
371    }
372
373    pub fn append_third_party_base64<T>(&self, slice: T) -> Result<Self, error::Token>
374    where
375        T: AsRef<[u8]>,
376    {
377        let decoded = base64::decode_config(slice, base64::URL_SAFE)?;
378        self.append_third_party(&decoded)
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use crate::{BiscuitBuilder, BlockBuilder, KeyPair};
385
386    use super::UnverifiedBiscuit;
387
388    #[test]
389    fn consistent_with_biscuit() {
390        let root_key = KeyPair::new();
391        let external_key = KeyPair::new();
392        let biscuit = BiscuitBuilder::new()
393            .fact("test(true)")
394            .unwrap()
395            .build(&root_key)
396            .unwrap()
397            .append(BlockBuilder::new().fact("test(false)").unwrap())
398            .unwrap();
399        let req = biscuit.third_party_request().unwrap();
400        let res = req
401            .create_block(
402                &external_key.private(),
403                BlockBuilder::new().fact("third_party(true)").unwrap(),
404            )
405            .unwrap();
406        let biscuit = biscuit
407            .append_third_party(external_key.public(), res)
408            .unwrap();
409
410        let unverified = UnverifiedBiscuit::from_base64(biscuit.to_base64().unwrap()).unwrap();
411
412        unverified.clone().verify(root_key.public()).unwrap();
413
414        assert_eq!(unverified.blocks, biscuit.blocks);
415
416        assert_eq!(
417            unverified.external_public_keys(),
418            biscuit.external_public_keys()
419        );
420    }
421}