Skip to content

Commit

Permalink
Introduce label lattice definition in Oak ABI
Browse files Browse the repository at this point in the history
Not used yet, but as a starting point for future functionality.

It is in the ABI because it will be used by both the runtime and the
SDK.

Ref project-oak#630
  • Loading branch information
tiziano88 committed Mar 5, 2020
1 parent 3924ae3 commit b97a38c
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions oak/server/rust/oak_abi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license = "Apache-2.0"

[dependencies]
prost = "*"
hashbrown = "*"

[build-dependencies]
oak_utils = "*"
Expand Down
3 changes: 2 additions & 1 deletion oak/server/rust/oak_abi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ use std::path;
fn main() {
let proto_dir = path::Path::new("../../../../oak/proto/");
let oak_api_path = &*proto_dir.join("oak_api.proto");
let policy_path = &*proto_dir.join("policy.proto");

// Tell Cargo that if the given file changes, to rerun this build script.
// https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed
println!("cargo:rerun-if-changed={}", oak_api_path.to_str().unwrap());

prost_build::compile_protos(&[oak_api_path], &[proto_dir]).unwrap();
prost_build::compile_protos(&[oak_api_path, policy_path], &[proto_dir]).unwrap();
}
228 changes: 228 additions & 0 deletions oak/server/rust/oak_abi/src/label.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
//
// Copyright 2020 The Project Oak Authors
//
// Licensed 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.
//

//! Labels are defined at the ABI level because they need to be available:
//!
//! - to applications (e.g. via the SDK), so that they can be manipulated by running Oak Nodes
//! - to the Runtime, so that they can be checked and enforced when data is exchanged between Nodes
//! over Channels
//!
//! In order to support this, Labels are serialized and deserialized across the ABI boundary, and
//! can be read and write using ABI functions.
use prost::Message;

// We use `hashbrown` since it is `no_std` compatible.
use hashbrown::HashSet;

/// A trait representing a label as part of a lattice.
pub trait Label: Sized {
/// Convert the label to bytes.
fn serialize(&self) -> Vec<u8>;

/// Build the label from bytes.
fn deserialize(bytes: &[u8]) -> Option<Self>;

/// Compare two labels according to the lattice structure: L_0 ⊑ L_1.
fn can_flow_to(&self, other: &Self) -> bool;
}

// Implement the necessary traits on `Tag` so that it may be used in `HashSet`.

impl Eq for crate::proto::policy::Tag {}

#[allow(clippy::derive_hash_xor_eq)]
impl std::hash::Hash for crate::proto::policy::Tag {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
// As a quick solution, we simply serialize the message and hash the resulting `Vec`.
let mut bytes = Vec::new();
self.encode(&mut bytes)
.expect("could not serialize to bytes");
bytes.hash(state);
}
}

impl Label for crate::proto::policy::Label {
fn serialize(&self) -> Vec<u8> {
let mut bytes = Vec::new();
self.encode(&mut bytes)
.expect("could not serialize to bytes");
bytes
}
fn deserialize(bytes: &[u8]) -> Option<Self> {
Self::decode(bytes).ok()
}

fn can_flow_to(&self, other: &Self) -> bool {
#![allow(clippy::mutable_key_type)]

let self_secrecy_tags: HashSet<_> = self.secrecy_tags.iter().collect();
let other_secrecy_tags: HashSet<_> = other.secrecy_tags.iter().collect();
let self_integrity_tags: HashSet<_> = self.integrity_tags.iter().collect();
let other_integrity_tags: HashSet<_> = other.integrity_tags.iter().collect();

// The target label must have (compared to the self label):
// - same or more secrecy tags
// - same or fewer integrity tags
self_secrecy_tags.is_subset(&other_secrecy_tags)
&& other_integrity_tags.is_subset(&self_integrity_tags)
}
}

