190 lines
6.0 KiB
Rust
190 lines
6.0 KiB
Rust
//! 测试 key 只用于显式注入的 sumdb 对端,不进入产品公钥或项目设置。
|
|
|
|
use super::*;
|
|
use base64::{Engine, engine::general_purpose::STANDARD};
|
|
use ring::signature::{Ed25519KeyPair, KeyPair};
|
|
use sha2::{Digest, Sha256};
|
|
use std::{
|
|
cell::RefCell,
|
|
io::{Cursor, Write},
|
|
};
|
|
|
|
#[derive(Default)]
|
|
pub(super) struct Cache(pub RefCell<BTreeMap<String, Vec<u8>>>);
|
|
impl Store for Cache {
|
|
fn get(&self, hash: &str, _: u64) -> Result<Option<Vec<u8>>, String> {
|
|
Ok(self.0.borrow().get(hash).cloned())
|
|
}
|
|
fn put(&self, bytes: &[u8]) -> Result<(), String> {
|
|
self.0.borrow_mut().insert(
|
|
crate::compiler::project::dependency::digest(bytes),
|
|
bytes.to_vec(),
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default)]
|
|
pub(super) struct Registry(pub BTreeMap<String, Vec<u8>>);
|
|
impl Transport for Registry {
|
|
fn get(&self, url: &str, limit: usize) -> Result<Vec<u8>, String> {
|
|
let bytes = self
|
|
.0
|
|
.get(url)
|
|
.ok_or_else(|| format!("missing owned registry response {url}"))?;
|
|
assert!(bytes.len() <= limit);
|
|
Ok(bytes.clone())
|
|
}
|
|
}
|
|
|
|
pub(super) fn runtime() -> Metadata {
|
|
serde_json::from_value(serde_json::json!({"pack":{"name":"go", "version":"1.25.0", "sha256":"0".repeat(64)},
|
|
"target":BuildTarget::host().unwrap().name(), "python_markers":null, "python_wheel_tags":[]})).unwrap()
|
|
}
|
|
|
|
pub(super) fn zip(module: &str, version: &str, contents: &str) -> Vec<u8> {
|
|
let mut archive = zip::ZipWriter::new(Cursor::new(Vec::new()));
|
|
archive
|
|
.start_file(
|
|
format!("{module}@v{version}/value.go"),
|
|
zip::write::SimpleFileOptions::default(),
|
|
)
|
|
.unwrap();
|
|
archive.write_all(contents.as_bytes()).unwrap();
|
|
archive.finish().unwrap().into_inner()
|
|
}
|
|
|
|
fn key() -> Ed25519KeyPair {
|
|
Ed25519KeyPair::from_seed_unchecked(&[47; 32]).unwrap()
|
|
}
|
|
fn key_id() -> Vec<u8> {
|
|
let mut hash = Sha256::new();
|
|
hash.update(b"dever-sumdb-test\n");
|
|
hash.update([1]);
|
|
hash.update(key().public_key().as_ref());
|
|
hash.finalize()[..4].to_vec()
|
|
}
|
|
pub(super) fn verifier() -> Verifier {
|
|
let mut bytes = vec![1];
|
|
bytes.extend_from_slice(key().public_key().as_ref());
|
|
Verifier::new(&format!(
|
|
"dever-sumdb-test+{}+{}",
|
|
key_id()
|
|
.iter()
|
|
.map(|byte| format!("{byte:02x}"))
|
|
.collect::<String>(),
|
|
STANDARD.encode(bytes)
|
|
))
|
|
.unwrap()
|
|
}
|
|
|
|
fn root(hashes: &[[u8; 32]]) -> [u8; 32] {
|
|
if hashes.is_empty() {
|
|
return Sha256::digest([]).into();
|
|
}
|
|
if hashes.len() == 1 {
|
|
return hashes[0];
|
|
}
|
|
let split = hashes.len().next_power_of_two() / 2;
|
|
let mut hash = Sha256::new();
|
|
hash.update([1]);
|
|
hash.update(root(&hashes[..split]));
|
|
hash.update(root(&hashes[split..]));
|
|
hash.finalize().into()
|
|
}
|
|
|
|
impl Registry {
|
|
pub fn module(&mut self, name: &str, version: &str, source: &str) {
|
|
let prefix = format!(
|
|
"https://proxy.golang.org/{}/@v/v{version}",
|
|
module::escape(name)
|
|
);
|
|
self.0
|
|
.insert(format!("{prefix}.mod"), source.as_bytes().to_vec());
|
|
self.0.insert(
|
|
format!("{prefix}.zip"),
|
|
zip(name, version, "package value\n"),
|
|
);
|
|
}
|
|
|
|
pub fn seal(&mut self) {
|
|
self.0
|
|
.retain(|path, _| !path.starts_with("https://sum.golang.org/"));
|
|
let records = self
|
|
.0
|
|
.iter()
|
|
.filter_map(|(path, source)| {
|
|
let path = path.strip_prefix("https://proxy.golang.org/")?;
|
|
let (name, version) = path.split_once("/@v/v")?;
|
|
let version = version.strip_suffix(".mod")?;
|
|
let archive =
|
|
&self.0[&format!("https://proxy.golang.org/{name}/@v/v{version}.zip")];
|
|
let text = format!(
|
|
"{name} v{version} {}\n{name} v{version}/go.mod {}\n",
|
|
sumdb::zip_hash(name, version, archive).unwrap(),
|
|
sumdb::mod_hash(source)
|
|
);
|
|
Some((
|
|
format!("https://sum.golang.org/lookup/{name}@v{version}"),
|
|
text,
|
|
))
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let hashes = records
|
|
.iter()
|
|
.map(|(_, text)| {
|
|
let mut hash = Sha256::new();
|
|
hash.update([0]);
|
|
hash.update(text);
|
|
hash.finalize().into()
|
|
})
|
|
.collect::<Vec<[u8; 32]>>();
|
|
let text = format!(
|
|
"go.sum database tree\n{}\n{}\n",
|
|
hashes.len(),
|
|
STANDARD.encode(root(&hashes))
|
|
);
|
|
let mut signature = key_id();
|
|
signature.extend_from_slice(key().sign(text.as_bytes()).as_ref());
|
|
let note = format!(
|
|
"{text}\n— dever-sumdb-test {}\n",
|
|
STANDARD.encode(signature)
|
|
);
|
|
self.0.insert(
|
|
"https://sum.golang.org/latest".into(),
|
|
note.as_bytes().to_vec(),
|
|
);
|
|
for (id, (path, text)) in records.into_iter().enumerate() {
|
|
self.0
|
|
.insert(path, format!("{id}\n{text}\n{note}").into_bytes());
|
|
}
|
|
assert!(hashes.len() < 256);
|
|
self.0.insert(
|
|
format!("https://sum.golang.org/tile/8/0/000.p/{}", hashes.len()),
|
|
hashes.into_iter().flatten().collect(),
|
|
);
|
|
}
|
|
}
|
|
|
|
pub(super) fn graph() -> Registry {
|
|
let mut registry = Registry::default();
|
|
for (name, version, source) in [
|
|
(
|
|
"a",
|
|
"1.0.0",
|
|
"module example.com/a\nrequire (\nexample.com/b v1.0.0\nexample.com/c v1.0.0\n)\n",
|
|
),
|
|
("b", "1.0.0", "module example.com/b\n"),
|
|
("b", "1.2.0", "module example.com/b\n"),
|
|
(
|
|
"c",
|
|
"1.0.0",
|
|
"module example.com/c\nrequire example.com/b v1.2.0\n",
|
|
),
|
|
] {
|
|
registry.module(&format!("example.com/{name}"), version, source);
|
|
}
|
|
registry.seal();
|
|
registry
|
|
}
|