biscuit_auth/format/
mod.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//! token serialization/deserialization
6//!
7//! Biscuit tokens are serialized to Protobuf. There are two levels of serialization:
8//!
9//! - serialization of Biscuit blocks to Protobuf then `Vec<u8>`
10//! - serialization of a wrapper structure containing serialized blocks and the signature
11use super::crypto::{self, KeyPair, PrivateKey, PublicKey, TokenNext};
12
13use prost::Message;
14
15use super::error;
16use super::token::Block;
17use crate::crypto::ExternalSignature;
18use crate::crypto::Signature;
19use crate::datalog::SymbolTable;
20use crate::token::RootKeyProvider;
21use crate::token::DATALOG_3_3;
22
23/// Structures generated from the Protobuf schema
24pub mod schema; /*{
25                    include!(concat!(env!("OUT_DIR"), "/biscuit.format.schema.rs"));
26                }*/
27
28pub mod convert;
29
30use self::convert::*;
31
32pub(crate) const THIRD_PARTY_SIGNATURE_VERSION: u32 = 1;
33pub(crate) const DATALOG_3_3_SIGNATURE_VERSION: u32 = 1;
34pub(crate) const NON_ED25519_SIGNATURE_VERSION: u32 = 1;
35/// Intermediate structure for token serialization
36///
37/// This structure contains the blocks serialized to byte arrays. Those arrays
38/// will be used for the signature
39#[derive(Clone, Debug)]
40pub struct SerializedBiscuit {
41    pub root_key_id: Option<u32>,
42    pub authority: crypto::Block,
43    pub blocks: Vec<crypto::Block>,
44    pub proof: crypto::TokenNext,
45}
46
47impl SerializedBiscuit {
48    pub fn from_slice<KP>(slice: &[u8], key_provider: KP) -> Result<Self, error::Format>
49    where
50        KP: RootKeyProvider,
51    {
52        let deser = SerializedBiscuit::deserialize(
53            slice,
54            ThirdPartyVerificationMode::PreviousSignatureHashing,
55        )?;
56
57        let root = key_provider.choose(deser.root_key_id)?;
58        deser.verify(&root)?;
59
60        Ok(deser)
61    }
62
63    pub(crate) fn unsafe_from_slice<KP>(
64        slice: &[u8],
65        key_provider: KP,
66    ) -> Result<Self, error::Format>
67    where
68        KP: RootKeyProvider,
69    {
70        let deser =
71            SerializedBiscuit::deserialize(slice, ThirdPartyVerificationMode::UnsafeLegacy)?;
72
73        let root = key_provider.choose(deser.root_key_id)?;
74        deser.verify_inner(&root, ThirdPartyVerificationMode::UnsafeLegacy)?;
75
76        Ok(deser)
77    }
78
79    pub(crate) fn deserialize(
80        slice: &[u8],
81        verification_mode: ThirdPartyVerificationMode,
82    ) -> Result<Self, error::Format> {
83        let data = schema::Biscuit::decode(slice).map_err(|e| {
84            error::Format::DeserializationError(format!("deserialization error: {:?}", e))
85        })?;
86
87        let next_key = PublicKey::from_proto(&data.authority.next_key)?;
88        let mut next_key_algorithm = next_key.algorithm();
89
90        let signature = Signature::from_vec(data.authority.signature);
91
92        if data.authority.external_signature.is_some() {
93            return Err(error::Format::DeserializationError(
94                "the authority block must not contain an external signature".to_string(),
95            ));
96        }
97
98        let authority = crypto::Block {
99            data: data.authority.block,
100            next_key,
101            signature,
102            external_signature: None,
103            version: data.authority.version.unwrap_or_default(),
104        };
105
106        let mut blocks = Vec::new();
107        for block in data.blocks {
108            let next_key = PublicKey::from_proto(&block.next_key)?;
109            next_key_algorithm = next_key.algorithm();
110
111            let signature = Signature::from_vec(block.signature);
112
113            let external_signature = if let Some(ex) = block.external_signature {
114                if verification_mode == ThirdPartyVerificationMode::PreviousSignatureHashing
115                    && block.version != Some(THIRD_PARTY_SIGNATURE_VERSION)
116                {
117                    return Err(error::Format::DeserializationError(
118                        "Unsupported third party block version".to_string(),
119                    ));
120                }
121
122                let public_key = PublicKey::from_proto(&ex.public_key)?;
123                let signature = Signature::from_vec(ex.signature);
124
125                Some(ExternalSignature {
126                    public_key,
127                    signature,
128                })
129            } else {
130                None
131            };
132
133            blocks.push(crypto::Block {
134                data: block.block.clone(),
135                next_key,
136                signature,
137                external_signature,
138                version: block.version.unwrap_or_default(),
139            });
140        }
141
142        let proof = match data.proof.content {
143            None => {
144                return Err(error::Format::DeserializationError(
145                    "could not find proof".to_string(),
146                ))
147            }
148            Some(schema::proof::Content::NextSecret(v)) => {
149                let next_key_algorithm = match next_key_algorithm {
150                    schema::public_key::Algorithm::Ed25519 => crate::builder::Algorithm::Ed25519,
151                    schema::public_key::Algorithm::Secp256r1 => {
152                        crate::builder::Algorithm::Secp256r1
153                    }
154                };
155                TokenNext::Secret(PrivateKey::from_bytes(&v, next_key_algorithm)?)
156            }
157            Some(schema::proof::Content::FinalSignature(v)) => {
158                let signature = Signature::from_vec(v);
159
160                TokenNext::Seal(signature)
161            }
162        };
163
164        let deser = SerializedBiscuit {
165            root_key_id: data.root_key_id,
166            authority,
167            blocks,
168            proof,
169        };
170
171        Ok(deser)
172    }
173
174    pub(crate) fn extract_blocks(
175        &self,
176        symbols: &mut SymbolTable,
177    ) -> Result<(schema::Block, Vec<schema::Block>), error::Token> {
178        let mut block_external_keys = Vec::new();
179
180        let authority = schema::Block::decode(&self.authority.data[..]).map_err(|e| {
181            error::Token::Format(error::Format::BlockDeserializationError(format!(
182                "error deserializing authority block: {:?}",
183                e
184            )))
185        })?;
186
187        symbols.extend(&SymbolTable::from(authority.symbols.clone())?)?;
188
189        for pk in &authority.public_keys {
190            symbols
191                .public_keys
192                .insert_fallible(&PublicKey::from_proto(pk)?)?;
193        }
194        // the authority block should not have an external key
195        block_external_keys.push(None);
196        //FIXME: return an error if the authority block has an external key
197
198        let mut blocks = vec![];
199
200        for block in self.blocks.iter() {
201            let deser = schema::Block::decode(&block.data[..]).map_err(|e| {
202                error::Token::Format(error::Format::BlockDeserializationError(format!(
203                    "error deserializing block: {:?}",
204                    e
205                )))
206            })?;
207
208            if let Some(external_signature) = &block.external_signature {
209                block_external_keys.push(Some(external_signature.public_key));
210            } else {
211                block_external_keys.push(None);
212                symbols.extend(&SymbolTable::from(deser.symbols.clone())?)?;
213                for pk in &deser.public_keys {
214                    symbols
215                        .public_keys
216                        .insert_fallible(&PublicKey::from_proto(pk)?)?;
217                }
218            }
219
220            blocks.push(deser);
221        }
222
223        Ok((authority, blocks))
224    }
225
226    /// serializes the token
227    pub fn to_proto(&self) -> schema::Biscuit {
228        let authority = schema::SignedBlock {
229            block: self.authority.data.clone(),
230            next_key: self.authority.next_key.to_proto(),
231            signature: self.authority.signature.to_bytes().to_vec(),
232            external_signature: None,
233            version: if self.authority.version > 0 {
234                Some(self.authority.version)
235            } else {
236                None
237            },
238        };
239
240        let mut blocks = Vec::new();
241        for block in &self.blocks {
242            let b = schema::SignedBlock {
243                block: block.data.clone(),
244                next_key: block.next_key.to_proto(),
245                signature: block.signature.to_bytes().to_vec(),
246                external_signature: block.external_signature.as_ref().map(|external_signature| {
247                    schema::ExternalSignature {
248                        signature: external_signature.signature.to_bytes().to_vec(),
249                        public_key: external_signature.public_key.to_proto(),
250                    }
251                }),
252                version: if block.version > 0 {
253                    Some(block.version)
254                } else {
255                    None
256                },
257            };
258
259            blocks.push(b);
260        }
261
262        schema::Biscuit {
263            root_key_id: self.root_key_id,
264            authority,
265            blocks,
266            proof: schema::Proof {
267                content: match &self.proof {
268                    TokenNext::Seal(signature) => Some(schema::proof::Content::FinalSignature(
269                        signature.to_bytes().to_vec(),
270                    )),
271                    TokenNext::Secret(private) => Some(schema::proof::Content::NextSecret(
272                        private.to_bytes().to_vec(),
273                    )),
274                },
275            },
276        }
277    }
278
279    pub fn serialized_size(&self) -> usize {
280        self.to_proto().encoded_len()
281    }
282
283    /// serializes the token
284    pub fn to_vec(&self) -> Result<Vec<u8>, error::Format> {
285        let b = self.to_proto();
286
287        let mut v = Vec::new();
288
289        b.encode(&mut v)
290            .map(|_| v)
291            .map_err(|e| error::Format::SerializationError(format!("serialization error: {:?}", e)))
292    }
293
294    /// creates a new token
295    pub fn new(
296        root_key_id: Option<u32>,
297        root_keypair: &KeyPair,
298        next_keypair: &KeyPair,
299        authority: &Block,
300    ) -> Result<Self, error::Token> {
301        let authority_signature_version = block_signature_version(
302            root_keypair,
303            next_keypair,
304            &None,
305            &Some(authority.version),
306            std::iter::empty(),
307        );
308        Self::new_inner(
309            root_key_id,
310            root_keypair,
311            next_keypair,
312            authority,
313            authority_signature_version,
314        )
315    }
316
317    /// creates a new token
318    pub(crate) fn new_inner(
319        root_key_id: Option<u32>,
320        root_keypair: &KeyPair,
321        next_keypair: &KeyPair,
322        authority: &Block,
323        authority_signature_version: u32,
324    ) -> Result<Self, error::Token> {
325        let mut v = Vec::new();
326        token_block_to_proto_block(authority)
327            .encode(&mut v)
328            .map_err(|e| {
329                error::Format::SerializationError(format!("serialization error: {:?}", e))
330            })?;
331
332        let signature = crypto::sign_authority_block(
333            root_keypair,
334            next_keypair,
335            &v,
336            authority_signature_version,
337        )?;
338
339        Ok(SerializedBiscuit {
340            root_key_id,
341            authority: crypto::Block {
342                data: v,
343                next_key: next_keypair.public(),
344                signature,
345                external_signature: None,
346                version: authority_signature_version,
347            },
348            blocks: vec![],
349            proof: TokenNext::Secret(next_keypair.private()),
350        })
351    }
352
353    /// adds a new block, serializes it and sign a new token
354    pub fn append(
355        &self,
356        next_keypair: &KeyPair,
357        block: &Block,
358        external_signature: Option<ExternalSignature>,
359    ) -> Result<Self, error::Token> {
360        let keypair = self.proof.keypair()?;
361
362        let mut v = Vec::new();
363        token_block_to_proto_block(block)
364            .encode(&mut v)
365            .map_err(|e| {
366                error::Format::SerializationError(format!("serialization error: {:?}", e))
367            })?;
368
369        let signature_version = block_signature_version(
370            &keypair,
371            next_keypair,
372            &external_signature,
373            &Some(block.version),
374            // std::iter::once(self.authority.version)
375            //     .chain(self.blocks.iter().map(|block| block.version)),
376            self.blocks
377                .iter()
378                .chain([&self.authority])
379                .map(|block| block.version),
380        );
381
382        let signature = crypto::sign_block(
383            &keypair,
384            next_keypair,
385            &v,
386            external_signature.as_ref(),
387            &self.last_block().signature,
388            signature_version,
389        )?;
390
391        // Add new block
392        let mut blocks = self.blocks.clone();
393        blocks.push(crypto::Block {
394            data: v,
395            next_key: next_keypair.public(),
396            signature,
397            external_signature,
398            version: signature_version,
399        });
400
401        Ok(SerializedBiscuit {
402            root_key_id: self.root_key_id,
403            authority: self.authority.clone(),
404            blocks,
405            proof: TokenNext::Secret(next_keypair.private()),
406        })
407    }
408
409    /// adds a new block, serializes it and sign a new token
410    pub fn append_serialized(
411        &self,
412        next_keypair: &KeyPair,
413        block: Vec<u8>,
414        external_signature: Option<ExternalSignature>,
415    ) -> Result<Self, error::Token> {
416        let keypair = self.proof.keypair()?;
417
418        let signature_version = block_signature_version(
419            &keypair,
420            next_keypair,
421            &external_signature,
422            // The version block is not directly available, so we don’t take it into account here
423            // `append_serialized` is only used for third-party blocks anyway, so maybe we should make `external_signature` mandatory and not bother
424            &None,
425            std::iter::once(self.authority.version)
426                .chain(self.blocks.iter().map(|block| block.version)),
427        );
428
429        let signature = crypto::sign_block(
430            &keypair,
431            next_keypair,
432            &block,
433            external_signature.as_ref(),
434            &self.last_block().signature,
435            signature_version,
436        )?;
437
438        // Add new block
439        let mut blocks = self.blocks.clone();
440        blocks.push(crypto::Block {
441            data: block,
442            next_key: next_keypair.public(),
443            signature,
444            external_signature,
445            version: signature_version,
446        });
447
448        Ok(SerializedBiscuit {
449            root_key_id: self.root_key_id,
450            authority: self.authority.clone(),
451            blocks,
452            proof: TokenNext::Secret(next_keypair.private()),
453        })
454    }
455
456    /// checks the signature on a deserialized token
457    pub fn verify(&self, root: &PublicKey) -> Result<(), error::Format> {
458        self.verify_inner(root, ThirdPartyVerificationMode::PreviousSignatureHashing)
459    }
460
461    pub(crate) fn verify_inner(
462        &self,
463        root: &PublicKey,
464        verification_mode: ThirdPartyVerificationMode,
465    ) -> Result<(), error::Format> {
466        //FIXME: try batched signature verification
467        let mut current_pub = root;
468        let mut previous_signature;
469
470        crypto::verify_authority_block_signature(&self.authority, current_pub)?;
471        current_pub = &self.authority.next_key;
472        previous_signature = &self.authority.signature;
473
474        for block in &self.blocks {
475            let verification_mode = match (block.version, verification_mode) {
476                (0, ThirdPartyVerificationMode::UnsafeLegacy) => {
477                    ThirdPartyVerificationMode::UnsafeLegacy
478                }
479                _ => ThirdPartyVerificationMode::PreviousSignatureHashing,
480            };
481
482            crypto::verify_block_signature(
483                block,
484                current_pub,
485                previous_signature,
486                verification_mode,
487            )?;
488            current_pub = &block.next_key;
489            previous_signature = &block.signature;
490        }
491
492        match &self.proof {
493            TokenNext::Secret(private) => {
494                if current_pub != &private.public() {
495                    return Err(error::Format::Signature(
496                        error::Signature::InvalidSignature(
497                            "the last public key does not match the private key".to_string(),
498                        ),
499                    ));
500                }
501            }
502            TokenNext::Seal(signature) => {
503                //FIXME: replace with SHA512 hashing
504                let block = if self.blocks.is_empty() {
505                    &self.authority
506                } else {
507                    &self.blocks[self.blocks.len() - 1]
508                };
509
510                let to_verify = crypto::generate_seal_signature_payload_v0(block);
511
512                current_pub.verify_signature(&to_verify, &signature)?;
513            }
514        }
515
516        Ok(())
517    }
518
519    pub fn seal(&self) -> Result<Self, error::Token> {
520        let keypair = self.proof.keypair()?;
521
522        //FIXME: replace with SHA512 hashing
523        let block = if self.blocks.is_empty() {
524            &self.authority
525        } else {
526            &self.blocks[self.blocks.len() - 1]
527        };
528
529        let to_sign = crypto::generate_seal_signature_payload_v0(block);
530
531        let signature = keypair.sign(&to_sign)?;
532
533        Ok(SerializedBiscuit {
534            root_key_id: self.root_key_id,
535            authority: self.authority.clone(),
536            blocks: self.blocks.clone(),
537            proof: TokenNext::Seal(signature),
538        })
539    }
540
541    pub(crate) fn last_block(&self) -> &crypto::Block {
542        self.blocks.last().unwrap_or(&self.authority)
543    }
544}
545
546#[derive(Clone, Copy, Debug, PartialEq)]
547pub(crate) enum ThirdPartyVerificationMode {
548    UnsafeLegacy,
549    PreviousSignatureHashing,
550}
551
552fn block_signature_version<I>(
553    block_keypair: &KeyPair,
554    next_keypair: &KeyPair,
555    external_signature: &Option<ExternalSignature>,
556    block_version: &Option<u32>,
557    previous_blocks_sig_versions: I,
558) -> u32
559where
560    I: Iterator<Item = u32>,
561{
562    if external_signature.is_some() {
563        return THIRD_PARTY_SIGNATURE_VERSION;
564    }
565
566    match block_version {
567        Some(block_version) if *block_version >= DATALOG_3_3 => {
568            return DATALOG_3_3_SIGNATURE_VERSION;
569        }
570        _ => {}
571    }
572
573    match (block_keypair, next_keypair) {
574        (KeyPair::Ed25519(_), KeyPair::Ed25519(_)) => {}
575        _ => {
576            return NON_ED25519_SIGNATURE_VERSION;
577        }
578    }
579
580    previous_blocks_sig_versions.max().unwrap_or(0)
581}
582
583#[cfg(test)]
584mod tests {
585    use std::io::Read;
586
587    use crate::{
588        builder::Algorithm,
589        crypto::{ExternalSignature, Signature},
590        format::block_signature_version,
591        token::{DATALOG_3_1, DATALOG_3_3},
592        KeyPair,
593    };
594
595    #[test]
596    fn proto() {
597        // somehow when building under cargo-tarpaulin, OUT_DIR is not set
598        let out_dir = match std::env::var("OUT_DIR") {
599            Ok(dir) => dir,
600            Err(_) => return,
601        };
602        prost_build::compile_protos(&["src/format/schema.proto"], &["src/"]).unwrap();
603        let mut file = std::fs::File::open(&format!("{out_dir}/biscuit.format.schema.rs")).unwrap();
604        let mut contents = String::new();
605        file.read_to_string(&mut contents).unwrap();
606
607        let commited_schema = include_str!("schema.rs");
608
609        if &contents != commited_schema {
610            println!(
611                "{}",
612                colored_diff::PrettyDifference {
613                    expected: &contents,
614                    actual: commited_schema
615                }
616            );
617            panic!();
618        }
619    }
620
621    #[test]
622    fn test_block_signature_version() {
623        assert_eq!(
624            block_signature_version(
625                &KeyPair::new(),
626                &KeyPair::new(),
627                &None,
628                &Some(DATALOG_3_1),
629                std::iter::empty()
630            ),
631            0,
632            "ed25519 everywhere, authority block, no new datalog features"
633        );
634        assert_eq!(
635            block_signature_version(
636                &KeyPair::new_with_algorithm(Algorithm::Secp256r1),
637                &KeyPair::new_with_algorithm(Algorithm::Ed25519),
638                &None,
639                &Some(DATALOG_3_1),
640                std::iter::empty()
641            ),
642            1,
643            "s256r1 root key, authority block, no new datalog features"
644        );
645        assert_eq!(
646            block_signature_version(
647                &KeyPair::new_with_algorithm(Algorithm::Ed25519),
648                &KeyPair::new_with_algorithm(Algorithm::Secp256r1),
649                &None,
650                &Some(DATALOG_3_1),
651                std::iter::empty()
652            ),
653            1,
654            "s256r1 next key, authority block, no new datalog features"
655        );
656        assert_eq!(
657            block_signature_version(
658                &KeyPair::new_with_algorithm(Algorithm::Secp256r1),
659                &KeyPair::new_with_algorithm(Algorithm::Secp256r1),
660                &None,
661                &Some(DATALOG_3_1),
662                std::iter::empty()
663            ),
664            1,
665            "s256r1 root & next key, authority block, no new datalog features"
666        );
667        assert_eq!(
668            block_signature_version(
669                &KeyPair::new(),
670                &KeyPair::new(),
671                &Some(ExternalSignature {
672                    public_key: KeyPair::new().public(),
673                    signature: Signature::from_vec(Vec::new())
674                }),
675                &Some(DATALOG_3_1),
676                std::iter::once(0)
677            ),
678            1,
679            "ed25519 root & next key, third-party block, no new datalog features"
680        );
681        assert_eq!(
682            block_signature_version(
683                &KeyPair::new(),
684                &KeyPair::new(),
685                &None,
686                &Some(DATALOG_3_3),
687                std::iter::empty()
688            ),
689            1,
690            "ed25519 root & next key, first-party block, new datalog features"
691        );
692        assert_eq!(
693            block_signature_version(
694                &KeyPair::new(),
695                &KeyPair::new(),
696                &None,
697                &Some(DATALOG_3_1),
698                std::iter::once(1)
699            ),
700            1,
701            "ed25519 root & next key, first-party block, no new datalog features, previous v1 block"
702        );
703    }
704}