#[cfg(test)]
mod tests {
use super::*;

fn authorization_bearer_token_hmac_tag(
authorization_bearer_token_hmac: &[u8],
) -> crate::proto::policy::Tag {
crate::proto::policy::Tag {
tag: Option::Some(crate::proto::policy::tag::Tag::GrpcTag(
crate::proto::policy::GrpcTag {
authorization_bearer_token_hmac: authorization_bearer_token_hmac.into(),
},
)),
}
}

#[test]
fn serialize_deserialize() {
let labels = vec![
crate::proto::policy::Label {
secrecy_tags: vec![],
integrity_tags: vec![],
},
crate::proto::policy::Label {
secrecy_tags: vec![authorization_bearer_token_hmac_tag(&[0, 0, 0])],
integrity_tags: vec![authorization_bearer_token_hmac_tag(&[1, 1, 1])],
},
crate::proto::policy::Label {
secrecy_tags: vec![
authorization_bearer_token_hmac_tag(&[0, 0, 0]),
authorization_bearer_token_hmac_tag(&[0, 0, 0]),
],
integrity_tags: vec![
authorization_bearer_token_hmac_tag(&[1, 1, 1]),
authorization_bearer_token_hmac_tag(&[1, 1, 1]),
],
},
crate::proto::policy::Label {
secrecy_tags: vec![
authorization_bearer_token_hmac_tag(&[0, 0, 0]),
authorization_bearer_token_hmac_tag(&[1, 1, 1]),
],
integrity_tags: vec![
authorization_bearer_token_hmac_tag(&[2, 2, 2]),
authorization_bearer_token_hmac_tag(&[3, 3, 3]),
],
},
];
for label in labels.iter() {
let bytes = label.serialize();
let deserialized = crate::proto::policy::Label::deserialize(&bytes).unwrap();
assert_eq!(*label, deserialized);
}
}

#[test]
fn label_flow() {
let tag_0 = authorization_bearer_token_hmac_tag(&[0, 0, 0]);
let tag_1 = authorization_bearer_token_hmac_tag(&[1, 1, 1]);

// The least privileged label.
//
// A node or channel with this label has only observed public data.
let public_trusted = crate::proto::policy::Label {
secrecy_tags: vec![],
integrity_tags: vec![],
};

// A label that corresponds to the secrecy of tag_0.
//
// A node or channel with this label may have observed data related to tag_0.
let label_0 = crate::proto::policy::Label {
secrecy_tags: vec![tag_0.clone()],
integrity_tags: vec![],
};

// A label that corresponds to the secrecy of tag_1.
//
// A node or channel with this label may have observed data related to tag_1.
let label_1 = crate::proto::policy::Label {
secrecy_tags: vec![tag_1.clone()],
integrity_tags: vec![],
};

// A label that corresponds to the combined secrecy of both tag_0 and tag_1.
//
// A node or channel with this label may have observed data related to tag_0 and tag_1.
let label_0_1 = crate::proto::policy::Label {
secrecy_tags: vec![tag_0.clone(), tag_1.clone()],
integrity_tags: vec![],
};

// A label identical to `label_0_1`, but in which tags appear in a different order. This
// should not make any difference in terms of what the label actually represent.
let label_1_0 = crate::proto::policy::Label {
secrecy_tags: vec![tag_1, tag_0],
integrity_tags: vec![],
};

// These labels form a lattice with the following shape:
//
// (label_0_1 == label_1_0)
// / \
// / \
// label_0 label_1
// \ /
// \ /
// public_trusted

// Data with any label can flow to the same label.
assert_eq!(true, public_trusted.can_flow_to(&public_trusted));
assert_eq!(true, label_0.can_flow_to(&label_0));
assert_eq!(true, label_1.can_flow_to(&label_1));
assert_eq!(true, label_0_1.can_flow_to(&label_0_1));
assert_eq!(true, label_1_0.can_flow_to(&label_1_0));

// label_0_1 and label_1_0 are effectively the same label, since the order of tags does not
// matter.
assert_eq!(true, label_0_1.can_flow_to(&label_1_0));
assert_eq!(true, label_1_0.can_flow_to(&label_0_1));

// public_trusted data can flow to more private data;
assert_eq!(true, public_trusted.can_flow_to(&label_0));
assert_eq!(true, public_trusted.can_flow_to(&label_1));
assert_eq!(true, public_trusted.can_flow_to(&label_0_1));

// Private data cannot flow to public_trusted.
assert_eq!(false, label_0.can_flow_to(&public_trusted));
assert_eq!(false, label_1.can_flow_to(&public_trusted));
assert_eq!(false, label_0_1.can_flow_to(&public_trusted));

// Private data with non-comparable labels cannot flow to each other.
assert_eq!(false, label_0.can_flow_to(&label_1));
assert_eq!(false, label_1.can_flow_to(&label_0));

// Private data can flow to even more private data.
assert_eq!(true, label_0.can_flow_to(&label_0_1));
assert_eq!(true, label_1.can_flow_to(&label_0_1));

// And vice versa.
assert_eq!(false, label_0_1.can_flow_to(&label_0));
assert_eq!(false, label_0_1.can_flow_to(&label_1));
}
}
2 changes: 2 additions & 0 deletions oak/server/rust/oak_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
pub mod proto;
pub use proto::*;

pub mod label;

/// Handle used to identify read or write channel halves.
///
/// These handles are used for all host function calls.
Expand Down
4 changes: 4 additions & 0 deletions oak/server/rust/oak_abi/src/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@
//

include!(concat!(env!("OUT_DIR"), "/oak.rs"));

pub mod policy {
include!(concat!(env!("OUT_DIR"), "/oak.policy.rs"));
}

0 comments on commit b97a38c

Please sign in to comment.