blob: 1a5e430833c5c13305efe29396ee3b2c00963f4b [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use teaclave_crypto::TeaclaveFile128Key;
use std::collections::HashMap;
#[cfg(not(feature = "mesalock_sgx"))]
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, Write};
use std::path::{Path, PathBuf};
use std::prelude::v1::*;
#[cfg(feature = "mesalock_sgx")]
use std::untrusted::fs::File;
use crate::FileAuthTag;
use anyhow::Context;
use protected_fs::ProtectedFile;
#[derive(Clone, Debug, Default)]
pub struct StagedFileInfo {
pub path: PathBuf,
pub crypto_info: TeaclaveFile128Key,
pub cmac: FileAuthTag,
}
impl StagedFileInfo {
pub fn new(
path: impl AsRef<Path>,
crypto_info: TeaclaveFile128Key,
cmac: impl Into<FileAuthTag>,
) -> Self {
StagedFileInfo {
path: path.as_ref().into(),
crypto_info,
cmac: cmac.into(),
}
}
pub fn create_readable_io(&self) -> anyhow::Result<Box<dyn io::Read>> {
let f = ProtectedFile::open_ex(&self.path, &self.crypto_info.key)?;
let tag = f
.current_meta_gmac()
.context("Failed to get gmac from protected file")?;
anyhow::ensure!(self.cmac == tag, "Corrupted input file: {:?}", self.path);
Ok(Box::new(f))
}
pub fn create_writable_io(&self) -> anyhow::Result<Box<dyn io::Write>> {
let f = ProtectedFile::create_ex(&self.path, &self.crypto_info.key)?;
Ok(Box::new(f))
}
pub fn convert_file(
&self,
dst: impl AsRef<Path>,
crypto: TeaclaveFile128Key,
) -> anyhow::Result<StagedFileInfo> {
let src_file = ProtectedFile::open_ex(&self.path, &self.crypto_info.key)
.context("Convert: failed to open src file")?;
let mut dest_file = ProtectedFile::create_ex(dst.as_ref(), &crypto.key)
.context("Convert: failed to create dst file")?;
let mut reader = BufReader::with_capacity(4096, src_file);
loop {
let buffer = reader.fill_buf()?;
let rd_len = buffer.len();
if rd_len == 0 {
break;
}
let wt_len = dest_file.write(buffer)?;
anyhow::ensure!(
rd_len == wt_len,
"Cannot fully write to dest file: Rd({:?}) != Wt({:?})",
rd_len,
wt_len
);
reader.consume(rd_len);
}
dest_file
.flush()
.context("Convert: dst_file flush failed")?;
let tag = dest_file
.current_meta_gmac()
.context("Convert: cannot get dst_file gmac")?;
Ok(StagedFileInfo::new(dst, crypto, tag))
}
#[cfg(test_mode)]
pub fn create_with_plaintext_file(path: impl AsRef<Path>) -> anyhow::Result<StagedFileInfo> {
let bytes = read_all_bytes(path.as_ref())?;
let dst = path.as_ref().with_extension("test_enc");
Self::create_with_bytes(dst, &bytes)
}
#[cfg(test_mode)]
pub fn get_plaintext(&self) -> anyhow::Result<Vec<u8>> {
let mut content = Vec::new();
let mut f = ProtectedFile::open_ex(&self.path, &self.crypto_info.key)?;
f.read_to_end(&mut content)?;
Ok(content)
}
pub fn create_with_bytes(
path: impl AsRef<Path>,
bytes: &[u8],
) -> anyhow::Result<StagedFileInfo> {
let crypto = TeaclaveFile128Key::random();
let mut f = ProtectedFile::create_ex(&path, &crypto.key)?;
f.write_all(bytes)?;
f.flush()?;
let tag = f.current_meta_gmac()?;
Ok(Self::new(path.as_ref(), crypto, tag))
}
}
pub fn read_all_bytes(path: impl AsRef<Path>) -> anyhow::Result<Vec<u8>> {
let mut content = Vec::new();
let mut file = File::open(path)?;
file.read_to_end(&mut content)?;
Ok(content)
}
#[derive(Debug, Default, Clone)]
pub struct StagedFiles {
entries: HashMap<String, StagedFileInfo>,
}
impl StagedFiles {
pub fn new(entries: HashMap<String, StagedFileInfo>) -> Self {
StagedFiles { entries }
}
pub fn get(&self, key: &str) -> Option<&StagedFileInfo> {
self.entries.get(key)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl std::iter::FromIterator<(String, StagedFileInfo)> for StagedFiles {
fn from_iter<T: IntoIterator<Item = (String, StagedFileInfo)>>(iter: T) -> Self {
StagedFiles {
entries: HashMap::from_iter(iter),
}
}
}