1use crate::bounded::{ParseLimits, parse_bounded};
4use crate::canonical::{CanonicalError, tagged_digest};
5use crate::ids::{ContentDigest, ExecutionPolicyId, PlanId};
6use crate::scope::Handle;
7use crate::serve::{
8 BUNDLE_LIMITS, Embedded, List, Maybe, SERVICE_LIMITS, Text, Validate, canonical_envelope,
9 invalid, service_document,
10};
11use crate::{Document, DocumentError, Timestamp};
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14use std::collections::BTreeMap;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
17#[serde(rename_all = "snake_case")]
18pub enum SigningMode {
19 RequireSigned,
20 AllowUnsigned,
21 }
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
24#[serde(rename_all = "snake_case", deny_unknown_fields)]
25pub enum Signer {
26 Unsigned,
27 Ed25519 {
28 key_id: Handle,
29 },
30 Sigstore {
31 certificate_identity: Text<1, 4096>,
32 issuer: Text<1, 4096>,
33 },
34}
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
36#[serde(transparent)]
37pub struct RegistrationId(pub ContentDigest);
38impl RegistrationId {
39 pub fn as_str(&self) -> &str {
40 self.0.as_str()
41 }
42}
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
46#[serde(deny_unknown_fields)]
47pub struct CompileReport {
48 pub command: CompileCommand,
49 pub status: CompileStatus,
50 pub plan_id: PlanId,
51 pub out: Text<1, 4096>,
52}
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
54pub enum CompileCommand {
55 #[serde(rename = "plan compile")]
56 Compile,
57}
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
59pub enum CompileStatus {
60 #[serde(rename = "compiled")]
61 Compiled,
62}
63service_document!(PlanBundle,PlanBundleSchema,"rustev.plan-bundle/1",BUNDLE_LIMITS,{
64 pub definition:Embedded<crate::definition::Definition>,
65 pub execution_policy:Maybe<Embedded<crate::execution::ExecutionPolicy>>,
66 pub calibrations:List<Embedded<crate::calibration::CalibrationArtifact>,0,1024>,
67 #[schemars(extend("maxProperties" = 1024))] pub bindings:BTreeMap<Handle,Handle>,pub compile_report:CompileReport,
69});
70impl Validate for PlanBundle {
71 fn validate(&self) -> Result<(), String> {
72 if self.bindings.len() > 1024 {
73 Err("at most 1024 backend bindings".into())
74 } else {
75 Ok(())
76 }
77 }
78}
79impl PlanBundle {
80 pub fn digest(&self) -> Result<ContentDigest, CanonicalError> {
84 Ok(ContentDigest::from_digest(tagged_digest(
85 Self::SCHEMA,
86 &Document::canonical(self)?,
87 )))
88 }
89}
90pub type Ed25519Signature = List<u8, 64, 64>;
93#[derive(Debug, Clone, Serialize)]
98#[serde(transparent)]
99pub struct SigstoreBundle(Box<serde_json::value::RawValue>);
100impl PartialEq for SigstoreBundle {
101 fn eq(&self, other: &Self) -> bool {
102 self.0.get() == other.0.get()
103 }
104}
105impl SigstoreBundle {
106 pub fn bytes(&self) -> &[u8] {
108 self.0.get().as_bytes()
109 }
110}
111impl JsonSchema for SigstoreBundle {
112 fn schema_name() -> std::borrow::Cow<'static, str> {
113 "SigstoreBundle".into()
114 }
115 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
116 schemars::json_schema!({"type":"object","maxProperties":10000,"description":"Opaque Sigstore JSON: at most 1 MiB, depth 32, strings 256 KiB, collections 10000, total values 100000, integer tokens 40 bytes."})
117 }
118}
119impl<'de> Deserialize<'de> for SigstoreBundle {
120 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
121 let raw = Box::<serde_json::value::RawValue>::deserialize(d)?;
122 let limits = ParseLimits {
123 max_bytes: 1024 * 1024,
124 max_depth: 32,
125 max_string_bytes: 256 * 1024,
126 max_collection_len: 10000,
127 max_total_values: 100000,
128 max_number_bytes: 40,
129 allow_fractional_numbers: false,
130 };
131 let value: serde_json::Value =
132 parse_bounded(raw.get().as_bytes(), &limits).map_err(serde::de::Error::custom)?;
133 if !value.is_object() {
134 return Err(serde::de::Error::custom(
135 "Sigstore bundle must be an object",
136 ));
137 }
138 Ok(Self(raw))
139 }
140}
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
142#[serde(rename_all = "snake_case", deny_unknown_fields)]
143pub enum SignatureScheme {
144 Ed25519 {
145 key_id: Handle,
146 signature: Ed25519Signature,
147 },
148 Sigstore {
149 bundle: SigstoreBundle,
150 },
151}
152service_document!(PlanBundleSignature,PlanBundleSignatureSchema,"rustev.plan-bundle-signature/1",SERVICE_LIMITS,{
153 pub bundle_digest:ContentDigest,pub scheme:SignatureScheme,
154});
155impl Validate for PlanBundleSignature {}
156impl PlanBundleSignature {
157 pub fn signing_input(&self) -> Vec<u8> {
158 format!("{}\n{}", Self::SCHEMA, self.bundle_digest).into_bytes()
159 }
160}
161service_document!(PlanRegistration,PlanRegistrationSchema,"rustev.plan-registration/1",SERVICE_LIMITS,{
162 pub plan_id:PlanId,pub execution_policy_id:Maybe<ExecutionPolicyId>,
163 pub bundle_digest:ContentDigest,pub signer:Signer,pub signing_mode:SigningMode,
165 pub service_build:Handle,pub configuration_digest:ContentDigest,pub registered_at_ms:Timestamp,
166});
167impl Validate for PlanRegistration {}
168impl PlanRegistration {
169 pub fn registration_id(&self) -> Result<RegistrationId, CanonicalError> {
171 let mut value = serde_json::to_value(self).map_err(|e| CanonicalError::Serialize {
172 message: e.to_string(),
173 })?;
174 value.as_object_mut().unwrap().remove("registered_at_ms");
175 Ok(RegistrationId(ContentDigest::from_digest(tagged_digest(
176 Self::SCHEMA,
177 &crate::canonical::canonical_value_bytes(&value)?,
178 ))))
179 }
180}