create-sia-app 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +179 -0
- package/package.json +29 -0
- package/template/CLAUDE.md +160 -0
- package/template/README.md +102 -0
- package/template/_gitignore +5 -0
- package/template/biome.json +40 -0
- package/template/index.html +13 -0
- package/template/package.json +30 -0
- package/template/rust/README.md +16 -0
- package/template/rust/sia-sdk-rs/.changeset/added_cancel_function_to_cancel_inflight_packed_uploads.md +6 -0
- package/template/rust/sia-sdk-rs/.changeset/check_if_we_have_enough_hosts_prior_to_encoding_in_upload_slabs.md +16 -0
- package/template/rust/sia-sdk-rs/.changeset/fix_slab_length_in_packed_object.md +5 -0
- package/template/rust/sia-sdk-rs/.changeset/fix_upload_racing_race_conditon.md +13 -0
- package/template/rust/sia-sdk-rs/.changeset/improved_parallelism_of_packed_uploads.md +5 -0
- package/template/rust/sia-sdk-rs/.changeset/progress_callback_will_now_be_called_as_expected_for_packed_uploads.md +5 -0
- package/template/rust/sia-sdk-rs/.github/dependabot.yml +10 -0
- package/template/rust/sia-sdk-rs/.github/workflows/main.yml +36 -0
- package/template/rust/sia-sdk-rs/.github/workflows/prepare-release.yml +34 -0
- package/template/rust/sia-sdk-rs/.github/workflows/release.yml +30 -0
- package/template/rust/sia-sdk-rs/.rustfmt.toml +4 -0
- package/template/rust/sia-sdk-rs/Cargo.lock +4127 -0
- package/template/rust/sia-sdk-rs/Cargo.toml +3 -0
- package/template/rust/sia-sdk-rs/LICENSE +21 -0
- package/template/rust/sia-sdk-rs/README.md +30 -0
- package/template/rust/sia-sdk-rs/indexd/CHANGELOG.md +79 -0
- package/template/rust/sia-sdk-rs/indexd/Cargo.toml +79 -0
- package/template/rust/sia-sdk-rs/indexd/benches/upload.rs +258 -0
- package/template/rust/sia-sdk-rs/indexd/src/app_client.rs +1710 -0
- package/template/rust/sia-sdk-rs/indexd/src/builder.rs +354 -0
- package/template/rust/sia-sdk-rs/indexd/src/download.rs +379 -0
- package/template/rust/sia-sdk-rs/indexd/src/hosts.rs +659 -0
- package/template/rust/sia-sdk-rs/indexd/src/lib.rs +827 -0
- package/template/rust/sia-sdk-rs/indexd/src/mock.rs +162 -0
- package/template/rust/sia-sdk-rs/indexd/src/object_encryption.rs +125 -0
- package/template/rust/sia-sdk-rs/indexd/src/quic.rs +575 -0
- package/template/rust/sia-sdk-rs/indexd/src/rhp4.rs +52 -0
- package/template/rust/sia-sdk-rs/indexd/src/slabs.rs +497 -0
- package/template/rust/sia-sdk-rs/indexd/src/upload.rs +629 -0
- package/template/rust/sia-sdk-rs/indexd/src/wasm_time.rs +41 -0
- package/template/rust/sia-sdk-rs/indexd/src/web_transport.rs +398 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/CHANGELOG.md +76 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/Cargo.toml +47 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/README.md +10 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/example.py +130 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/bin/uniffi-bindgen.rs +3 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/builder.rs +377 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/io.rs +155 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/lib.rs +1039 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/logging.rs +58 -0
- package/template/rust/sia-sdk-rs/indexd_ffi/src/tls.rs +23 -0
- package/template/rust/sia-sdk-rs/indexd_wasm/Cargo.toml +33 -0
- package/template/rust/sia-sdk-rs/indexd_wasm/src/lib.rs +818 -0
- package/template/rust/sia-sdk-rs/knope.toml +54 -0
- package/template/rust/sia-sdk-rs/sia_derive/CHANGELOG.md +38 -0
- package/template/rust/sia-sdk-rs/sia_derive/Cargo.toml +19 -0
- package/template/rust/sia-sdk-rs/sia_derive/src/lib.rs +278 -0
- package/template/rust/sia-sdk-rs/sia_sdk/CHANGELOG.md +91 -0
- package/template/rust/sia-sdk-rs/sia_sdk/Cargo.toml +59 -0
- package/template/rust/sia-sdk-rs/sia_sdk/benches/merkle_root.rs +12 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/blake2.rs +22 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/consensus.rs +767 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v1.rs +257 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v2.rs +291 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding.rs +26 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async/v2.rs +367 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async.rs +6 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/encryption.rs +303 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/erasure_coding.rs +347 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/lib.rs +15 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/macros.rs +435 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/merkle.rs +112 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/merkle.rs +357 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/rpc.rs +1507 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/types.rs +146 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp.rs +7 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/seed.rs +278 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/signing.rs +236 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/common.rs +677 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/currency.rs +450 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/specifier.rs +110 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/spendpolicy.rs +778 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/utils.rs +117 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/v1.rs +1737 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/v2.rs +1726 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/work.rs +59 -0
- package/template/rust/sia-sdk-rs/sia_sdk/src/types.rs +16 -0
- package/template/scripts/setup-rust.js +29 -0
- package/template/src/App.tsx +13 -0
- package/template/src/components/DevNote.tsx +21 -0
- package/template/src/components/auth/ApproveScreen.tsx +84 -0
- package/template/src/components/auth/AuthFlow.tsx +77 -0
- package/template/src/components/auth/ConnectScreen.tsx +214 -0
- package/template/src/components/auth/LoadingScreen.tsx +8 -0
- package/template/src/components/auth/RecoveryScreen.tsx +182 -0
- package/template/src/components/upload/UploadZone.tsx +314 -0
- package/template/src/index.css +9 -0
- package/template/src/lib/constants.ts +8 -0
- package/template/src/lib/format.ts +35 -0
- package/template/src/lib/hex.ts +13 -0
- package/template/src/lib/sdk.ts +25 -0
- package/template/src/lib/wasm-env.ts +5 -0
- package/template/src/main.tsx +12 -0
- package/template/src/stores/auth.ts +86 -0
- package/template/tsconfig.app.json +31 -0
- package/template/tsconfig.json +7 -0
- package/template/tsconfig.node.json +26 -0
- package/template/vite.config.ts +18 -0
- package/template/wasm/indexd_wasm/indexd_wasm.d.ts +309 -0
- package/template/wasm/indexd_wasm/indexd_wasm.js +1507 -0
- package/template/wasm/indexd_wasm/indexd_wasm_bg.wasm +0 -0
- package/template/wasm/indexd_wasm/package.json +31 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
use chrono::{DateTime, Utc};
|
|
2
|
+
use serde::{Deserialize, Serialize};
|
|
3
|
+
|
|
4
|
+
use serde_with::base64::Base64;
|
|
5
|
+
use serde_with::{DefaultOnNull, serde_as};
|
|
6
|
+
use sia::blake2::{Blake2b256, Digest};
|
|
7
|
+
use sia::encoding::{self, SiaDecodable, SiaDecode, SiaEncodable, SiaEncode};
|
|
8
|
+
use sia::encryption::{CipherReader, CipherWriter, EncryptionKey};
|
|
9
|
+
use sia::signing::{PrivateKey, PublicKey, Signature};
|
|
10
|
+
use sia::types::Hash256;
|
|
11
|
+
use thiserror::Error;
|
|
12
|
+
use tokio::io::{AsyncRead, AsyncWrite};
|
|
13
|
+
|
|
14
|
+
use crate::object_encryption::{
|
|
15
|
+
DecryptError, open_data_key, open_metadata, open_metadata_key, seal_data_key, seal_metadata,
|
|
16
|
+
seal_metadata_key,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, SiaEncode, SiaDecode)]
|
|
20
|
+
#[serde(rename_all = "camelCase")]
|
|
21
|
+
/// A Sector is a unit of data stored on the Sia network. It can be referenced by its Merkle root.
|
|
22
|
+
pub struct Sector {
|
|
23
|
+
pub root: Hash256,
|
|
24
|
+
pub host_key: PublicKey,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// A Slab is an erasure-coded collection of sectors. The sectors can be downloaded and
|
|
28
|
+
/// used to recover the original data.
|
|
29
|
+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
30
|
+
#[serde(rename_all = "camelCase")]
|
|
31
|
+
pub struct Slab {
|
|
32
|
+
pub encryption_key: EncryptionKey,
|
|
33
|
+
pub min_shards: u8,
|
|
34
|
+
pub sectors: Vec<Sector>,
|
|
35
|
+
pub offset: u32,
|
|
36
|
+
pub length: u32,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl Slab {
|
|
40
|
+
/// creates a unique identifier for the resulting slab to be referenced by hashing
|
|
41
|
+
/// its contents, excluding the host key, length, and offset.
|
|
42
|
+
pub fn digest(&self) -> Hash256 {
|
|
43
|
+
let mut state = Blake2b256::new();
|
|
44
|
+
|
|
45
|
+
(self.min_shards as u64).encode(&mut state).unwrap();
|
|
46
|
+
self.encryption_key.encode(&mut state).unwrap();
|
|
47
|
+
self.sectors.iter().for_each(|sector| {
|
|
48
|
+
sector.root.encode(&mut state).unwrap();
|
|
49
|
+
});
|
|
50
|
+
state.finalize().into()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl SiaEncodable for Slab {
|
|
55
|
+
fn encode<W: std::io::Write>(&self, w: &mut W) -> encoding::Result<()> {
|
|
56
|
+
self.encryption_key.encode(w)?;
|
|
57
|
+
self.min_shards.encode(w)?;
|
|
58
|
+
self.sectors.encode(w)?;
|
|
59
|
+
let combined: u64 = (self.offset as u64) << 32 | (self.length as u64);
|
|
60
|
+
combined.encode(w)?;
|
|
61
|
+
Ok(())
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
impl SiaDecodable for Slab {
|
|
66
|
+
fn decode<R: std::io::Read>(r: &mut R) -> encoding::Result<Self> {
|
|
67
|
+
let encryption_key = EncryptionKey::decode(r)?;
|
|
68
|
+
let min_shards = u8::decode(r)?;
|
|
69
|
+
let sectors = Vec::<Sector>::decode(r)?;
|
|
70
|
+
let combined = u64::decode(r)?;
|
|
71
|
+
|
|
72
|
+
Ok(Self {
|
|
73
|
+
encryption_key,
|
|
74
|
+
min_shards,
|
|
75
|
+
sectors,
|
|
76
|
+
offset: (combined >> 32) as u32,
|
|
77
|
+
length: combined as u32,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
|
83
|
+
#[serde(rename_all = "camelCase")]
|
|
84
|
+
pub struct PinnedSlab {
|
|
85
|
+
pub id: Hash256,
|
|
86
|
+
pub encryption_key: EncryptionKey,
|
|
87
|
+
pub min_shards: u8,
|
|
88
|
+
pub sectors: Vec<Sector>,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#[derive(Debug, Error)]
|
|
92
|
+
pub enum SealedObjectError {
|
|
93
|
+
#[error("decryption error: {0}")]
|
|
94
|
+
Decryption(#[from] DecryptError),
|
|
95
|
+
#[error("sealed object ID does not match contents")]
|
|
96
|
+
ContentsMismatch,
|
|
97
|
+
#[error("encoding error: {0}")]
|
|
98
|
+
Encoding(#[from] encoding::Error),
|
|
99
|
+
#[error("invalid signature")]
|
|
100
|
+
InvalidSignature,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#[serde_as]
|
|
104
|
+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
|
105
|
+
#[serde(rename_all = "camelCase")]
|
|
106
|
+
pub struct SealedObject {
|
|
107
|
+
#[serde_as(as = "Base64")]
|
|
108
|
+
pub encrypted_data_key: Vec<u8>,
|
|
109
|
+
pub slabs: Vec<Slab>,
|
|
110
|
+
pub data_signature: Signature,
|
|
111
|
+
|
|
112
|
+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
113
|
+
#[serde_as(as = "DefaultOnNull<Base64>")]
|
|
114
|
+
pub encrypted_metadata_key: Vec<u8>,
|
|
115
|
+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
116
|
+
#[serde_as(as = "DefaultOnNull<Base64>")]
|
|
117
|
+
pub encrypted_metadata: Vec<u8>,
|
|
118
|
+
pub metadata_signature: Signature,
|
|
119
|
+
|
|
120
|
+
pub created_at: DateTime<Utc>,
|
|
121
|
+
pub updated_at: DateTime<Utc>,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
impl SiaDecodable for SealedObject {
|
|
125
|
+
fn decode<R: std::io::Read>(r: &mut R) -> encoding::Result<Self> {
|
|
126
|
+
let encrypted_data_key = Vec::<u8>::decode(r)?;
|
|
127
|
+
let slabs = Vec::<Slab>::decode(r)?;
|
|
128
|
+
let data_signature = Signature::decode(r)?;
|
|
129
|
+
let encrypted_metadata_key = Vec::<u8>::decode(r)?;
|
|
130
|
+
let encrypted_metadata = Vec::<u8>::decode(r)?;
|
|
131
|
+
let metadata_signature = Signature::decode(r)?;
|
|
132
|
+
let created_at = DateTime::<Utc>::decode(r)?;
|
|
133
|
+
let updated_at = DateTime::<Utc>::decode(r)?;
|
|
134
|
+
|
|
135
|
+
Ok(Self {
|
|
136
|
+
encrypted_data_key,
|
|
137
|
+
slabs,
|
|
138
|
+
data_signature,
|
|
139
|
+
encrypted_metadata_key,
|
|
140
|
+
encrypted_metadata,
|
|
141
|
+
metadata_signature,
|
|
142
|
+
created_at,
|
|
143
|
+
updated_at,
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
impl SealedObject {
|
|
149
|
+
fn data_sig_hash(object_id: &Hash256, encrypted_data_key: &[u8]) -> Hash256 {
|
|
150
|
+
let mut state = Blake2b256::default();
|
|
151
|
+
object_id.encode(&mut state).unwrap();
|
|
152
|
+
state.update(encrypted_data_key);
|
|
153
|
+
state.finalize().into()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fn meta_sig_hash(
|
|
157
|
+
object_id: &Hash256,
|
|
158
|
+
encrypted_meta_key: &[u8],
|
|
159
|
+
encrypted_metadata: &[u8],
|
|
160
|
+
) -> Hash256 {
|
|
161
|
+
let mut state = Blake2b256::default();
|
|
162
|
+
object_id.encode(&mut state).unwrap();
|
|
163
|
+
state.update(encrypted_meta_key);
|
|
164
|
+
state.update(encrypted_metadata);
|
|
165
|
+
state.finalize().into()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
fn verify_signatures(
|
|
169
|
+
&self,
|
|
170
|
+
app_key: &PublicKey,
|
|
171
|
+
object_id: &Hash256,
|
|
172
|
+
) -> Result<(), SealedObjectError> {
|
|
173
|
+
let data_sig_hash = Self::data_sig_hash(object_id, &self.encrypted_data_key);
|
|
174
|
+
let meta_sig_hash = Self::meta_sig_hash(
|
|
175
|
+
object_id,
|
|
176
|
+
&self.encrypted_metadata_key,
|
|
177
|
+
&self.encrypted_metadata,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if !app_key.verify(data_sig_hash.as_ref(), &self.data_signature) {
|
|
181
|
+
return Err(SealedObjectError::InvalidSignature);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if !app_key.verify(meta_sig_hash.as_ref(), &self.metadata_signature) {
|
|
185
|
+
return Err(SealedObjectError::InvalidSignature);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
Ok(())
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
pub fn id(&self) -> Hash256 {
|
|
192
|
+
object_id(&self.slabs)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
pub fn open(self, app_key: &PrivateKey) -> Result<Object, SealedObjectError> {
|
|
196
|
+
// verify signatures first
|
|
197
|
+
let object_id = self.id();
|
|
198
|
+
self.verify_signatures(&app_key.public_key(), &object_id)?;
|
|
199
|
+
|
|
200
|
+
// decrypt data key and metadata
|
|
201
|
+
let data_key = open_data_key(app_key, &object_id, &self.encrypted_data_key)?;
|
|
202
|
+
let metadata = if !self.encrypted_metadata.is_empty() {
|
|
203
|
+
let metadata_key =
|
|
204
|
+
open_metadata_key(app_key, &object_id, &self.encrypted_metadata_key)?;
|
|
205
|
+
open_metadata(&metadata_key, &self.encrypted_metadata)?
|
|
206
|
+
} else {
|
|
207
|
+
Vec::new()
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
Ok(Object {
|
|
211
|
+
data_key,
|
|
212
|
+
slabs: self.slabs,
|
|
213
|
+
metadata,
|
|
214
|
+
created_at: self.created_at,
|
|
215
|
+
updated_at: self.updated_at,
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// An ObjectEvent represents an object and whether it was deleted or not.
|
|
221
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
222
|
+
pub struct ObjectEvent {
|
|
223
|
+
pub id: Hash256,
|
|
224
|
+
pub deleted: bool,
|
|
225
|
+
pub updated_at: DateTime<Utc>,
|
|
226
|
+
pub object: Option<Object>,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// An Object represents a file stored on the Sia network, consisting of multiple slabs and
|
|
230
|
+
// associated metadata.
|
|
231
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
232
|
+
pub struct Object {
|
|
233
|
+
data_key: EncryptionKey, // not public to avoid accidental exposure
|
|
234
|
+
|
|
235
|
+
slabs: Vec<Slab>,
|
|
236
|
+
created_at: DateTime<Utc>,
|
|
237
|
+
updated_at: DateTime<Utc>,
|
|
238
|
+
|
|
239
|
+
pub metadata: Vec<u8>,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
impl Object {
|
|
243
|
+
pub(crate) fn new(data_key: EncryptionKey, slabs: Vec<Slab>, metadata: Vec<u8>) -> Self {
|
|
244
|
+
Object {
|
|
245
|
+
data_key,
|
|
246
|
+
slabs,
|
|
247
|
+
metadata,
|
|
248
|
+
created_at: Utc::now(),
|
|
249
|
+
updated_at: Utc::now(),
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
pub fn id(&self) -> Hash256 {
|
|
254
|
+
object_id(&self.slabs)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/// Returns the slabs that make up the object.
|
|
258
|
+
pub fn slabs(&self) -> &[Slab] {
|
|
259
|
+
&self.slabs
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/// Returns the creation time of the object.
|
|
263
|
+
pub fn created_at(&self) -> &DateTime<Utc> {
|
|
264
|
+
&self.created_at
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// Returns the last updated time of the object.
|
|
268
|
+
pub fn updated_at(&self) -> &DateTime<Utc> {
|
|
269
|
+
&self.updated_at
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// Returns the total size of the object by summing the lengths of its slabs.
|
|
273
|
+
pub fn size(&self) -> u64 {
|
|
274
|
+
self.slabs.iter().fold(0_u64, |v, s| v + s.length as u64)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
pub fn seal(&self, app_key: &PrivateKey) -> SealedObject {
|
|
278
|
+
let object_id = self.id();
|
|
279
|
+
|
|
280
|
+
// encypt data key and create data signature
|
|
281
|
+
let encrypted_data_key = seal_data_key(app_key, &object_id, &self.data_key);
|
|
282
|
+
let data_signature = {
|
|
283
|
+
let sig_hash = SealedObject::data_sig_hash(&object_id, &encrypted_data_key);
|
|
284
|
+
app_key.sign(sig_hash.as_ref())
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// encrypt metadata key and metadata, if present, and create metadata signature
|
|
288
|
+
let (encrypted_metadata_key, encrypted_metadata) = if !self.metadata.is_empty() {
|
|
289
|
+
let metadata_key = EncryptionKey::from(rand::random::<[u8; 32]>());
|
|
290
|
+
let encrypted_metadata_key = seal_metadata_key(app_key, &object_id, &metadata_key);
|
|
291
|
+
let encrypted_metadata = seal_metadata(&metadata_key, &self.metadata);
|
|
292
|
+
(encrypted_metadata_key, encrypted_metadata)
|
|
293
|
+
} else {
|
|
294
|
+
(Vec::new(), Vec::new())
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
let metadata_signature = {
|
|
298
|
+
let sig_hash = SealedObject::meta_sig_hash(
|
|
299
|
+
&object_id,
|
|
300
|
+
&encrypted_metadata_key,
|
|
301
|
+
&encrypted_metadata,
|
|
302
|
+
);
|
|
303
|
+
app_key.sign(sig_hash.as_ref())
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
SealedObject {
|
|
307
|
+
encrypted_data_key,
|
|
308
|
+
encrypted_metadata_key,
|
|
309
|
+
slabs: self.slabs.clone(),
|
|
310
|
+
encrypted_metadata,
|
|
311
|
+
data_signature,
|
|
312
|
+
metadata_signature,
|
|
313
|
+
|
|
314
|
+
created_at: self.created_at,
|
|
315
|
+
updated_at: self.updated_at,
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
pub(crate) fn slabs_mut(&mut self) -> &mut Vec<Slab> {
|
|
320
|
+
&mut self.slabs
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/// Returns a reader that encrypts data on-the-fly using the object's encryption key.
|
|
324
|
+
pub(crate) fn reader<R: AsyncRead + Unpin>(&self, r: R, offset: usize) -> CipherReader<R> {
|
|
325
|
+
CipherReader::new(r, self.data_key.clone(), offset)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/// Returns a writer that encrypts data on-the-fly using the object's encryption key.
|
|
329
|
+
pub(crate) fn writer<W: AsyncWrite + Unpin>(&self, w: W, offset: usize) -> CipherWriter<W> {
|
|
330
|
+
CipherWriter::new(w, self.data_key.clone(), offset)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/// Returns the object's encryption key.
|
|
334
|
+
///
|
|
335
|
+
/// Be careful when using this function to avoid accidental exposure.
|
|
336
|
+
pub(crate) fn data_key(&self) -> &EncryptionKey {
|
|
337
|
+
&self.data_key
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
impl Default for Object {
|
|
342
|
+
fn default() -> Self {
|
|
343
|
+
Object {
|
|
344
|
+
data_key: EncryptionKey::from(rand::random::<[u8; 32]>()),
|
|
345
|
+
slabs: Vec::new(),
|
|
346
|
+
metadata: Vec::new(),
|
|
347
|
+
created_at: Utc::now(),
|
|
348
|
+
updated_at: Utc::now(),
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
pub(crate) fn object_id(slabs: &[Slab]) -> Hash256 {
|
|
354
|
+
let mut state = Blake2b256::default();
|
|
355
|
+
for slab in slabs.iter() {
|
|
356
|
+
let slab_id = slab.digest();
|
|
357
|
+
slab_id
|
|
358
|
+
.encode(&mut state)
|
|
359
|
+
.expect("hashing slab_id shouldn't fail");
|
|
360
|
+
((slab.offset as u64) << 32 | slab.length as u64)
|
|
361
|
+
.encode(&mut state)
|
|
362
|
+
.expect("hashing slab offset/length shouldn't fail");
|
|
363
|
+
}
|
|
364
|
+
state.finalize().into()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#[cfg(test)]
|
|
368
|
+
mod test {
|
|
369
|
+
use super::*;
|
|
370
|
+
use sia::hash_256;
|
|
371
|
+
|
|
372
|
+
#[test]
|
|
373
|
+
fn test_object_id() {
|
|
374
|
+
let slabs = vec![Slab {
|
|
375
|
+
encryption_key: [0u8; 32].into(),
|
|
376
|
+
min_shards: 1,
|
|
377
|
+
sectors: vec![Sector {
|
|
378
|
+
root: Hash256::new([1u8; 32]),
|
|
379
|
+
host_key: PublicKey::new([2u8; 32]),
|
|
380
|
+
}],
|
|
381
|
+
offset: 10,
|
|
382
|
+
length: 100,
|
|
383
|
+
}];
|
|
384
|
+
|
|
385
|
+
let id = object_id(&slabs);
|
|
386
|
+
assert_eq!(
|
|
387
|
+
id.to_string(),
|
|
388
|
+
"1b13d5dd22605af0573cae7fe9242c1ee83727c29798308b2b170864677b46d0"
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/// tests Slab.digest against a reference digest
|
|
393
|
+
#[test]
|
|
394
|
+
fn test_slab_digest() {
|
|
395
|
+
let s = Slab {
|
|
396
|
+
min_shards: 1,
|
|
397
|
+
encryption_key: [
|
|
398
|
+
152, 138, 169, 77, 22, 195, 154, 192, 91, 139, 241, 61, 75, 225, 38, 124, 225, 31,
|
|
399
|
+
187, 165, 80, 215, 75, 121, 115, 204, 235, 9, 90, 248, 68, 92,
|
|
400
|
+
]
|
|
401
|
+
.into(),
|
|
402
|
+
sectors: vec![
|
|
403
|
+
Sector {
|
|
404
|
+
root: hash_256!(
|
|
405
|
+
"fb0a42cce246d6bb9716eb0e97579a1d0d5c2bb34d7234e9ae271d4fd8201b24"
|
|
406
|
+
),
|
|
407
|
+
host_key: PublicKey::new(rand::random()), // host key is not included in the digest
|
|
408
|
+
},
|
|
409
|
+
Sector {
|
|
410
|
+
root: hash_256!(
|
|
411
|
+
"8125994daee38e1fbaf7a26c7935420ce055202f7175eae98d291ebe80f2b00e"
|
|
412
|
+
),
|
|
413
|
+
host_key: PublicKey::new(rand::random()), // host key is not included in the digest
|
|
414
|
+
},
|
|
415
|
+
Sector {
|
|
416
|
+
root: hash_256!(
|
|
417
|
+
"54ee41b57b9439868b119b8fe1c6c602bd6b35e27d31400c5bb85912b60c9f0a"
|
|
418
|
+
),
|
|
419
|
+
host_key: PublicKey::new(rand::random()), // host key is not included in the digest
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
// length and offset are not included in the digest
|
|
423
|
+
length: 100,
|
|
424
|
+
offset: 100,
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
assert_eq!(
|
|
428
|
+
s.digest().to_string(),
|
|
429
|
+
"d1aea84f8682d7ae17b6c1f14dc344eb70b9328ee913a76fc241559657b06284"
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
#[test]
|
|
434
|
+
fn test_object_roundtrip() {
|
|
435
|
+
let slabs = vec![
|
|
436
|
+
Slab {
|
|
437
|
+
encryption_key: rand::random::<[u8; 32]>().into(),
|
|
438
|
+
min_shards: 2,
|
|
439
|
+
sectors: vec![],
|
|
440
|
+
offset: 0,
|
|
441
|
+
length: 100,
|
|
442
|
+
},
|
|
443
|
+
Slab {
|
|
444
|
+
encryption_key: rand::random::<[u8; 32]>().into(),
|
|
445
|
+
min_shards: 2,
|
|
446
|
+
sectors: vec![],
|
|
447
|
+
offset: 0,
|
|
448
|
+
length: 100,
|
|
449
|
+
},
|
|
450
|
+
];
|
|
451
|
+
let meta = b"hello, world!".to_vec();
|
|
452
|
+
|
|
453
|
+
let mut obj = Object::default();
|
|
454
|
+
obj.slabs = slabs.clone();
|
|
455
|
+
obj.metadata = meta.clone();
|
|
456
|
+
|
|
457
|
+
let seed: [u8; 32] = rand::random();
|
|
458
|
+
let private_key = PrivateKey::from_seed(&seed);
|
|
459
|
+
|
|
460
|
+
let sealed = obj.seal(&private_key);
|
|
461
|
+
let opened = sealed.open(&private_key).expect("should open");
|
|
462
|
+
|
|
463
|
+
assert_eq!(opened.slabs, slabs);
|
|
464
|
+
assert_eq!(opened.metadata, meta);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/// tests that the SealedObject struct is compatible with the Go implementation
|
|
468
|
+
/// by deserializing a reference object
|
|
469
|
+
#[test]
|
|
470
|
+
fn test_sealed_object_golden() {
|
|
471
|
+
let mut seed = [0u8; 32];
|
|
472
|
+
hex::decode_to_slice(
|
|
473
|
+
"9593edfd90ef2da9973af3bca88afdf54b6e7ff66ff6c749505734b9cf6b8aec",
|
|
474
|
+
&mut seed,
|
|
475
|
+
)
|
|
476
|
+
.expect("hex");
|
|
477
|
+
let app_key = PrivateKey::from_seed(&seed);
|
|
478
|
+
|
|
479
|
+
let mut expected_object_key = [0u8; 32];
|
|
480
|
+
hex::decode_to_slice(
|
|
481
|
+
"cd280dd064e3c2d4259579bf0deda2492962c130ee8d7d2e11f1daca3e5cc579",
|
|
482
|
+
&mut expected_object_key,
|
|
483
|
+
)
|
|
484
|
+
.expect("hex");
|
|
485
|
+
let expected_object_key = EncryptionKey::from(expected_object_key);
|
|
486
|
+
|
|
487
|
+
let expected_metadata = hex::decode("b9d615255cc17596e3870c3adf5e11c1da0dd78e30f29c6b6d223949c7d91492db55cdc6e04ce65b5b1fea4ccc953e883d5bf23e9c893ecb5221e7315f16e7f95dfc70f0ed1ee1306e8733a22a5faf1b139f01f0f77ae00d71fe3bbefa4b65aca80f749e4788ace89beaa79ac651aedc54cba7066264df9db54c22c1e17cea1a").expect("hex");
|
|
488
|
+
|
|
489
|
+
let sealed_data = hex::decode("480000000000000063f49bd7cf21d25565ebb26a900efabec342418989958f09b735073af7dd5076231c32524ff6208f3a85f8cc8dd4a6ac913e0df51a34ab9ee325b41fc3de93a1107becc3b527f4870200000000000000739b1966a1d8ab194c0f0ebe012b00259491c13b73e1ad67128957590714dd33000000000000000000881300000a000000a4e3e3f002e05ff9d05a3193cc1513a66c10d82979a0a2fc9aa0aa4c7de2c64600000000000000000000100000200000005cc53fb91b99eb0d4aa422a820d2e64109a344e5234d7fb31ec62460f5dc52b6ae0b67c71093cebdb7a6cb79a65dfb6f2cb28eb3704a302a4517269fce75680c48000000000000008465df0369c7a4144257ce38bb566b47350e21ae0049b97d56348c1269c4fa5a1ea32f88c20e0ba000446f66e955c191d816bd4f3576caacbd309149343c629c0c7a7fc99201644da800000000000000f58f39972c8b1db49fef8606f20e690fb68fc9d29b5ab0da3a871ddb0cd5e66968ea0cf852a521697f422baac24c9418ca42fbc766d08fe0b55417e4d0f42831040e1c4e3dba1557ef7285649ab1c810ee1771a4c04d2bea4b2bbf592d3e3a1cee7a9217191242a6526db0c0ac09398f2090179063b0bdc2278a88540531f6ad3b5d44ecac36b1fef09205a2011ea4539a9abb914f23461a443efe47e581698416e452fa37af929a8bf8668d999920b5ec04005f2b947194b19271846b3c375e75257eebaad288bcb1eb36f6748562d97fcd0904eb491090ea8f61c0ddbc741f187af2b22151180400096e88f1ffffff00096e88f1ffffff").expect("hex");
|
|
490
|
+
let sealed = SealedObject::decode(&mut &sealed_data[..]).expect("decode");
|
|
491
|
+
let opened = sealed.open(&app_key).expect("open");
|
|
492
|
+
|
|
493
|
+
assert_eq!(opened.data_key, expected_object_key);
|
|
494
|
+
assert_eq!(opened.slabs.len(), 2);
|
|
495
|
+
assert_eq!(opened.metadata, expected_metadata);
|
|
496
|
+
}
|
|
497
|
+
}
|