Files
start-os/core/src/registry/os/asset/sign.rs
Aiden McClelland c65db31fd9 Feature/consolidate setup (#3092)
* start consolidating

* add start-cli flash-os

* combine install and setup and refactor all

* use http

* undo mock

* fix translation

* translations

* use dialogservice wrapper

* better ST messaging on setup

* only warn on update if breakages (#3097)

* finish setup wizard and ui language-keyboard feature

* fix typo

* wip: localization

* remove start-tunnel readme

* switch to posix strings for language internal

* revert mock

* translate backend strings

* fix missing about text

* help text for args

* feat: add "Add new gateway" option (#3098)

* feat: add "Add new gateway" option

* Update web/projects/ui/src/app/routes/portal/components/form/controls/select.component.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add translation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Matt Hill <mattnine@protonmail.com>

* fix dns selection

* keyboard keymap also

* ability to shutdown after install

* revert mock

* working setup flow + manifest localization

* (mostly) redundant localization on frontend

* version bump

* omit live medium from disk list and better space management

* ignore missing package archive on 035 migration

* fix device migration

* add i18n helper to sdk

* fix install over 0.3.5.1

* fix grub config

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-27 14:44:41 -08:00

224 lines
6.4 KiB
Rust

use std::collections::BTreeMap;
use std::panic::UnwindSafe;
use std::path::PathBuf;
use clap::Parser;
use exver::Version;
use imbl_value::InternedString;
use itertools::Itertools;
use rpc_toolkit::{Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use crate::context::CliContext;
use crate::prelude::*;
use crate::progress::FullProgressTracker;
use crate::registry::asset::RegistryAsset;
use crate::registry::context::RegistryContext;
use crate::registry::os::SIG_CONTEXT;
use crate::registry::os::index::OsVersionInfo;
use crate::s9pk::merkle_archive::source::ArchiveSource;
use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile;
use crate::sign::commitment::blake3::Blake3Commitment;
use crate::sign::ed25519::Ed25519;
use crate::sign::{AnySignature, AnyVerifyingKey, SignatureScheme};
use crate::util::io::open_file;
use crate::util::serde::Base64;
pub fn sign_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"iso",
from_fn_async(sign_iso)
.with_metadata("get_signer", Value::Bool(true))
.no_cli(),
)
.subcommand(
"img",
from_fn_async(sign_img)
.with_metadata("get_signer", Value::Bool(true))
.no_cli(),
)
.subcommand(
"squashfs",
from_fn_async(sign_squashfs)
.with_metadata("get_signer", Value::Bool(true))
.no_cli(),
)
}
#[derive(Debug, Deserialize, Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
pub struct SignAssetParams {
#[ts(type = "string")]
version: Version,
#[ts(type = "string")]
platform: InternedString,
#[ts(skip)]
#[serde(rename = "__Auth_signer")]
signer: AnyVerifyingKey,
signature: AnySignature,
}
async fn sign_asset(
ctx: RegistryContext,
SignAssetParams {
version,
platform,
signer,
signature,
}: SignAssetParams,
accessor: impl FnOnce(
&mut Model<OsVersionInfo>,
) -> &mut Model<BTreeMap<InternedString, RegistryAsset<Blake3Commitment>>>
+ UnwindSafe
+ Send,
) -> Result<(), Error> {
ctx.db
.mutate(|db| {
let guid = db.as_index().as_signers().get_signer(&signer)?;
if !db
.as_index()
.as_os()
.as_versions()
.as_idx(&version)
.or_not_found(&version)?
.as_authorized()
.de()?
.contains(&guid)
{
return Err(Error::new(
eyre!("{}", t!("registry.os.asset.signer-not-authorized", guid = guid)),
ErrorKind::Authorization,
));
}
accessor(
db.as_index_mut()
.as_os_mut()
.as_versions_mut()
.as_idx_mut(&version)
.or_not_found(&version)?,
)
.as_idx_mut(&platform)
.or_not_found(&platform)?
.mutate(|s| {
signer.scheme().verify_commitment(
&signer,
&s.commitment,
SIG_CONTEXT,
&signature,
)?;
s.signatures.insert(signer, signature);
Ok(())
})?;
Ok(())
})
.await
.result
}
pub async fn sign_iso(ctx: RegistryContext, params: SignAssetParams) -> Result<(), Error> {
sign_asset(ctx, params, |m| m.as_iso_mut()).await
}
pub async fn sign_img(ctx: RegistryContext, params: SignAssetParams) -> Result<(), Error> {
sign_asset(ctx, params, |m| m.as_img_mut()).await
}
pub async fn sign_squashfs(ctx: RegistryContext, params: SignAssetParams) -> Result<(), Error> {
sign_asset(ctx, params, |m| m.as_squashfs_mut()).await
}
#[derive(Debug, Deserialize, Serialize, Parser)]
#[command(rename_all = "kebab-case")]
#[serde(rename_all = "camelCase")]
pub struct CliSignAssetParams {
#[arg(short = 'p', long = "platform", help = "help.arg.platform")]
pub platform: InternedString,
#[arg(short = 'v', long = "version", help = "help.arg.os-version")]
pub version: Version,
#[arg(help = "help.arg.asset-file-path")]
pub file: PathBuf,
}
pub async fn cli_sign_asset(
HandlerArgs {
context: ctx,
parent_method,
method,
params:
CliSignAssetParams {
platform,
version,
file: path,
},
..
}: HandlerArgs<CliContext, CliSignAssetParams>,
) -> Result<(), Error> {
let ext = match path.extension().and_then(|e| e.to_str()) {
Some("iso") => "iso",
Some("img") => "img",
Some("squashfs") => "squashfs",
_ => {
return Err(Error::new(
eyre!("{}", t!("registry.os.asset.unknown-extension")),
ErrorKind::InvalidRequest,
));
}
};
let file = MultiCursorFile::from(open_file(&path).await?);
let progress = FullProgressTracker::new();
let mut sign_phase = progress.add_phase(InternedString::intern("Signing File"), Some(10));
let mut index_phase = progress.add_phase(
InternedString::intern("Adding Signature to Registry Index"),
Some(1),
);
let progress_task =
progress.progress_bar_task(&format!("Adding {} to registry...", path.display()));
sign_phase.start();
let blake3 = file.blake3_mmap().await?;
let size = file
.size()
.await
.ok_or_else(|| Error::new(eyre!("{}", t!("registry.os.asset.failed-read-metadata")), ErrorKind::Filesystem))?;
let commitment = Blake3Commitment {
hash: Base64(*blake3.as_bytes()),
size,
};
let signature = AnySignature::Ed25519(Ed25519.sign_commitment(
ctx.developer_key()?,
&commitment,
SIG_CONTEXT,
)?);
sign_phase.complete();
index_phase.start();
ctx.call_remote::<RegistryContext>(
&parent_method
.into_iter()
.chain(method)
.chain([ext])
.join("."),
imbl_value::json!({
"platform": platform,
"version": version,
"signature": signature,
}),
)
.await?;
index_phase.complete();
progress.complete();
progress_task.await.with_kind(ErrorKind::Unknown)?;
Ok(())
}