biscuit_auth/token/
third_party.rs1use std::cmp::max;
6
7use prost::Message;
8
9use crate::{
10 builder::BlockBuilder,
11 crypto::generate_external_signature_payload_v1,
12 datalog::SymbolTable,
13 error,
14 format::{convert::token_block_to_proto_block, schema, SerializedBiscuit},
15 KeyPair, PrivateKey,
16};
17
18use super::THIRD_PARTY_SIGNATURE_VERSION;
19
20#[derive(PartialEq, Debug)]
22pub struct ThirdPartyRequest {
23 pub(crate) previous_signature: Vec<u8>,
24}
25
26impl ThirdPartyRequest {
27 pub(crate) fn from_container(
28 container: &SerializedBiscuit,
29 ) -> Result<ThirdPartyRequest, error::Token> {
30 if container.proof.is_sealed() {
31 return Err(error::Token::AppendOnSealed);
32 }
33
34 let previous_signature = container
35 .blocks
36 .last()
37 .unwrap_or(&container.authority)
38 .signature
39 .to_bytes()
40 .to_vec();
41 Ok(ThirdPartyRequest { previous_signature })
42 }
43
44 pub fn serialize(&self) -> Result<Vec<u8>, error::Token> {
45 let previous_signature = self.previous_signature.clone();
46
47 let request = schema::ThirdPartyBlockRequest {
48 legacy_previous_key: None,
49 legacy_public_keys: Vec::new(),
50 previous_signature,
51 };
52 let mut v = Vec::new();
53
54 request.encode(&mut v).map(|_| v).map_err(|e| {
55 error::Token::Format(error::Format::SerializationError(format!(
56 "serialization error: {:?}",
57 e
58 )))
59 })
60 }
61
62 pub fn serialize_base64(&self) -> Result<String, error::Token> {
63 Ok(base64::encode_config(self.serialize()?, base64::URL_SAFE))
64 }
65
66 pub fn deserialize(slice: &[u8]) -> Result<Self, error::Token> {
67 let data = schema::ThirdPartyBlockRequest::decode(slice).map_err(|e| {
68 error::Format::DeserializationError(format!("deserialization error: {:?}", e))
69 })?;
70
71 if !data.legacy_public_keys.is_empty() {
72 return Err(error::Token::Format(error::Format::DeserializationError(
73 "public keys were provided in third-party block request".to_owned(),
74 )));
75 }
76
77 if data.legacy_previous_key.is_some() {
78 return Err(error::Token::Format(error::Format::DeserializationError(
79 "previous public key was provided in third-party block request".to_owned(),
80 )));
81 }
82
83 let previous_signature = data.previous_signature.to_vec();
84
85 Ok(ThirdPartyRequest { previous_signature })
86 }
87
88 pub fn deserialize_base64<T>(slice: T) -> Result<Self, error::Token>
89 where
90 T: AsRef<[u8]>,
91 {
92 let decoded = base64::decode_config(slice, base64::URL_SAFE)?;
93 Self::deserialize(&decoded)
94 }
95
96 pub fn create_block(
98 self,
99 private_key: &PrivateKey,
100 block_builder: BlockBuilder,
101 ) -> Result<ThirdPartyBlock, error::Token> {
102 let symbols = SymbolTable::new();
103 let mut block = block_builder.build(symbols);
104 block.version = max(super::DATALOG_3_2, block.version);
105
106 let mut payload = Vec::new();
107 token_block_to_proto_block(&block)
108 .encode(&mut payload)
109 .map_err(|e| {
110 error::Format::SerializationError(format!("serialization error: {:?}", e))
111 })?;
112
113 let signed_payload = generate_external_signature_payload_v1(
114 &payload,
115 &self.previous_signature,
116 THIRD_PARTY_SIGNATURE_VERSION,
117 );
118
119 let keypair = KeyPair::from(private_key);
120 let signature = keypair.sign(&signed_payload)?;
121
122 let public_key = keypair.public();
123 let content = schema::ThirdPartyBlockContents {
124 payload,
125 external_signature: schema::ExternalSignature {
126 signature: signature.to_bytes().to_vec(),
127 public_key: public_key.to_proto(),
128 },
129 };
130
131 Ok(ThirdPartyBlock(content))
132 }
133}
134
135#[derive(Clone, Debug)]
140pub struct ThirdPartyBlock(pub(crate) schema::ThirdPartyBlockContents);
141
142impl ThirdPartyBlock {
143 pub fn serialize(&self) -> Result<Vec<u8>, error::Token> {
144 let mut buffer = vec![];
145 self.0.encode(&mut buffer).map(|_| buffer).map_err(|e| {
146 error::Token::Format(error::Format::SerializationError(format!(
147 "serialization error: {:?}",
148 e
149 )))
150 })
151 }
152
153 pub fn serialize_base64(&self) -> Result<String, error::Token> {
154 Ok(base64::encode_config(self.serialize()?, base64::URL_SAFE))
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn third_party_request_roundtrip() {
164 let mut rng: rand::rngs::StdRng = rand::SeedableRng::seed_from_u64(0);
165 let root = KeyPair::new_with_rng(crate::builder::Algorithm::Ed25519, &mut rng);
166 let biscuit1 = crate::Biscuit::builder()
167 .fact("right(\"file1\", \"read\")")
168 .unwrap()
169 .fact("right(\"file2\", \"read\")")
170 .unwrap()
171 .fact("right(\"file1\", \"write\")")
172 .unwrap()
173 .build_with_rng(&root, crate::token::default_symbol_table(), &mut rng)
174 .unwrap();
175 let req = biscuit1.third_party_request().unwrap();
176 let serialized_req = req.serialize().unwrap();
177 let parsed_req = ThirdPartyRequest::deserialize(&serialized_req).unwrap();
178
179 assert_eq!(req, parsed_req);
180 }
181}