use schemars::{JsonSchema, Schema, SchemaGenerator};
use serde::{Deserialize, Serialize};
use serde_json::json;
use super::schema::{add_rule, names, property};
use crate::{RevealFormat, SharingScheme, bip39};
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[schemars(transform = phrase_lengths)]
#[non_exhaustive]
pub struct RevealParams {
pub message: String,
#[serde(default)]
pub format: Option<RevealFormat>,
#[serde(default)]
pub note: Option<String>,
#[serde(default)]
#[schemars(range(min = 1))]
pub length: Option<u64>,
}
fn phrase_lengths(schema: &mut Schema) {
let format = RevealFormat::Bip39;
add_rule(
schema,
json!({
"if": {
"properties": { "format": { "const": format.as_str() } },
"required": ["format"],
},
"then": {
"properties": {
"length": {
"enum": bip39::phrase_lengths().collect::<Vec<_>>(),
"x-message": format!(
"'length' counts words for '{format}', and a phrase has {} words",
bip39::PHRASE_LENGTHS
),
}
}
},
}),
);
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[schemars(transform = longer_than_a_header)]
#[non_exhaustive]
pub struct EnterShareParams {
pub message: String,
#[serde(default)]
#[schemars(schema_with = "share_formats")]
pub format: Option<RevealFormat>,
#[serde(default)]
pub note: Option<String>,
#[serde(default)]
pub length: Option<u64>,
}
fn share_formats(_: &mut SchemaGenerator) -> Schema {
let (words, bytes): (Vec<RevealFormat>, Vec<RevealFormat>) = RevealFormat::ALL
.iter()
.partition(|format| format.is_words());
let mut schema = names(bytes.iter().map(|format| format.as_str()));
let words: Vec<&str> = words.iter().map(|format| format.as_str()).collect();
schema.insert(
"allOf".to_string(),
json!([{
"not": { "enum": words },
"x-message": format!(
"a share is written in {}; words are for the secret the shares combine into",
crate::types::either(bytes.iter().map(|format| format.as_str()))
),
}]),
);
schema
}
fn longer_than_a_header(schema: &mut Schema) {
let header = SharingScheme::RiteSssV1.header_len();
property(schema, "length").insert(
"allOf".to_string(),
json!([{
"minimum": header.saturating_add(1),
"x-message": format!(
"'length' is a share's secret plus {header} header bytes, so it is longer \
than {header}"
),
}]),
);
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[schemars(transform = share_limits)]
#[non_exhaustive]
pub struct SplitSecretParams {
#[serde(default)]
pub scheme: Option<SharingScheme>,
#[schemars(range(min = 2))]
pub threshold: u8,
#[schemars(range(min = 2))]
pub shares: u8,
}
const DEFAULT_SHARING_SCHEME: SharingScheme = SharingScheme::RiteSssV1;
fn share_limits(schema: &mut Schema) {
let largest = SharingScheme::ALL
.iter()
.map(|scheme| scheme.max_shares())
.max()
.unwrap_or(u8::MAX);
add_rule(
schema,
json!({
"properties": {
"threshold": { "maximum": largest },
"shares": { "maximum": largest },
}
}),
);
for &scheme in SharingScheme::ALL {
let max = scheme.max_shares();
let names_it = if scheme == DEFAULT_SHARING_SCHEME {
json!({ "properties": { "scheme": { "const": scheme.as_str() } } })
} else {
json!({
"properties": { "scheme": { "const": scheme.as_str() } },
"required": ["scheme"],
})
};
add_rule(
schema,
json!({
"if": names_it,
"then": {
"properties": {
"threshold": { "maximum": max },
"shares": { "maximum": max },
}
},
}),
);
}
}
impl Default for SplitSecretParams {
fn default() -> Self {
Self {
scheme: None,
threshold: 2,
shares: 2,
}
}
}