Skip to content

Commit

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

Ref #630
  • Loading branch information
tiziano88 committed Feb 28, 2020
1 parent 6019c50 commit 03219d4
Show file tree
Hide file tree
Showing 5 changed files with 1,136 additions and 1 deletion.
5 changes: 4 additions & 1 deletion oak/server/rust/oak_runtime/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
fn main() {
oak_utils::run_protoc_rust(protoc_rust::Args {
out_dir: "src/proto",
input: &["../../../../oak/proto/application.proto"],
input: &[
"../../../../oak/proto/application.proto",
"../../../../oak/proto/policy.proto",
],
includes: &["../../../../oak/proto"],
customize: protoc_rust::Customize::default(),
})
Expand Down
218 changes: 218 additions & 0 deletions oak/server/rust/oak_runtime/src/label.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
//
// 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.
//

use protobuf::Message;
use std::collections::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.write_to_vec(&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.write_to_vec(&mut bytes)
.expect("could not serialize to bytes");
bytes
}
fn deserialize(bytes: &[u8]) -> Option<Self> {
protobuf::parse_from_bytes(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_oneof_tag::grpc_tag(
crate::proto::policy::GrpcTag {
authorization_bearer_token_hmac: authorization_bearer_token_hmac.into(),
..Default::default()
},
)),
..Default::default()
}
}

#[test]
fn serialize_deserialize() {
let labels = vec![
crate::proto::policy::Label {
secrecy_tags: vec![].into(),
integrity_tags: vec![].into(),
..Default::default()
},
crate::proto::policy::Label {
secrecy_tags: vec![authorization_bearer_token_hmac_tag(&[0, 0, 0])].into(),
integrity_tags: vec![authorization_bearer_token_hmac_tag(&[1, 1, 1])].into(),
..Default::default()
},
crate::proto::policy::Label {
secrecy_tags: vec![
authorization_bearer_token_hmac_tag(&[0, 0, 0]),
authorization_bearer_token_hmac_tag(&[0, 0, 0]),
]
.into(),
integrity_tags: vec![
authorization_bearer_token_hmac_tag(&[1, 1, 1]),
authorization_bearer_token_hmac_tag(&[1, 1, 1]),
]
.into(),
..Default::default()
},
crate::proto::policy::Label {
secrecy_tags: vec![
authorization_bearer_token_hmac_tag(&[0, 0, 0]),
authorization_bearer_token_hmac_tag(&[1, 1, 1]),
]
.into(),
integrity_tags: vec![
authorization_bearer_token_hmac_tag(&[2, 2, 2]),
authorization_bearer_token_hmac_tag(&[3, 3, 3]),
]
.into(),
..Default::default()
},
];
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 = crate::proto::policy::Label {
secrecy_tags: vec![].into(),
integrity_tags: vec![].into(),
..Default::default()
};

// 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()].into(),
integrity_tags: vec![].into(),
..Default::default()
};

// 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()].into(),
integrity_tags: vec![].into(),
..Default::default()
};

// 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, tag_1].into(),
integrity_tags: vec![].into(),
..Default::default()
};

// These labels form a lattice with the following shape:
//
// label_0_1
// / \
// / \
// label_0 label_1
// \ /
// \ /
// public

// Data with any label can flow to the same label.
assert_eq!(true, public.can_flow_to(&public));
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));

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

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

// 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));
}
}
1 change: 1 addition & 0 deletions oak/server/rust/oak_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod proto;

pub mod channel;
pub mod config;
pub mod label;
pub mod message;
pub mod node;
pub mod runtime;
Expand Down
1 change: 1 addition & 0 deletions oak/server/rust/oak_runtime/src/proto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@

#[allow(clippy::all)]
pub mod application;
pub mod policy;
Loading

0 comments on commit 03219d4

Please sign in to comment.