diff --git a/.gitignore b/.gitignore index c5e82d7..842aa51 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,9 @@ -bin \ No newline at end of file +bin +# in Rust projects, /src/bin/ holds source code +!**/src/bin + +# in Rust projects, this directory contains only derived files. +target + +# in Rust projects, lock files +Cargo.lock diff --git a/attestation_asps/python_asps/.gitignore b/attestation_asps/python_asps/.gitignore new file mode 100644 index 0000000..f584da3 --- /dev/null +++ b/attestation_asps/python_asps/.gitignore @@ -0,0 +1,3 @@ +*.spec +build/ +dist/ diff --git a/attestation_asps/python_asps/Makefile b/attestation_asps/python_asps/Makefile new file mode 100644 index 0000000..7f462fa --- /dev/null +++ b/attestation_asps/python_asps/Makefile @@ -0,0 +1,20 @@ +# Expects that the Python package, pyinstaller, has been installed, +# and is in the search path. +# To install: +# python3 -m pip install pyinstaller + +SRCDIR := ./ +BINDIR := dist/ +DEST_BINDIR := ../bin/ + +SOURCE := p_hashfile_id.py +AUX_FILES := copland.py +EXE_NAME := $(patsubst %.py,%, $(SOURCE)) + +all: $(SOURCE) $(AUX_FILES) + pyinstaller $(SOURCE) --onefile + cp $(BINDIR)/$(EXE_NAME) $(DEST_BINDIR) + +clean: + rm -rf $(BINDIR) + rm -rf build/ diff --git a/attestation_asps/python_asps/TEMPLATE.py.txt b/attestation_asps/python_asps/TEMPLATE.py.txt new file mode 100644 index 0000000..3560899 --- /dev/null +++ b/attestation_asps/python_asps/TEMPLATE.py.txt @@ -0,0 +1,71 @@ +## TEMPLATE.txt +## General structure for ASP's written in rust + +import sys +import json +import hashlib +import binascii +import base64 + +import copland + +## function where the work of the ASP is performed. +## May raise exception(s) which will be handled in main. + +def body(): + + # For every ASP, an ASPRunRequest appears as the single command-line argument + numargs = len(sys.argv) + if (numargs == 1): + raise Exception("no ASPRunRequest provided to p_hashfile_id") + json_req = sys.argv[1] + request = json.loads(json_req, object_hook=copland.ASPRunRequest.from_json) + + # Code for specific for this ASP. + # This example computes the HASH of the file named in an argument for the ASP. + # May raise an exception, which will be captured in main. + + asp_args = request.ASP_ARGS + filename = asp_args['filepath'] + + with open(filename,"rb") as f: + bytes = f.read() + + hash_string = hashlib.sha256(bytes).hexdigest() + # evidence as bytes + hash_bytes = hash_string.encode() + + # End of code specific for this ASP. + + # Common code to bundle computed value. + # Step 1: + # The return value for an ASP, must be + # encoded in BASE64, and converted to ascii for JSON transmission + hash_b64 = base64.b64encode(hash_bytes).decode('ascii') + + # Step 2: + # wrap the value as Evidence + evidence = copland.RAWEV([hash_b64]) + + # Step 3: + # Construct the ASPRunResponse with this evidence. + response = copland.successfulASPRunResponse(evidence) + response_json = json.dumps(response, default=lambda o: o.__dict__) + return response_json + +# Main simply invokes the body() function above, +# and checks for exceptions. +# If it detects an exception, this ASP will return +# an ASPRunResponse with SUCCESS = false, o/w uses +# ASPRunResponse returned from body() + +if __name__ == "__main__": + try: + response_json = body() + except BaseException as e: + response = copland.failureASPRunResponse(str(e)) + response_json = json.dumps(response, default=lambda o: o.__dict__) + finally: + # The ASP output (ASPRunRequest) is written to stdout. + # The caller will capture stdout to receive the response from this ASP. + print(response_json) diff --git a/attestation_asps/python_asps/copland.py b/attestation_asps/python_asps/copland.py new file mode 100644 index 0000000..2518c29 --- /dev/null +++ b/attestation_asps/python_asps/copland.py @@ -0,0 +1,110 @@ +class ASP_PARAMS: + def __init__(self, asp_id, asp_args, plc, targ_id): + self.ASP_ID = asp_id + self.ASP_ARGS = asp_args + self.ASP_PLC = asp + self.ASP_TARG_ID = asp_targ_id + +class Attestation_Session: + def __init__(self, session_plc, plc_mapping, pubkey_mapping): + self.Session_Plc = session_plc + self.Plc_Mapping = plc_mapping + self.PubKey_Mapping = pubkey_mapping + +class ProtocolRunRequest: + def __init__(self, type, action, req_plc, term, rawev, attestation_session): + self.TYPE = type + self.ACTION = action + self.REQ_PLC = req_plc + self.TERM = term + self.RAWEV = rawev + self.ATTESTATION_SESSION = attestation_session + +class ProtocolRunResponse: + def __init__(self, type, action, success, payload): + self.TYPE = type + self.ACTION = action + self.SUCCESS = success, + self.PAYLOAD = payload + +class ProtocolAppraiseRequest: + def __init__(self, type, action, attestation_session, term, req_plc, evidence, rawev): + self.TYPE = type, + self.ACTION = action, + self.ATTESTATION_SESSION = attestation_session, + self.TERM = term, + self.REQ_PLC = req_plc, + self.EVIDENCE = evidence, + self.RAWEV: rawev + +class ProtocolAppraiseResponse: + def __init__(self, type, action, success, payload): + self.TYPE = type + self.ACTION = action + self.SUCCESS = success + self.PAYLOAD = payload + +class RAWEV: + def __init__(self, rawev): + self.RawEv = rawev + + @staticmethod + def from_json(dct): + keys = dct.keys() + if len(keys) == 1 and 'RawEv' in keys: + return RAWEV(dct['RawEv']) + else: + return dct + +class ASPRunRequest: + def __init__(self, type, action, asp_id, asp_args, asp_plc, asp_targ_id, rawev): + self.TYPE = type + self.ACTION = action + self.ASP_ID = asp_id + self.ASP_ARGS = asp_args + self.ASP_PLC = asp_plc + self.ASP_TARG_ID = asp_targ_id + self.RAWEV = rawev + + @staticmethod + def from_json(dct): + keys = dct.keys() + if 'TYPE' in keys and 'ACTION' in keys and 'ASP_ID' in keys and 'ASP_ARGS' in keys and 'ASP_PLC' in keys and 'ASP_TARG_ID' in keys and 'RAWEV' in keys: + return ASPRunRequest(dct['TYPE'], dct['ACTION'], dct['ASP_ID'], dct['ASP_ARGS'], dct['ASP_PLC'], dct['ASP_TARG_ID'], dct['RAWEV']) + elif len(keys) == 1 and 'RawEv' in keys: + return RAWEV.from_json(dct) + else: + return dct + +class ASPRunResponse: + def __init__(self, type, action, success, payload): + self.TYPE = type + self.ACTION = action + self.SUCCESS = success + self.PAYLOAD = payload + + @staticmethod + def from_json(dct): + keys = dct.keys() + if 'TYPE' in keys and 'ACTION' in keys and 'SUCCESS' in keys and 'PAYLOAD' in keys: + return ASPRunResponse(dct['TYPE'], dct['ACTION'], dct['SUCCESS'], dct['PAYLOAD']) + elif len(keys) == 1 and 'RawEv' in keys: + return RAWEV.from_json(dct) + else: + return dct + + +def failureASPRunResponse (error_msg): + empty_evidence = RAWEV([]) + response = ASPRunResponse("RESPONSE", + "ASP_RUN", + False, + empty_evidence) + return response + +def successfulASPRunResponse (evidence): + response = ASPRunResponse("RESPONSE", + "ASP_RUN", + True, + evidence) + return response diff --git a/attestation_asps/python_asps/p_hashfile_id.py b/attestation_asps/python_asps/p_hashfile_id.py new file mode 100644 index 0000000..a2611e5 --- /dev/null +++ b/attestation_asps/python_asps/p_hashfile_id.py @@ -0,0 +1,51 @@ +## TEMPLATE.txt +## General structure for ASP's written in rust + +import sys +import json +import hashlib +import binascii +import base64 + +import copland + +## ASP to compute the hash of the filenmae provided in the 'filepath' argument. + +def body(): + + # For every ASP, an ASPRunRequest appears as the single command-line argument + numargs = len(sys.argv) + if (numargs == 1): + raise Exception("no ASPRunRequest provided to p_hashfile_id") + json_req = sys.argv[1] + request = json.loads(json_req, object_hook=copland.ASPRunRequest.from_json) + + + asp_args = request.ASP_ARGS + filename = asp_args['filepath'] + + with open(filename,"rb") as f: + bytes = f.read() + + hash_string = hashlib.sha256(bytes).hexdigest() + # evidence as bytes + hash_bytes = hash_string.encode() + hash_b64 = base64.b64encode(hash_bytes).decode('ascii') + + evidence = copland.RAWEV([hash_b64]) + + response = copland.successfulASPRunResponse(evidence) + response_json = json.dumps(response, default=lambda o: o.__dict__) + return response_json + + +if __name__ == "__main__": + try: + response_json = body() + except BaseException as e: + response = copland.failureASPRunResponse(str(e)) + response_json = json.dumps(response, default=lambda o: o.__dict__) + finally: + # The ASP output (ASPRunRequest) is written to stdout. + # The caller will capture stdout to receive the response from this ASP. + print(response_json) diff --git a/attestation_asps/rust_asps/Cargo.toml b/attestation_asps/rust_asps/Cargo.toml new file mode 100644 index 0000000..7eba7ab --- /dev/null +++ b/attestation_asps/rust_asps/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "rust_asps" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "r_hashfile_id" +path = "src/bin/r_hashfile_id.rs" +test = false +bench = false + +[[bin]] +name = "r_uptime_id" +path = "src/bin/r_uptime_id.rs" +test = false +bench = false + +[dependencies] +rust_am_lib = { git = "https://github.com/ku-sldg/rust-am-lib.git", veersion = "0.1.1"} +serde_json = "1.0.125" +anyhow = "1.0.86" +base64 = "0.13" +sha2 = "0.10.8" +sysinfo = "0.31.3" diff --git a/attestation_asps/rust_asps/Makefile b/attestation_asps/rust_asps/Makefile new file mode 100644 index 0000000..8f168ab --- /dev/null +++ b/attestation_asps/rust_asps/Makefile @@ -0,0 +1,15 @@ +SRCDIR := src/bin/ +BINDIR := target/debug/ +DEST_BINDIR := ../bin/ + +SOURCES := $(wildcard $(SRCDIR)*.rs) +BASE_NAMES := $(patsubst src/bin/%,%, $(patsubst %.rs,%, $(SOURCES))) + +all: + for asp in $(BASE_NAMES); do \ + cargo build --bin $$asp; \ + cp $(BINDIR)/$$asp $(DEST_BINDIR); \ + done + +clean: + cargo clean diff --git a/attestation_asps/rust_asps/src/bin/TEMPLATE.rs.txt b/attestation_asps/rust_asps/src/bin/TEMPLATE.rs.txt new file mode 100644 index 0000000..e0e6df7 --- /dev/null +++ b/attestation_asps/rust_asps/src/bin/TEMPLATE.rs.txt @@ -0,0 +1,92 @@ +// TEMPLATE.txt +// General structure for ASP's written in rust + +// Common Packages +use rust_am_lib::copland::*; +use base64; +use anyhow::{Context, Result}; +use std::env; + +// Packages required to perform specific ASP action. +// e.g. +use sha2::{Sha256, Digest}; + +// function where the work of the ASP is performed. +// May signal an error which will be handled in main. +fn body() -> Result { + + // For every ASP, an ASPRunRequest appears as the single command-line argument + let args: Vec = env::args().collect(); + + if args.len() < 2 { + return Err(anyhow::anyhow!("ASPRunRequest not supplied as command line argument")); + } + + let json_request = &args[1]; + // May fail. If so, return an Err Result + let req: ASPRunRequest = serde_json::from_str(json_request)?; + + // Code for specific for this ASP. + // This example computes the HASH of the file named in an argument for the ASP. + // May return an Err Result, which will be captured in main. + let args_map = req.ASP_ARGS; + let filename = &args_map.get("filepath").context("filename argument not provided to ASP, hashfile_id")?; + + /* Code sample for accessing input evidence within the ASP. + + // Suppose the filename is to be extracted from evidence, instead of an ASP argument + + let evidence_in = match req.RAWEV {RawEv::RawEv(x) => x,}; + + let latest_evidence = &evidence_in[evidence_in.len() - 1]; + // Evidence is always base64 encoded, so decode this + let filename_bytes = base64::decode(latest_evidence)?; + let filename = String::from_utf8(filename_bytes)?; + */ + + let bytes = std::fs::read(filename)?; // Vec + + let hash = Sha256::digest(&bytes); + + // End of code specific for this ASP. + + // Common code to bundle computed value. + // Step 1: + // The return value for an ASP, must be + // encoded in BASE64, and converted to ascii for JSON transmission + let hash_b64: String = base64::encode(&hash); + + // Step 2: + // wrap the value as Evidence + let evidence = RawEv::RawEv(vec![hash_b64]); + + // Step 3: + // Construct the ASPRunResponse with this evidence. + let response = successfulASPRunResponse (evidence); + let response_json = serde_json::to_string(&response)?; + Ok (response_json) +} + +// Main simply invokes the body() funciton above, +// and checks for Err Result. +// If it detects an Err Result, this ASP will return +// an ASPRunResponse with SUCCESS = false, o/w uses +// ASPRunResponse returned from body() + +fn main() -> () { + + let response_json = match body() { + Ok(resp) => resp, + Err(_error) => { + let response = failureASPRunResponse (_error.to_string()); + // If an error occurs converting the failure response to JSON + // there is nothing else to do but panic. + // This should never happen. + serde_json::to_string(&response).unwrap_or_else(|error| {panic!("Failed to json.encode failure response: {error:?}");}) + } + }; + // The ASP output (ASPRunRequest) is written to stdout. + // The caller will capture stdout to receive the response from this ASP. + println!("{response_json}"); + () +} diff --git a/attestation_asps/rust_asps/src/bin/r_hashfile_id.rs b/attestation_asps/rust_asps/src/bin/r_hashfile_id.rs new file mode 100644 index 0000000..67cfa72 --- /dev/null +++ b/attestation_asps/rust_asps/src/bin/r_hashfile_id.rs @@ -0,0 +1,78 @@ +// TEMPLATE.txt +// General structure for ASP's written in rust + +// Common Packages +use rust_am_lib::copland::*; +use anyhow::{Context, Result}; +use std::env; + +// Packages required to perform specific ASP action. +// e.g. +use sha2::{Sha256, Digest}; + +// function where the work of the ASP is performed. +// May signal an error which will be handled in main. +fn body() -> Result { + + // For every ASP, an ASPRunRequest appears as the single command-line argument + let args: Vec = env::args().collect(); + + if args.len() < 2 { + return Err(anyhow::anyhow!("ASPRunRequest not supplied as command line argument")); + } + + let json_request = &args[1]; + // May fail. If so, return an Err Result + let req: ASPRunRequest = serde_json::from_str(json_request)?; + + // Code for specific for this ASP. + // This example computes the HASH of the file named in an argument for the ASP. + // May return an Err Result, which will be captured in main. + let args_map = req.ASP_ARGS; + let filename = &args_map.get("filepath").context("filename argument not provided to ASP, hashfile_id")?; + + let bytes = std::fs::read(filename)?; // Vec + + let hash = Sha256::digest(&bytes); + + // End of code specific for this ASP. + + // Common code to bundle computed value. + // Step 1: + // The return value for an ASP, must be + // encoded in BASE64, and converted to ascii for JSON transmission + let hash_b64: String = base64::encode(hash); + + // Step 2: + // wrap the value as Evidence + let evidence = RawEv::RawEv(vec![hash_b64]); + + // Step 3: + // Construct the ASPRunResponse with this evidence. + let response = successfulASPRunResponse (evidence); + let response_json = serde_json::to_string(&response)?; + Ok (response_json) +} + +// Main simply invokes the body() function above, +// and checks for Err Result. +// If it detects an Err Result, this ASP will return +// an ASPRunResponse with SUCCESS = false, o/w uses +// ASPRunResponse returned from body() + +fn main() { + + let response_json = match body() { + Ok(resp) => resp, + Err(_error) => { + let response = failureASPRunResponse (_error.to_string()); + // If an error occurs converting the failure response to JSON + // there is nothing else to do but panic. + // This should never happen. + serde_json::to_string(&response).unwrap_or_else(|error| {panic!("Failed to json.encode failure response: {error:?}");}) + } + }; + // The ASP output (ASPRunRequest) is written to stdout. + // The caller will capture stdout to receive the response from this ASP. + println!("{response_json}"); +} diff --git a/attestation_asps/rust_asps/src/bin/r_readfile_id.rs b/attestation_asps/rust_asps/src/bin/r_readfile_id.rs new file mode 100644 index 0000000..5afb663 --- /dev/null +++ b/attestation_asps/rust_asps/src/bin/r_readfile_id.rs @@ -0,0 +1,67 @@ +// Common Packages +use rust_am_lib::copland::*; +use anyhow::{Context, Result}; +use std::env; + +// function where the work of the ASP is performed. +// May signal an error which will be handled in main. +fn body() -> Result { + + // For every ASP, an ASPRunRequest appears as the single command-line argument + let args: Vec = env::args().collect(); + + if args.len() < 2 { + return Err(anyhow::anyhow!("ASPRunRequest not supplied as command line argument")); + } + + let json_request = &args[1]; + // May fail. If so, return an Err Result + let req: ASPRunRequest = serde_json::from_str(json_request)?; + + // Code for specific for this ASP. + // This example computes the HASH of the file named in an argument for the ASP. + // May return an Err Result, which will be captured in main. + let args_map = req.ASP_ARGS; + let filename = &args_map.get("filepath").context("filename argument not provided to ASP, hashfile_id")?; + + let bytes = std::fs::read(filename)?; // Vec + + // Common code to bundle computed value. + // Step 1: + // The return value for an ASP, must be + // encoded in BASE64, and converted to ascii for JSON transmission + let hash_b64: String = base64::encode(bytes); + + // Step 2: + // wrap the value as Evidence + let evidence = RawEv::RawEv(vec![hash_b64]); + + // Step 3: + // Construct the ASPRunResponse with this evidence. + let response = successfulASPRunResponse (evidence); + let response_json = serde_json::to_string(&response)?; + Ok (response_json) +} + +// Main simply invokes the body() function above, +// and checks for Err Result. +// If it detects an Err Result, this ASP will return +// an ASPRunResponse with SUCCESS = false, o/w uses +// ASPRunResponse returned from body() + +fn main() { + + let response_json = match body() { + Ok(resp) => resp, + Err(_error) => { + let response = failureASPRunResponse (_error.to_string()); + // If an error occurs converting the failure response to JSON + // there is nothing else to do but panic. + // This should never happen. + serde_json::to_string(&response).unwrap_or_else(|error| {panic!("Failed to json.encode failure response: {error:?}");}) + } + }; + // The ASP output (ASPRunRequest) is written to stdout. + // The caller will capture stdout to receive the response from this ASP. + println!("{response_json}"); +} diff --git a/attestation_asps/rust_asps/src/bin/r_uptime_id.rs b/attestation_asps/rust_asps/src/bin/r_uptime_id.rs new file mode 100644 index 0000000..5210afa --- /dev/null +++ b/attestation_asps/rust_asps/src/bin/r_uptime_id.rs @@ -0,0 +1,56 @@ +// Very simple use of the sysinfo crate. +// Returns seconds since most recent book. + +// The sysinfo crate provides access to a wide range of system information, +// including a variety of dynamic characteristics. + + +use rust_am_lib::copland::*; +use anyhow::{Result}; +use std::env; + +use sysinfo::System; + +fn body() -> Result { + + let args: Vec = env::args().collect(); + + if args.len() < 2 { + return Err(anyhow::anyhow!("ASPRunRequest not supplied as command line argument")); + } + let json_request = &args[1]; + + // decoding the ASPRunRequest just to confirm + // that we are being called as expected, even though + // no information in the request is required for execution. + let _req: ASPRunRequest = serde_json::from_str(json_request)?; + + // This ASP has no arguments. + // let args_map = req.ASP_ARGS; + + // returns seconds since last boot. + let up = System::uptime(); + let up_b64: String = base64::encode(up.to_be_bytes()); + + let evidence = RawEv::RawEv(vec![up_b64]); + + let response = successfulASPRunResponse (evidence); + + let response_json = serde_json::to_string(&response)?; + Ok (response_json) +} + +fn main() { + + let response_json = match body() { + Ok(resp) => resp, + Err(_error) => { + let response = failureASPRunResponse (_error.to_string()); + serde_json::to_string(&response).unwrap_or_else(|error| {panic!("Failed to json.encode failure response: {error:?}");}) + } + }; + + // The ASP output (ASPRunRequest) is written to stdout. + // The caller will capture stdout to receive the response from this ASP. + println!("{response_json}"); +}