From 03219d40d9f87039ba9d6dde6fc6dfb4373b1fee Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Thu, 27 Feb 2020 12:20:48 +0000 Subject: [PATCH] Introduce label lattice definition in Oak Runtime Not used yet, but as a starting point for future functionality. Ref #630 --- oak/server/rust/oak_runtime/build.rs | 5 +- oak/server/rust/oak_runtime/src/label.rs | 218 +++++ oak/server/rust/oak_runtime/src/lib.rs | 1 + oak/server/rust/oak_runtime/src/proto/mod.rs | 1 + .../rust/oak_runtime/src/proto/policy.rs | 912 ++++++++++++++++++ 5 files changed, 1136 insertions(+), 1 deletion(-) create mode 100644 oak/server/rust/oak_runtime/src/label.rs create mode 100644 oak/server/rust/oak_runtime/src/proto/policy.rs diff --git a/oak/server/rust/oak_runtime/build.rs b/oak/server/rust/oak_runtime/build.rs index 41a64a7276e..efd2a61d540 100644 --- a/oak/server/rust/oak_runtime/build.rs +++ b/oak/server/rust/oak_runtime/build.rs @@ -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(), }) diff --git a/oak/server/rust/oak_runtime/src/label.rs b/oak/server/rust/oak_runtime/src/label.rs new file mode 100644 index 00000000000..989b665fee9 --- /dev/null +++ b/oak/server/rust/oak_runtime/src/label.rs @@ -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; + + /// Build the label from bytes. + fn deserialize(bytes: &[u8]) -> Option; + + /// 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(&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 { + let mut bytes = Vec::new(); + self.write_to_vec(&mut bytes) + .expect("could not serialize to bytes"); + bytes + } + fn deserialize(bytes: &[u8]) -> Option { + 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)); + } +} diff --git a/oak/server/rust/oak_runtime/src/lib.rs b/oak/server/rust/oak_runtime/src/lib.rs index 72a0893d132..29503286351 100644 --- a/oak/server/rust/oak_runtime/src/lib.rs +++ b/oak/server/rust/oak_runtime/src/lib.rs @@ -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; diff --git a/oak/server/rust/oak_runtime/src/proto/mod.rs b/oak/server/rust/oak_runtime/src/proto/mod.rs index c67111e87fa..9c416c7e05b 100644 --- a/oak/server/rust/oak_runtime/src/proto/mod.rs +++ b/oak/server/rust/oak_runtime/src/proto/mod.rs @@ -16,3 +16,4 @@ #[allow(clippy::all)] pub mod application; +pub mod policy; diff --git a/oak/server/rust/oak_runtime/src/proto/policy.rs b/oak/server/rust/oak_runtime/src/proto/policy.rs new file mode 100644 index 00000000000..b6cc49dd4c9 --- /dev/null +++ b/oak/server/rust/oak_runtime/src/proto/policy.rs @@ -0,0 +1,912 @@ +// This file is generated by rust-protobuf 2.10.1. Do not edit +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![cfg_attr(rustfmt, rustfmt_skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unsafe_code)] +#![allow(unused_imports)] +#![allow(unused_results)] +//! Generated file from `policy.proto` + +use protobuf::Message as Message_imported_for_functions; +use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions; + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_10_1; + +#[derive(PartialEq,Clone,Default)] +pub struct Label { + // message fields + pub secrecy_tags: ::protobuf::RepeatedField, + pub integrity_tags: ::protobuf::RepeatedField, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a Label { + fn default() -> &'a Label { +