trussed_chunked/
utils.rs

1// Copyright (C) Nitrokey GmbH
2// SPDX-License-Identifier: Apache-2.0 or MIT
3
4use serde_byte_array::ByteArray;
5use trussed_core::{
6    syscall, try_syscall,
7    types::{KeyId, Location, Message, PathBuf, UserAttribute},
8    Error,
9};
10
11use crate::{ChunkedClient, CHACHA8_STREAM_NONCE_LEN};
12
13#[derive(Clone, Copy)]
14pub struct EncryptionData {
15    pub key: KeyId,
16    pub nonce: Option<ByteArray<CHACHA8_STREAM_NONCE_LEN>>,
17}
18
19/// Write a large file (can be larger than 1KiB)
20///
21/// This is a wrapper around the [chunked writes api](ChunkedClient)
22pub fn write_all(
23    client: &mut impl ChunkedClient,
24    location: Location,
25    path: PathBuf,
26    data: &[u8],
27    user_attribute: Option<UserAttribute>,
28    encryption: Option<EncryptionData>,
29) -> Result<(), Error> {
30    if let (Ok(msg), None) = (Message::from_slice(data), encryption) {
31        // Fast path for small files
32        try_syscall!(client.write_file(location, path, msg, user_attribute))?;
33        Ok(())
34    } else {
35        write_chunked(client, location, path, data, user_attribute, encryption)
36    }
37}
38
39fn write_chunked(
40    client: &mut impl ChunkedClient,
41    location: Location,
42    path: PathBuf,
43    data: &[u8],
44    user_attribute: Option<UserAttribute>,
45    encryption: Option<EncryptionData>,
46) -> Result<(), Error> {
47    let res = write_chunked_inner(client, location, path, data, user_attribute, encryption);
48    if res.is_err() {
49        syscall!(client.abort_chunked_write());
50        return res;
51    }
52    Ok(())
53}
54
55fn write_chunked_inner(
56    client: &mut impl ChunkedClient,
57    location: Location,
58    path: PathBuf,
59    data: &[u8],
60    user_attribute: Option<UserAttribute>,
61    encryption: Option<EncryptionData>,
62) -> Result<(), Error> {
63    let msg = Message::new();
64    let chunk_size = msg.capacity();
65    let chunks = data.chunks(chunk_size).map(|chunk| {
66        Message::from_slice(chunk).expect("Iteration over chunks yields maximum of chunk_size")
67    });
68    if let Some(encryption_data) = encryption {
69        try_syscall!(client.start_encrypted_chunked_write(
70            location,
71            path,
72            encryption_data.key,
73            encryption_data.nonce,
74            user_attribute,
75        ))?;
76    } else {
77        try_syscall!(client.start_chunked_write(location, path, user_attribute))?;
78    }
79    let mut written = 0;
80    for chunk in chunks {
81        written += chunk.len();
82        try_syscall!(client.write_file_chunk(chunk))?;
83    }
84
85    if { written % chunk_size } == 0 {
86        try_syscall!(client.write_file_chunk(Message::new()))?;
87    }
88    Ok(())
89}