From e90c5da6402dbdd406014e901c4bbb293d29e399 Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Fri, 25 Oct 2024 10:06:36 -0700 Subject: [PATCH 1/7] Sync main branch with cc authorizers --- .../confidential_computing/aci_authorizer.py | 72 ++++++++ .../confidential_computing/coco_authorizer.py | 72 ++++++++ .../dummy_authorizer.py | 46 +++++ .../confidential_computing/gpu_authorizer.py | 161 ++++++++++-------- .../confidential_computing/maa_authorizer.py | 63 +++++++ .../confidential_computing/snp_authorizer.py | 90 ++++++++++ 6 files changed, 433 insertions(+), 71 deletions(-) create mode 100644 nvflare/app_opt/confidential_computing/aci_authorizer.py create mode 100644 nvflare/app_opt/confidential_computing/coco_authorizer.py create mode 100644 nvflare/app_opt/confidential_computing/dummy_authorizer.py create mode 100644 nvflare/app_opt/confidential_computing/maa_authorizer.py create mode 100644 nvflare/app_opt/confidential_computing/snp_authorizer.py diff --git a/nvflare/app_opt/confidential_computing/aci_authorizer.py b/nvflare/app_opt/confidential_computing/aci_authorizer.py new file mode 100644 index 0000000000..1223b02260 --- /dev/null +++ b/nvflare/app_opt/confidential_computing/aci_authorizer.py @@ -0,0 +1,72 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# 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. +import json +import requests +import time +import jwt +from jwt import PyJWKClient + +from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer + +ACI_NAMESPACE = "x-ms" +maa_endpoint = 'sharedeus2.eus2.attest.azure.net' + +class ACIAuthorizer(CCAuthorizer): + def generate(self): + count = 0 + token = "" + while True: + count = count + 1 + try: + r = requests.post('http://localhost:8284/attest/maa', data=json.dumps({"maa_endpoint": maa_endpoint, "runtime_data": "ewp9"}), headers={"Content-Type" : "application/json"}) + if r.status_code == requests.codes.ok: + token = r.json().get("token") + break + except: + if count > 5: + break + time.sleep(2) + return token + + def verify(self, token): + try: + header = jwt.get_unverified_header(token) + # print(f"{header=}") + alg = header.get('alg') + jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") + signing_key = jwks_client.get_signing_key_from_jwt(token) + claims = jwt.decode(token, signing_key.key, algorithms=[alg]) + if claims: + # print(f"{claims=}") + return True + except: + return False + return False + + def can_generate(self) -> bool: + return True + + def can_verify(self) -> bool: + return True + + def get_namespace(self) -> str: + return ACI_NAMESPACE + +if __name__ == "__main__": + m = ACIAuthorizer() + token = m.generate() + print(type(token)) + v = m.verify(token) + print(v) + diff --git a/nvflare/app_opt/confidential_computing/coco_authorizer.py b/nvflare/app_opt/confidential_computing/coco_authorizer.py new file mode 100644 index 0000000000..b5aec2b790 --- /dev/null +++ b/nvflare/app_opt/confidential_computing/coco_authorizer.py @@ -0,0 +1,72 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# 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. +import json +import requests +import time +import jwt +from jwt import PyJWKClient + +from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer + +COCO_NAMESPACE = "x-ms" +maa_endpoint = 'sharedeus2.eus2.attest.azure.net' + +class CoCoAuthorizer(CCAuthorizer): + def generate(self): + count = 0 + token = "" + while True: + count = count + 1 + try: + r = requests.post('http://localhost:8284/attest/maa', data=json.dumps({"maa_endpoint": maa_endpoint, "runtime_data": "ewp9"}), headers={"Content-Type" : "application/json"}) + if r.status_code == requests.codes.ok: + token = r.json().get("token") + break + except: + if count > 5: + break + time.sleep(2) + return token + + def verify(self, token): + try: + header = jwt.get_unverified_header(token) + # print(f"{header=}") + alg = header.get('alg') + jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") + signing_key = jwks_client.get_signing_key_from_jwt(token) + claims = jwt.decode(token, signing_key.key, algorithms=[alg]) + if claims: + # print(f"{claims=}") + return True + except: + return False + return False + + def can_generate(self) -> bool: + return True + + def can_verify(self) -> bool: + return True + + def get_namespace(self) -> str: + return COCO_NAMESPACE + +if __name__ == "__main__": + m = CoCoAuthorizer() + token = m.generate() + print(type(token)) + v = m.verify(token) + print(v) + diff --git a/nvflare/app_opt/confidential_computing/dummy_authorizer.py b/nvflare/app_opt/confidential_computing/dummy_authorizer.py new file mode 100644 index 0000000000..219ca8e683 --- /dev/null +++ b/nvflare/app_opt/confidential_computing/dummy_authorizer.py @@ -0,0 +1,46 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# 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. + +import jwt +from jwt import PyJWKClient +import subprocess + +from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer + +MAA_NAMESPACE = "x-ms" +maa_endpoint = 'sharedeus2.eus2.attest.azure.net' + +class DummyAuthorizer(CCAuthorizer): + def generate(self): + return "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vc2hhcmVkZXVzMi5ldXMyLmF0dGVzdC5henVyZS5uZXQvY2VydHMiLCJraWQiOiJKMHBBUGRmWFhIcVdXaW1nckg4NTN3TUlkaDUvZkxlMXo2dVNYWVBYQ2EwPSIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDk5NDM0OTYsImlhdCI6MTcwOTkxNDY5NiwiaXNzIjoiaHR0cHM6Ly9zaGFyZWRldXMyLmV1czIuYXR0ZXN0LmF6dXJlLm5ldCIsImp0aSI6IjQ1MGIxNWMxMmRmYzIxMWM5ZWRkOGU4MmFiY2NiZTEzYmMyOTgzZjlhNjAzOTZlMzljZTJmZGIwYjFmNTg1YzEiLCJuYmYiOjE3MDk5MTQ2OTYsInNlY3VyZWJvb3QiOnRydWUsIngtbXMtYXR0ZXN0YXRpb24tdHlwZSI6ImF6dXJldm0iLCJ4LW1zLWF6dXJldm0tYXR0ZXN0YXRpb24tcHJvdG9jb2wtdmVyIjoiMi4wIiwieC1tcy1henVyZXZtLWF0dGVzdGVkLXBjcnMiOlswLDEsMiwzLDQsNSw2LDddLCJ4LW1zLWF6dXJldm0tYm9vdGRlYnVnLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0tZGJ2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1kYnh2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1kZWJ1Z2dlcnNkaXNhYmxlZCI6dHJ1ZSwiwC1tcy1henVyZXZtLWRlZmF1bHQtc2VjdXJlYm9vdGtleXN2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1lbGFtLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0tZmxpZ2h0c2lnbmluZy1lbmFibGVkIjpmYWxzZSwieC1tcy1henVyZXZtLWh2Y2ktcG9saWN5IjowLCJ4LW1zLWF6dXJldm0taHlwZXJ2aXNvcmRlYnVnLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0taXMtd2luZG93cyI6ZmFsc2UsIngtbXMtYXp1cmV2bS1rZXJuZWxkZWJ1Zy1lbmFibGVkIjpmYWxzZSwieC1tcy1henVyZXZtLW9zYnVpbGQiOiJOb3RBcHBsaWNhdGlvbiIsIngtbXMtYXp1cmV2bS1vc2Rpc3RybyI6IlVidW50dSIsIngtbXMtYXp1cmV2bS1vc3R5cGUiOiJMaW51eCIsIngtbXMtYXp1cmV2bS1vc3ZlcnNpb24tbWFqb3IiOjIyLCJ4LW1zLWF6dXJldm0tb3N2ZXJzaW9uLW1pbm9yIjo0LCJ4LW1zLWF6dXJldm0tc2lnbmluZ2Rpc2FibGVkIjp0cnVlLCJ4LW1zLWF6dXJldm0tdGVzdHNpZ25pbmctZW5hYmxlZCI6ZmFsc2UsIngtbXMtYXp1cmV2bS12bWlkIjoiQzRGRkM0QjMtOUFERi00MEQxLThDQ0MtMTAxMUUxQkNDREIwIiwieC1tcy1pc29sYXRpb24tdGVlIjp7IngtbXMtYXR0ZXN0YXRpb24tdHlwZSI6InNldnNucHZtIiwieC1tcy1jb21wbGlhbmNlLXN0YXR1cyI6ImF6dXJlLWNvbXBsaWFudC1jdm0iLCJ4LW1zLXJ1bnRpbWUiOnsia2V5cyI6W3siZSI6IkFRQUIiLCJrZXlfb3BzIjpbInNpZ24iXSwia2lkIjoiSENMQWtQdWIiLCJrdHkiOiJSU0EiLCJuIjoiNEFQakF3QUFwQjE4cnc0bDh4Y0pmQXNpT1pJb1lSdGpYLVdOM0RhdVZ2cWlOSGlNU2RFaFNtWW9CQnVTcUVfa2pHblpZOFRWb2RSRkdJNWtFalR4NmhBZFM1OHIzY2R6OEtYMERmOHZERjF3Y2NjVW52SHJGY3FnRnNGVWs0UHJZZko2eU9nell2bmhvdWFSQlZ4dmZ5bEMwZWZhTUNUUkdES2pZSzhPVV9RcWxFeGIzY19neEJZWGlSZ3dBYWFaZUd4eFNId3U0a3lwZ3hwMlhjWXlHLVU3a3FHc01VWnlkZmM0eUxiS1BQcl9zMUJYUTNSbkFtdTQtblhkdVRmcWlWX2gxbGN4V29fYVhSWkFNdG9hTnVkclkyMVZVV3AzVW5xeFFtdGVGcXplWWpEQm8ta2Fjel9iNG9Gem5OM29aSlVUZmJnb09sTzJzbDc3U05Xa0x3In0seyJlIjoiQVFBQiIsImtleV9vcHMiOlsiZW5jcnlwdCJdLCJraWQiOiJIQ0xFa1B1YiIsImt0eSI6IlJTQSIsIm4iOiJ3c3lxREFBQloySHlTd08wTDFaWXZwVkhOVFVpdXpPaGcyb3Bfa1VpckNqM1M5bEtpT051YmU4RWFsSFVBcUh6bGdEcF84dHdBdGRONGNOUzZUSWdITG94TXpaZUFpaWU1OGd2VGtYdzVqMThmVUY4UEVvT2NXVERFSmRMVXIxWnBEdTRSdUZXbDdkZHNIdFBJRDVqcmt0R21FajBCZVp5NzZWVGFUYU1iamhGRmphUGNCT01fOWNaVHJYdFduSG80WTF1TG53VUJPRzA3T1hrUmlTSjBHZ3phVFhvaFVzVGE4X0w5NDJfeml5QU16STliUnJmUV9JMXY0SFV0M1YzS3laaUZJS3B4X2hWQnZZZUhwcTZBbXlYRTExS0VXek5HMTJaZVNkbzBzMUhudFNidTQ3dUktYklIdnBLaVVQSzBYSGZWbDQwVnNwenJ2MjlqekppR1EifV0sInVzZXItZGF0YSI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwidm0tY29uZmlndXJhdGlvbiI6eyJjb25zb2xlLWVuYWJsZWQiOnRydWUsInJvb3QtY2VydC10aHVtYnByaW50IjoiNm5aWm5ZYUpjNEtxVVpfeXZBLW11Y0ZkWU5vdXZsUG5JVG5OTVhzSGwtMCIsInNlY3VyZS1ib290Ijp0cnVlLCJ0cG0tZW5hYmxlZCI6dHJ1ZSwidHBtLXBlcnNpc3RlZCI6dHJ1ZSwidm1VbmlxdWVJZCI6IkM0RkZDNEIzLTlBREYtNDBEMS04Q0NDLTEwMTFFMUJDQ0RCMCJ9fSwieC1tcy1zZXZzbnB2bS1hdXRob3JrZXlkaWdlc3QiOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWJvb3Rsb2FkZXItc3ZuIjo3LCJ4LW1zLXNldnNucHZtLWZhbWlseUlkIjoiMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWd1ZXN0c3ZuIjo2NTU0MSwieC1tcy1zZXZzbnB2bS1ob3N0ZGF0YSI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWlka2V5ZGlnZXN0IjoiMDM1NjIxNTg4MmE4MjUyNzlhODViMzAwYjBiNzQyOTMxZDExM2JmN2UzMmRkZTJlNTBmZmRlN2VjNzQzY2E0OTFlY2RkN2YzMzZkYzI4YTZlMGIyYmI1N2FmN2E0NGEzIiwieC1tcy1zZXZzbnB2bS1pbWFnZUlkIjoiMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWlzLWRlYnVnZ2FibGUiOmZhbHNlLCJ4LW1zLXNldnNucHZtLWxhdW5jaG1lYXN1cmVtZW50IjoiN2M0MjA4NjE0ZDMyNzYzMDI4M2VkOGFhNjUyOTcxZjNkYzI0YzU0NmY2ZWUxMzBkMzJlNGUzYjg0ZjFhYTFmNWVmMmQyMTAxMmQwZmRlMDU2ZDhmOTAwYzM5MmM3NzJjIiwieC1tcy1zZXZzbnB2bS1taWNyb2NvZGUtc3ZuIjo2MiwieC1tcy1zZXZzbnB2bS1taWdyYXRpb24tYWxsb3dlZCI6ZmFsc2UsIngtbXMtc2V2c25wdm0tcmVwb3J0ZGF0YSI6IjU0ODE0ZTlhNjQ0N2JjMWM5MGE5YTExNmYxYjRjYjdlMDU5ZTYzMzQzNDgwY2Q4N2FmMjcxZjc5MjdjOThlMTMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwieC1tcy1zZXZzbnB2bS1yZXBvcnRpZCI6ImRhNjU0YWY3NGZiODIzZjJiM2E1ODMzMzVmOGYzYmUzMmY2ODkwYTdkYjIzMzUyM2RkOWUwNTRmNDQzMDU2OWYiLCJ4LW1zLXNldnNucHZtLXNtdC1hbGxvd2VkIjp0cnVlLCJ4LW1zLXNldnNucHZtLXNucGZ3LXN2biI6MTUsIngtbXMtc2V2c25wdm0tdGVlLXN2biI6MCwieC1tcy1zZXZzbnB2bS12bXBsIjowfSwieC1tcy1wb2xpY3ktaGFzaCI6IndtOW1IbHZUVTgyZThVcW9PeTFZajFGQlJTTmtmZTk5LTY5SVlEcTllV3MiLCJ4LW1zLXJ1bnRpbWUiOnsiY2xpZW50LXBheWxvYWQiOnsibm9uY2UiOiIifSwia2V5cyI6W3siZSI6IkFRQUIiLCJrZXlfb3BzIjpbImVuY3J5cHQiXSwia2lkIjoiVHBtRXBoZW1lcmFsRW5jcnlwdGlvbktleSIsImt0eSI6IlJTQSIsIm4iOiJ6eHN2bWdBQV9rRlBKMjZzYnRfdFhVUDhqcHowMk50YnhRU2hPd0lxa1h6U1hxUGJmV1Vxb1hpUm9idzJqTC01ZTNiQU1LU3J3cmpMU09DcnNtbjdPMnZxVmNBMW9Ucl9ObFE3NEpMMnlBanZVWGFId0dBZVVzbkNfWm51UXltdzNFMXJ4NnNCUUxZWFcwMTRiQjNKX01feFBDaGZfQk9NTGdNTlRzbTNwbGh0eTVNRWJ5OWJBSXp6a2ZPNjZhblhWUWxUVE1xQk43SkJVeU5QSFBWQU82V3N6bzl6YnI1aWhlUUxOZDZLZXNEMHU1VXBYaVo4UU5zUng2cXJCUS12TkVsVXQ2cXRvbC1xVzh4TkdvckxBWlhhZ0FyRVpGTE9aOEZWa0hQWGlMM0wwZ253eDBvb0l5UG5pbk5WY2dLanFYR3pOdnB1ejJGTmNuQU9uS2pKZFEifV19LCJ4LW1zLXZlciI6IjEuMCJ9.COvydLJUjR5voFyG-AUJlEp9fyJNBtbptkgq9p-KNkMlFCorY87VZPDrmITH4gYM5YpYuDk370P81hvd2Pw9COZB1-t9VSaWMJzcyL-T43Sh8nSGNO13kOqDQiHss1907kBiFy2jWngaoxuJvO4BSNFkxL9bsCsEZVSpMDmO9zZkp1Ja7sp-Cptm9rwf5JTfKuWZ4cazn2hUkWbQHBg51b8AeryNzU-35oEhGCIPYqryXv5SY32PB9s-lwh6l7K3t768P817XKF3Szip0TZpgIMoM0GU4oNOnjnFZ3u8DnvuyEim-pCZgP7qpQmJI4lrgI6Sn-jxqTg8q0FUmAkcnQ" + + def verify(self, token): + return True + + def can_generate(self) -> bool: + return True + + def can_verify(self) -> bool: + return True + + def get_namespace(self) -> str: + return MAA_NAMESPACE + + +if __name__ == "__main__": + m = MAAAuthorizer() + token = m.generate() + print(type(token)) + v = m.verify(token) + print(v) diff --git a/nvflare/app_opt/confidential_computing/gpu_authorizer.py b/nvflare/app_opt/confidential_computing/gpu_authorizer.py index bd55e2a463..d88cf27a96 100644 --- a/nvflare/app_opt/confidential_computing/gpu_authorizer.py +++ b/nvflare/app_opt/confidential_computing/gpu_authorizer.py @@ -14,80 +14,99 @@ from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer - -GPU_NAMESPACE = "x-nv-gpu-" - - +import json +import jwt + +from nv_attestation_sdk import attestation +import os + +GPU_NAMESPACE = "x-nv-gpu" +policy = """{ + "version":"1.0", + "authorization-rules":{ + "sub":"NVIDIA-GPU-ATTESTATION", + "secboot":true, + "x-nvidia-gpu-manufacturer":"NVIDIA Corporation", + "x-nvidia-attestation-type":"GPU", + "x-nvidia-attestation-detailed-result":{ + "x-nvidia-gpu-driver-rim-schema-validated":true, + "x-nvidia-gpu-vbios-rim-cert-validated":true, + "x-nvidia-gpu-attestation-report-cert-chain-validated":true, + "x-nvidia-gpu-driver-rim-schema-fetched":true, + "x-nvidia-gpu-attestation-report-parsed":true, + "x-nvidia-gpu-nonce-match":true, + "x-nvidia-gpu-vbios-rim-signature-verified":true, + "x-nvidia-gpu-driver-rim-signature-verified":true, + "x-nvidia-gpu-arch-check":true, + "x-nvidia-gpu-measurements-match":true, + "x-nvidia-gpu-attestation-report-signature-verified":true, + "x-nvidia-gpu-vbios-rim-schema-validated":true, + "x-nvidia-gpu-driver-rim-cert-validated":true, + "x-nvidia-gpu-vbios-rim-schema-fetched":true, + "x-nvidia-gpu-vbios-rim-measurements-available":true + }, + "x-nvidia-gpu-driver-version":"535.104.05", + "hwmodel":"GH100 A01 GSP BROM", + "measres":"comparison-successful", + "x-nvidia-gpu-vbios-version":"96.00.5E.00.02" + } +} +""" class GPUAuthorizer(CCAuthorizer): - """Note: This is just a fake implementation for GPU authorizer. It will be replaced later - with the real implementation. - - """ - - def __init__(self, verifiers: list) -> None: - """ - - Args: - verifiers (list): - each element in this list is a dictionary and the keys of dictionary are - "devices", "env", "url", "appraisal_policy_file" and "result_policy_file." - - the values of devices are "gpu" and "cpu" - the values of env are "local" and "test" - currently, valid combination is gpu + local - - url must be an empty string - appraisal_policy_file must point to an existing file - currently supports an empty file only - - result_policy_file must point to an existing file - currently supports the following content only - - .. code-block:: json - - { - "version":"1.0", - "authorization-rules":{ - "x-nv-gpu-available":true, - "x-nv-gpu-attestation-report-available":true, - "x-nv-gpu-info-fetched":true, - "x-nv-gpu-arch-check":true, - "x-nv-gpu-root-cert-available":true, - "x-nv-gpu-cert-chain-verified":true, - "x-nv-gpu-ocsp-cert-chain-verified":true, - "x-nv-gpu-ocsp-signature-verified":true, - "x-nv-gpu-cert-ocsp-nonce-match":true, - "x-nv-gpu-cert-check-complete":true, - "x-nv-gpu-measurement-available":true, - "x-nv-gpu-attestation-report-parsed":true, - "x-nv-gpu-nonce-match":true, - "x-nv-gpu-attestation-report-driver-version-match":true, - "x-nv-gpu-attestation-report-vbios-version-match":true, - "x-nv-gpu-attestation-report-verified":true, - "x-nv-gpu-driver-rim-schema-fetched":true, - "x-nv-gpu-driver-rim-schema-validated":true, - "x-nv-gpu-driver-rim-cert-extracted":true, - "x-nv-gpu-driver-rim-signature-verified":true, - "x-nv-gpu-driver-rim-driver-measurements-available":true, - "x-nv-gpu-driver-vbios-rim-fetched":true, - "x-nv-gpu-vbios-rim-schema-validated":true, - "x-nv-gpu-vbios-rim-cert-extracted":true, - "x-nv-gpu-vbios-rim-signature-verified":true, - "x-nv-gpu-vbios-rim-driver-measurements-available":true, - "x-nv-gpu-vbios-index-conflict":true, - "x-nv-gpu-measurements-match":true - } - } - - """ - super().__init__() - self.verifiers = verifiers + def __init__(self, verifier_url="https://nras.attestation.nvidia.com/v1/attest/gpu"): + self._can_generate = True + self.client = attestation.Attestation() + self.client.set_name("thisNode1") + self.client.set_nonce("931d8dd0add203ac3d8b4fbde75e115278eefcdceac5b87671a748f32364dfcb") + self.client.add_verifier(attestation.Devices.GPU, attestation.Environment.REMOTE, verifier_url, "") + self.remote_att_result_policy = policy + + def generate(self): + try: + self.client.attest() + token = self.client.get_token() + except BaseException: + self.can_generate = False + token = "[[],{}]" + return token + + def verify(self, eat_token): + try: + # header = jwt.get_unverified_header(jwt_token[1]) + # # url = header.get("jku") + # alg = header.get('alg') + # jwks_client = PyJWKClient(self.verifier_url) + # signing_key = jwks_client.get_signing_key_from_jwt(jwt_token) + jwt_token = json.loads(eat_token)[1] + # pprint.pprint(f"{jwt_token=}") + claims = jwt.decode(jwt_token.get("REMOTE_GPU_CLAIMS"), options={"verify_signature": False}) + # pprint.pprint(f"{claims=}") + nonce = claims.get('eat_nonce') + self.client.set_name("nvflare_node1") + # self.client.set_nonce("931d8dd0add203ac3d8b4fbde75e115278eefcdceac5b87671a748f32364dfcb") + self.client.set_nonce(nonce) + self.client.set_token(name='nvflare_node1', eat_token=eat_token) + result = self.client.validate_token(self.remote_att_result_policy) + except BaseException as e: + print("Exception {e=}") + result = False + return result + + def can_generate(self) -> bool: + return True + + def can_verify(self) -> bool: + return True def get_namespace(self) -> str: return GPU_NAMESPACE - def generate(self) -> str: - raise NotImplementedError +if __name__=="__main__": + gpu_ath = GPUAuthorizer(verifier_url="https://nras.attestation.nvidia.com/v1/attest/gpu") + # gpu_tp.generate() + test_token = gpu_ath.generate() + # print(test_token) + # test_token='[["JWT", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJOVi1BdHRlc3RhdGlvbi1TREsiLCJpYXQiOjE3MDk3NTA0NTQsImV4cCI6bnVsbH0.WI8rPoyzzDCXSOYN6yGC6kEYpqXif-SagpLli7-KeTM"], {"REMOTE_GPU_CLAIMS": "eyJraWQiOiJudi1hdHRlc3RhdGlvbi1zaWduLWtpZC1wcm9kLTIwMjQwMzA1MTE1MjIyIiwiYWxnIjoiRVMzODQifQ.eyJzdWIiOiJOVklESUEtR1BVLUFUVEVTVEFUSU9OIiwic2VjYm9vdCI6dHJ1ZSwieC1udmlkaWEtZ3B1LW1hbnVmYWN0dXJlciI6Ik5WSURJQSBDb3Jwb3JhdGlvbiIsIngtbnZpZGlhLWF0dGVzdGF0aW9uLXR5cGUiOiJHUFUiLCJpc3MiOiJodHRwczpcL1wvbnJhcy5hdHRlc3RhdGlvbi5udmlkaWEuY29tIiwiZWF0X25vbmNlIjoiOTMxRDhERDBBREQyMDNBQzNEOEI0RkJERTc1RTExNTI3OEVFRkNEQ0VBQzVCODc2NzFBNzQ4RjMyMzY0REZDQiIsIngtbnZpZGlhLWF0dGVzdGF0aW9uLWRldGFpbGVkLXJlc3VsdCI6eyJ4LW52aWRpYS1ncHUtZHJpdmVyLXJpbS1zY2hlbWEtdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLWNlcnQtdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtYXR0ZXN0YXRpb24tcmVwb3J0LWNlcnQtY2hhaW4tdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtZHJpdmVyLXJpbS1zY2hlbWEtZmV0Y2hlZCI6dHJ1ZSwieC1udmlkaWEtZ3B1LWF0dGVzdGF0aW9uLXJlcG9ydC1wYXJzZWQiOnRydWUsIngtbnZpZGlhLWdwdS1ub25jZS1tYXRjaCI6dHJ1ZSwieC1udmlkaWEtZ3B1LXZiaW9zLXJpbS1zaWduYXR1cmUtdmVyaWZpZWQiOnRydWUsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLXNpZ25hdHVyZS12ZXJpZmllZCI6dHJ1ZSwieC1udmlkaWEtZ3B1LWFyY2gtY2hlY2siOnRydWUsIngtbnZpZGlhLWF0dGVzdGF0aW9uLXdhcm5pbmciOm51bGwsIngtbnZpZGlhLWdwdS1tZWFzdXJlbWVudHMtbWF0Y2giOnRydWUsIngtbnZpZGlhLWdwdS1hdHRlc3RhdGlvbi1yZXBvcnQtc2lnbmF0dXJlLXZlcmlmaWVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLXNjaGVtYS12YWxpZGF0ZWQiOnRydWUsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLWNlcnQtdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLXNjaGVtYS1mZXRjaGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLW1lYXN1cmVtZW50cy1hdmFpbGFibGUiOnRydWUsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLWRyaXZlci1tZWFzdXJlbWVudHMtYXZhaWxhYmxlIjp0cnVlfSwieC1udmlkaWEtdmVyIjoiMS4wIiwibmJmIjoxNzA5NzUwNDY2LCJ4LW52aWRpYS1ncHUtZHJpdmVyLXZlcnNpb24iOiI1MzUuMTI5LjAzIiwiZGJnc3RhdCI6ImRpc2FibGVkIiwiaHdtb2RlbCI6IkdIMTAwIEEwMSBHU1AgQlJPTSIsIm9lbWlkIjoiNTcwMyIsIm1lYXNyZXMiOiJjb21wYXJpc29uLXN1Y2Nlc3NmdWwiLCJleHAiOjE3MDk3NTQwNjYsImlhdCI6MTcwOTc1MDQ2NiwieC1udmlkaWEtZWF0LXZlciI6IkVBVC0yMSIsInVlaWQiOiI1MzQyNDkwNzAzMzcyNjAwNjk3MjE4NzI1ODI2NDM4NzA4NDYzNjE3MzI2MDk5MjMiLCJ4LW52aWRpYS1ncHUtdmJpb3MtdmVyc2lvbiI6Ijk2LjAwLjc0LjAwLjExIiwianRpIjoiOWU2NzU3MmYtYThmNy00YWY3LWFhYzctNzNiOWEzZGU0NzIwIn0.Te2hD9zdKCg5c58kmKbEajsB83o7hQ4hIt4AsGJ5GNc2ibhUiooLwtlPy1gie3eJKTfbmiBmP8t9fMA2h0kOodu2uRUjWOkKwtKoAI9esHuSxz1_avu65hsj-njKzgBW"}]' + result = gpu_ath.verify(test_token) + print(f"{result=}") - def verify(self, token: str) -> bool: - raise NotImplementedError diff --git a/nvflare/app_opt/confidential_computing/maa_authorizer.py b/nvflare/app_opt/confidential_computing/maa_authorizer.py new file mode 100644 index 0000000000..0263c59c37 --- /dev/null +++ b/nvflare/app_opt/confidential_computing/maa_authorizer.py @@ -0,0 +1,63 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# 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. + +import jwt +from jwt import PyJWKClient +import subprocess + +from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer + +MAA_NAMESPACE = "x-ms" +maa_endpoint = 'sharedeus2.eus2.attest.azure.net' + +class MAAAuthorizer(CCAuthorizer): + def generate(self): + cmd = ['sudo', 'AttestationClient', '-o', 'token'] + cp = subprocess.run(cmd, capture_output=True) + # print(f"{cp.stdout=}\n{cp.stderr=}") + # print(token) + token = cp.stdout + return cp.stdout + + def verify(self, token): + try: + header = jwt.get_unverified_header(token) + # print(f"{header=}") + alg = header.get('alg') + jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") + signing_key = jwks_client.get_signing_key_from_jwt(token) + claims = jwt.decode(token, signing_key.key, algorithms=[alg]) + if claims: + # print(f"{claims=}") + return True + except: + return False + return True + + def can_generate(self) -> bool: + return True + + def can_verify(self) -> bool: + return True + + def get_namespace(self) -> str: + return MAA_NAMESPACE + + +if __name__ == "__main__": + m = MAAAuthorizer() + token = m.generate() + print(type(token)) + v = m.verify(token) + print(v) diff --git a/nvflare/app_opt/confidential_computing/snp_authorizer.py b/nvflare/app_opt/confidential_computing/snp_authorizer.py new file mode 100644 index 0000000000..9bc7dedff1 --- /dev/null +++ b/nvflare/app_opt/confidential_computing/snp_authorizer.py @@ -0,0 +1,90 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# 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. + +import jwt +from jwt import PyJWKClient +import subprocess +import base64 +import traceback +import os + +from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer + +SNP_NAMESPACE = "x-snp" + +class SNPAuthorizer(CCAuthorizer): + def generate(self): + cmd = ['sudo', 'snpguest', 'report', 'report.bin', 'request.bin'] + with open('request.bin', 'wb') as request_file: + request_file.write(b'\x01'*64) + cp = subprocess.run(cmd, capture_output=True) + # print(token) + with open('report.bin', 'rb') as report_file: + token = base64.b64encode(report_file.read()) + # print(token) + return token + + def verify(self, token): + # print("Now verifying SNP") + try: + # if not os.path.exists('./cert'): + # os.mkdir('./cert') + # cmd = ['snpguest', 'fetch', 'ca', 'der', 'genoa', './cert', '--endorser', 'vcek'] + # cp = subprocess.run(cmd, capture_output=True) + # if cp.returncode != 0: + # return False + report_bin = base64.b64decode(token) + with open('rcv_report.bin', 'wb') as report_file: + report_file.write(report_bin) + # cmd = ['snpguest', 'fetch', 'vcek', 'der', 'genoa', './cert', 'rcv_report.bin'] + # cp = subprocess.run(cmd, capture_output=True) + # if cp.returncode != 0: + # return False + # cmd = ['snpguest', 'verify', 'certs', './cert'] + # cp = subprocess.run(cmd, capture_output=True) + # if cp.returncode != 0: + # return False + cmd = ['snpguest', 'verify', 'attestation', './cert', 'rcv_report.bin'] + cp = subprocess.run(cmd, capture_output=True) + if cp.returncode != 0: + return False + print(f"{cp.stdout=}\n{cp.stderr=} Now returning True") + # print(f"{claims=}") +<<<<<<< HEAD + print("Now returning True") +======= +>>>>>>> 3bd88414 (Fix logger) + return True + except: + print(traceback.format_exc()) + return False + return True + + def can_generate(self) -> bool: + return True + + def can_verify(self) -> bool: + return True + + def get_namespace(self) -> str: + return SNP_NAMESPACE + + +if __name__ == "__main__": + m = SNPAuthorizer() + token = m.generate() + print(type(token)) + v = m.verify(token) + print(v) + From 86b42e3fffc8f9d05d4337b359288dcb131e61fb Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Fri, 25 Oct 2024 11:36:09 -0700 Subject: [PATCH 2/7] Fix merge conflict --- nvflare/app_opt/confidential_computing/snp_authorizer.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nvflare/app_opt/confidential_computing/snp_authorizer.py b/nvflare/app_opt/confidential_computing/snp_authorizer.py index 9bc7dedff1..718c59740b 100644 --- a/nvflare/app_opt/confidential_computing/snp_authorizer.py +++ b/nvflare/app_opt/confidential_computing/snp_authorizer.py @@ -61,10 +61,6 @@ def verify(self, token): return False print(f"{cp.stdout=}\n{cp.stderr=} Now returning True") # print(f"{claims=}") -<<<<<<< HEAD - print("Now returning True") -======= ->>>>>>> 3bd88414 (Fix logger) return True except: print(traceback.format_exc()) From acf29a96b7a35a0bcceea79b160b23b9f9d5129c Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Wed, 30 Oct 2024 12:09:05 -0700 Subject: [PATCH 3/7] Both authorizers verified --- .../confidential_computing/aci_authorizer.py | 15 ------ .../confidential_computing/gpu_authorizer.py | 48 ++++++------------- 2 files changed, 15 insertions(+), 48 deletions(-) diff --git a/nvflare/app_opt/confidential_computing/aci_authorizer.py b/nvflare/app_opt/confidential_computing/aci_authorizer.py index 1223b02260..1a6a743f69 100644 --- a/nvflare/app_opt/confidential_computing/aci_authorizer.py +++ b/nvflare/app_opt/confidential_computing/aci_authorizer.py @@ -42,31 +42,16 @@ def generate(self): def verify(self, token): try: header = jwt.get_unverified_header(token) - # print(f"{header=}") alg = header.get('alg') jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") signing_key = jwks_client.get_signing_key_from_jwt(token) claims = jwt.decode(token, signing_key.key, algorithms=[alg]) if claims: - # print(f"{claims=}") return True except: return False return False - def can_generate(self) -> bool: - return True - - def can_verify(self) -> bool: - return True - def get_namespace(self) -> str: return ACI_NAMESPACE -if __name__ == "__main__": - m = ACIAuthorizer() - token = m.generate() - print(type(token)) - v = m.verify(token) - print(v) - diff --git a/nvflare/app_opt/confidential_computing/gpu_authorizer.py b/nvflare/app_opt/confidential_computing/gpu_authorizer.py index d88cf27a96..be2f607115 100644 --- a/nvflare/app_opt/confidential_computing/gpu_authorizer.py +++ b/nvflare/app_opt/confidential_computing/gpu_authorizer.py @@ -14,14 +14,15 @@ from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer +import logging import json import jwt +import uuid from nv_attestation_sdk import attestation -import os GPU_NAMESPACE = "x-nv-gpu" -policy = """{ +default_policy = """{ "version":"1.0", "authorization-rules":{ "sub":"NVIDIA-GPU-ATTESTATION", @@ -53,13 +54,18 @@ } """ class GPUAuthorizer(CCAuthorizer): - def __init__(self, verifier_url="https://nras.attestation.nvidia.com/v1/attest/gpu"): + def __init__(self, verifier_url="https://nras.attestation.nvidia.com/v1/attest/gpu", policy_file=None): self._can_generate = True self.client = attestation.Attestation() - self.client.set_name("thisNode1") - self.client.set_nonce("931d8dd0add203ac3d8b4fbde75e115278eefcdceac5b87671a748f32364dfcb") + self.client.set_name("nvflare_node") + nonce = uuid.uuid4().hex + uuid.uuid1().hex + self.client.set_nonce(nonce) + if policy_file is None: + self.remote_att_result_policy = default_policy + else: + self.remote_att_result_policy = open(policy_file).read() self.client.add_verifier(attestation.Devices.GPU, attestation.Environment.REMOTE, verifier_url, "") - self.remote_att_result_policy = policy + self.logger = logging.getLogger(self.__class__.__name__) def generate(self): try: @@ -72,41 +78,17 @@ def generate(self): def verify(self, eat_token): try: - # header = jwt.get_unverified_header(jwt_token[1]) - # # url = header.get("jku") - # alg = header.get('alg') - # jwks_client = PyJWKClient(self.verifier_url) - # signing_key = jwks_client.get_signing_key_from_jwt(jwt_token) jwt_token = json.loads(eat_token)[1] - # pprint.pprint(f"{jwt_token=}") claims = jwt.decode(jwt_token.get("REMOTE_GPU_CLAIMS"), options={"verify_signature": False}) - # pprint.pprint(f"{claims=}") + # With claims, we will retrieve the nonce nonce = claims.get('eat_nonce') - self.client.set_name("nvflare_node1") - # self.client.set_nonce("931d8dd0add203ac3d8b4fbde75e115278eefcdceac5b87671a748f32364dfcb") self.client.set_nonce(nonce) - self.client.set_token(name='nvflare_node1', eat_token=eat_token) + self.client.set_token(name='nvflare_node', eat_token=eat_token) result = self.client.validate_token(self.remote_att_result_policy) except BaseException as e: - print("Exception {e=}") + self.logger.info(f"Token verification failed {e=}") result = False return result - def can_generate(self) -> bool: - return True - - def can_verify(self) -> bool: - return True - def get_namespace(self) -> str: return GPU_NAMESPACE - -if __name__=="__main__": - gpu_ath = GPUAuthorizer(verifier_url="https://nras.attestation.nvidia.com/v1/attest/gpu") - # gpu_tp.generate() - test_token = gpu_ath.generate() - # print(test_token) - # test_token='[["JWT", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJOVi1BdHRlc3RhdGlvbi1TREsiLCJpYXQiOjE3MDk3NTA0NTQsImV4cCI6bnVsbH0.WI8rPoyzzDCXSOYN6yGC6kEYpqXif-SagpLli7-KeTM"], {"REMOTE_GPU_CLAIMS": "eyJraWQiOiJudi1hdHRlc3RhdGlvbi1zaWduLWtpZC1wcm9kLTIwMjQwMzA1MTE1MjIyIiwiYWxnIjoiRVMzODQifQ.eyJzdWIiOiJOVklESUEtR1BVLUFUVEVTVEFUSU9OIiwic2VjYm9vdCI6dHJ1ZSwieC1udmlkaWEtZ3B1LW1hbnVmYWN0dXJlciI6Ik5WSURJQSBDb3Jwb3JhdGlvbiIsIngtbnZpZGlhLWF0dGVzdGF0aW9uLXR5cGUiOiJHUFUiLCJpc3MiOiJodHRwczpcL1wvbnJhcy5hdHRlc3RhdGlvbi5udmlkaWEuY29tIiwiZWF0X25vbmNlIjoiOTMxRDhERDBBREQyMDNBQzNEOEI0RkJERTc1RTExNTI3OEVFRkNEQ0VBQzVCODc2NzFBNzQ4RjMyMzY0REZDQiIsIngtbnZpZGlhLWF0dGVzdGF0aW9uLWRldGFpbGVkLXJlc3VsdCI6eyJ4LW52aWRpYS1ncHUtZHJpdmVyLXJpbS1zY2hlbWEtdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLWNlcnQtdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtYXR0ZXN0YXRpb24tcmVwb3J0LWNlcnQtY2hhaW4tdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtZHJpdmVyLXJpbS1zY2hlbWEtZmV0Y2hlZCI6dHJ1ZSwieC1udmlkaWEtZ3B1LWF0dGVzdGF0aW9uLXJlcG9ydC1wYXJzZWQiOnRydWUsIngtbnZpZGlhLWdwdS1ub25jZS1tYXRjaCI6dHJ1ZSwieC1udmlkaWEtZ3B1LXZiaW9zLXJpbS1zaWduYXR1cmUtdmVyaWZpZWQiOnRydWUsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLXNpZ25hdHVyZS12ZXJpZmllZCI6dHJ1ZSwieC1udmlkaWEtZ3B1LWFyY2gtY2hlY2siOnRydWUsIngtbnZpZGlhLWF0dGVzdGF0aW9uLXdhcm5pbmciOm51bGwsIngtbnZpZGlhLWdwdS1tZWFzdXJlbWVudHMtbWF0Y2giOnRydWUsIngtbnZpZGlhLWdwdS1hdHRlc3RhdGlvbi1yZXBvcnQtc2lnbmF0dXJlLXZlcmlmaWVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLXNjaGVtYS12YWxpZGF0ZWQiOnRydWUsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLWNlcnQtdmFsaWRhdGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLXNjaGVtYS1mZXRjaGVkIjp0cnVlLCJ4LW52aWRpYS1ncHUtdmJpb3MtcmltLW1lYXN1cmVtZW50cy1hdmFpbGFibGUiOnRydWUsIngtbnZpZGlhLWdwdS1kcml2ZXItcmltLWRyaXZlci1tZWFzdXJlbWVudHMtYXZhaWxhYmxlIjp0cnVlfSwieC1udmlkaWEtdmVyIjoiMS4wIiwibmJmIjoxNzA5NzUwNDY2LCJ4LW52aWRpYS1ncHUtZHJpdmVyLXZlcnNpb24iOiI1MzUuMTI5LjAzIiwiZGJnc3RhdCI6ImRpc2FibGVkIiwiaHdtb2RlbCI6IkdIMTAwIEEwMSBHU1AgQlJPTSIsIm9lbWlkIjoiNTcwMyIsIm1lYXNyZXMiOiJjb21wYXJpc29uLXN1Y2Nlc3NmdWwiLCJleHAiOjE3MDk3NTQwNjYsImlhdCI6MTcwOTc1MDQ2NiwieC1udmlkaWEtZWF0LXZlciI6IkVBVC0yMSIsInVlaWQiOiI1MzQyNDkwNzAzMzcyNjAwNjk3MjE4NzI1ODI2NDM4NzA4NDYzNjE3MzI2MDk5MjMiLCJ4LW52aWRpYS1ncHUtdmJpb3MtdmVyc2lvbiI6Ijk2LjAwLjc0LjAwLjExIiwianRpIjoiOWU2NzU3MmYtYThmNy00YWY3LWFhYzctNzNiOWEzZGU0NzIwIn0.Te2hD9zdKCg5c58kmKbEajsB83o7hQ4hIt4AsGJ5GNc2ibhUiooLwtlPy1gie3eJKTfbmiBmP8t9fMA2h0kOodu2uRUjWOkKwtKoAI9esHuSxz1_avu65hsj-njKzgBW"}]' - result = gpu_ath.verify(test_token) - print(f"{result=}") - From 9795dc93e9d841dfe16ae2822756a2c6e0362a74 Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Wed, 30 Oct 2024 12:22:58 -0700 Subject: [PATCH 4/7] Code clenup --- .../confidential_computing/cc_helper.py | 138 ------------------ .../confidential_computing/coco_authorizer.py | 72 --------- ...dummy_authorizer.py => mock_authorizer.py} | 25 +--- .../confidential_computing/snp_authorizer.py | 58 ++------ .../confidential_computing/tdx_authorizer.py | 2 +- 5 files changed, 19 insertions(+), 276 deletions(-) delete mode 100644 nvflare/app_opt/confidential_computing/cc_helper.py delete mode 100644 nvflare/app_opt/confidential_computing/coco_authorizer.py rename nvflare/app_opt/confidential_computing/{dummy_authorizer.py => mock_authorizer.py} (93%) diff --git a/nvflare/app_opt/confidential_computing/cc_helper.py b/nvflare/app_opt/confidential_computing/cc_helper.py deleted file mode 100644 index c01d0146e0..0000000000 --- a/nvflare/app_opt/confidential_computing/cc_helper.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. -# -# 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. -# import os.path - -import os -from typing import Dict - -from nv_attestation_sdk.attestation import Attestation, Devices, Environment - -from nvflare.fuel.utils.log_utils import get_obj_logger - - -class VerifierProp: - - DEVICES = "devices" # GPU, CPU, etc. - ENV = "env" - URL = "url" - APPRAISAL_POLICY_FILE = "appraisal_policy_file" - RESULT_POLICY_FILE = "result_policy_file" - - -class Device: - - GPU = "gpu" - CPU = "cpu" - NIC = "nic" - OS = "os" - DPU = "dpu" - - mapping = {GPU: Devices.GPU, CPU: Devices.CPU, NIC: Devices.NIC, OS: Devices.OS, DPU: Devices.DPU} - - -class Env: - - TEST = "test" - LOCAL = "local" - AZURE = "azure" - GCP = "gcp" - - mapping = {TEST: Environment.TEST, LOCAL: Environment.LOCAL, AZURE: Environment.AZURE, GCP: Environment.GCP} - - -class CCHelper(object): - def __init__(self, site_name: str, verifiers: list): - """Create an AttestationHelper instance - - Args: - site_name: name of the site - verifiers: dict that specifies verifiers to be used - """ - self.site_name = site_name - self.verifiers = verifiers - attestation = Attestation() - attestation.set_name(site_name) - self.attestation = attestation - self.token = None - self.logger = get_obj_logger(self) - for v in verifiers: - assert isinstance(v, dict) - url = None - env = None - devices = 0 - appraisal_policy_file = None - result_policy_file = None - for prop, value in v.items(): - if prop == VerifierProp.URL: - url = value - elif prop == VerifierProp.ENV: - env = Env.mapping.get(value) - elif prop == VerifierProp.DEVICES: - dv = Device.mapping.get(value) - if not dv: - raise ValueError(f"invalid device '{value}'") - devices = dv - elif prop == VerifierProp.APPRAISAL_POLICY_FILE: - appraisal_policy_file = value - elif prop == VerifierProp.RESULT_POLICY_FILE: - result_policy_file = value - if not env: - raise ValueError("Environment is not specified for verifier") - if not devices: - raise ValueError("Devices is not specified for verifier") - if url is None: - raise ValueError("Url is not specified for verifier") - if appraisal_policy_file is None: - raise ValueError("Appraisal policy file is not specified for verifier") - if not os.path.exists(appraisal_policy_file): - raise ValueError(f"Appraisal policy file '{appraisal_policy_file}' does not exist") - appraisal_policy = open(appraisal_policy_file, "rt").read().rstrip() - if result_policy_file is None: - raise ValueError("Result policy file is not specified for verifier") - if not os.path.exists(result_policy_file): - raise ValueError(f"Result policy file '{result_policy_file}' does not exist") - self.result_policy = open(result_policy_file, "rt").read().rstrip() - attestation.add_verifier(devices, env, url, appraisal_policy) - - def reset_participant(self, participant_name: str): - pass - - def prepare(self) -> bool: - """Prepare for attestation process - - Returns: error if any - """ - ok = self.attestation.attest() - self.logger.info(f"CC - attest result (is valid?): {ok}") - self.token = self.attestation.get_token(self.site_name) - self.logger.info(f"token {self.token=}") - return True - - def get_token(self): - return self.token - - def validate_participants(self, participants: Dict[str, str]) -> Dict[str, bool]: - """Validate CC policies of specified participants against the requirement policy of the site. - - Args: - participants: dict of participant name => token - - Returns: dict of participant name => bool - - """ - if not participants: - return {} - result = {k: self.attestation.validate_token(self.result_policy, v) for k, v in participants.items()} - self.logger.debug(f"CC - results from validating participants' tokens: {result}") - return result diff --git a/nvflare/app_opt/confidential_computing/coco_authorizer.py b/nvflare/app_opt/confidential_computing/coco_authorizer.py deleted file mode 100644 index b5aec2b790..0000000000 --- a/nvflare/app_opt/confidential_computing/coco_authorizer.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. -# -# 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. -import json -import requests -import time -import jwt -from jwt import PyJWKClient - -from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer - -COCO_NAMESPACE = "x-ms" -maa_endpoint = 'sharedeus2.eus2.attest.azure.net' - -class CoCoAuthorizer(CCAuthorizer): - def generate(self): - count = 0 - token = "" - while True: - count = count + 1 - try: - r = requests.post('http://localhost:8284/attest/maa', data=json.dumps({"maa_endpoint": maa_endpoint, "runtime_data": "ewp9"}), headers={"Content-Type" : "application/json"}) - if r.status_code == requests.codes.ok: - token = r.json().get("token") - break - except: - if count > 5: - break - time.sleep(2) - return token - - def verify(self, token): - try: - header = jwt.get_unverified_header(token) - # print(f"{header=}") - alg = header.get('alg') - jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") - signing_key = jwks_client.get_signing_key_from_jwt(token) - claims = jwt.decode(token, signing_key.key, algorithms=[alg]) - if claims: - # print(f"{claims=}") - return True - except: - return False - return False - - def can_generate(self) -> bool: - return True - - def can_verify(self) -> bool: - return True - - def get_namespace(self) -> str: - return COCO_NAMESPACE - -if __name__ == "__main__": - m = CoCoAuthorizer() - token = m.generate() - print(type(token)) - v = m.verify(token) - print(v) - diff --git a/nvflare/app_opt/confidential_computing/dummy_authorizer.py b/nvflare/app_opt/confidential_computing/mock_authorizer.py similarity index 93% rename from nvflare/app_opt/confidential_computing/dummy_authorizer.py rename to nvflare/app_opt/confidential_computing/mock_authorizer.py index 219ca8e683..18efa5f994 100644 --- a/nvflare/app_opt/confidential_computing/dummy_authorizer.py +++ b/nvflare/app_opt/confidential_computing/mock_authorizer.py @@ -12,35 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import jwt -from jwt import PyJWKClient -import subprocess - from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer -MAA_NAMESPACE = "x-ms" -maa_endpoint = 'sharedeus2.eus2.attest.azure.net' +MOCK_NAMESPACE = "x-mock" -class DummyAuthorizer(CCAuthorizer): +class MockAuthorizer(CCAuthorizer): def generate(self): return "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vc2hhcmVkZXVzMi5ldXMyLmF0dGVzdC5henVyZS5uZXQvY2VydHMiLCJraWQiOiJKMHBBUGRmWFhIcVdXaW1nckg4NTN3TUlkaDUvZkxlMXo2dVNYWVBYQ2EwPSIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDk5NDM0OTYsImlhdCI6MTcwOTkxNDY5NiwiaXNzIjoiaHR0cHM6Ly9zaGFyZWRldXMyLmV1czIuYXR0ZXN0LmF6dXJlLm5ldCIsImp0aSI6IjQ1MGIxNWMxMmRmYzIxMWM5ZWRkOGU4MmFiY2NiZTEzYmMyOTgzZjlhNjAzOTZlMzljZTJmZGIwYjFmNTg1YzEiLCJuYmYiOjE3MDk5MTQ2OTYsInNlY3VyZWJvb3QiOnRydWUsIngtbXMtYXR0ZXN0YXRpb24tdHlwZSI6ImF6dXJldm0iLCJ4LW1zLWF6dXJldm0tYXR0ZXN0YXRpb24tcHJvdG9jb2wtdmVyIjoiMi4wIiwieC1tcy1henVyZXZtLWF0dGVzdGVkLXBjcnMiOlswLDEsMiwzLDQsNSw2LDddLCJ4LW1zLWF6dXJldm0tYm9vdGRlYnVnLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0tZGJ2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1kYnh2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1kZWJ1Z2dlcnNkaXNhYmxlZCI6dHJ1ZSwiwC1tcy1henVyZXZtLWRlZmF1bHQtc2VjdXJlYm9vdGtleXN2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1lbGFtLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0tZmxpZ2h0c2lnbmluZy1lbmFibGVkIjpmYWxzZSwieC1tcy1henVyZXZtLWh2Y2ktcG9saWN5IjowLCJ4LW1zLWF6dXJldm0taHlwZXJ2aXNvcmRlYnVnLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0taXMtd2luZG93cyI6ZmFsc2UsIngtbXMtYXp1cmV2bS1rZXJuZWxkZWJ1Zy1lbmFibGVkIjpmYWxzZSwieC1tcy1henVyZXZtLW9zYnVpbGQiOiJOb3RBcHBsaWNhdGlvbiIsIngtbXMtYXp1cmV2bS1vc2Rpc3RybyI6IlVidW50dSIsIngtbXMtYXp1cmV2bS1vc3R5cGUiOiJMaW51eCIsIngtbXMtYXp1cmV2bS1vc3ZlcnNpb24tbWFqb3IiOjIyLCJ4LW1zLWF6dXJldm0tb3N2ZXJzaW9uLW1pbm9yIjo0LCJ4LW1zLWF6dXJldm0tc2lnbmluZ2Rpc2FibGVkIjp0cnVlLCJ4LW1zLWF6dXJldm0tdGVzdHNpZ25pbmctZW5hYmxlZCI6ZmFsc2UsIngtbXMtYXp1cmV2bS12bWlkIjoiQzRGRkM0QjMtOUFERi00MEQxLThDQ0MtMTAxMUUxQkNDREIwIiwieC1tcy1pc29sYXRpb24tdGVlIjp7IngtbXMtYXR0ZXN0YXRpb24tdHlwZSI6InNldnNucHZtIiwieC1tcy1jb21wbGlhbmNlLXN0YXR1cyI6ImF6dXJlLWNvbXBsaWFudC1jdm0iLCJ4LW1zLXJ1bnRpbWUiOnsia2V5cyI6W3siZSI6IkFRQUIiLCJrZXlfb3BzIjpbInNpZ24iXSwia2lkIjoiSENMQWtQdWIiLCJrdHkiOiJSU0EiLCJuIjoiNEFQakF3QUFwQjE4cnc0bDh4Y0pmQXNpT1pJb1lSdGpYLVdOM0RhdVZ2cWlOSGlNU2RFaFNtWW9CQnVTcUVfa2pHblpZOFRWb2RSRkdJNWtFalR4NmhBZFM1OHIzY2R6OEtYMERmOHZERjF3Y2NjVW52SHJGY3FnRnNGVWs0UHJZZko2eU9nell2bmhvdWFSQlZ4dmZ5bEMwZWZhTUNUUkdES2pZSzhPVV9RcWxFeGIzY19neEJZWGlSZ3dBYWFaZUd4eFNId3U0a3lwZ3hwMlhjWXlHLVU3a3FHc01VWnlkZmM0eUxiS1BQcl9zMUJYUTNSbkFtdTQtblhkdVRmcWlWX2gxbGN4V29fYVhSWkFNdG9hTnVkclkyMVZVV3AzVW5xeFFtdGVGcXplWWpEQm8ta2Fjel9iNG9Gem5OM29aSlVUZmJnb09sTzJzbDc3U05Xa0x3In0seyJlIjoiQVFBQiIsImtleV9vcHMiOlsiZW5jcnlwdCJdLCJraWQiOiJIQ0xFa1B1YiIsImt0eSI6IlJTQSIsIm4iOiJ3c3lxREFBQloySHlTd08wTDFaWXZwVkhOVFVpdXpPaGcyb3Bfa1VpckNqM1M5bEtpT051YmU4RWFsSFVBcUh6bGdEcF84dHdBdGRONGNOUzZUSWdITG94TXpaZUFpaWU1OGd2VGtYdzVqMThmVUY4UEVvT2NXVERFSmRMVXIxWnBEdTRSdUZXbDdkZHNIdFBJRDVqcmt0R21FajBCZVp5NzZWVGFUYU1iamhGRmphUGNCT01fOWNaVHJYdFduSG80WTF1TG53VUJPRzA3T1hrUmlTSjBHZ3phVFhvaFVzVGE4X0w5NDJfeml5QU16STliUnJmUV9JMXY0SFV0M1YzS3laaUZJS3B4X2hWQnZZZUhwcTZBbXlYRTExS0VXek5HMTJaZVNkbzBzMUhudFNidTQ3dUktYklIdnBLaVVQSzBYSGZWbDQwVnNwenJ2MjlqekppR1EifV0sInVzZXItZGF0YSI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwidm0tY29uZmlndXJhdGlvbiI6eyJjb25zb2xlLWVuYWJsZWQiOnRydWUsInJvb3QtY2VydC10aHVtYnByaW50IjoiNm5aWm5ZYUpjNEtxVVpfeXZBLW11Y0ZkWU5vdXZsUG5JVG5OTVhzSGwtMCIsInNlY3VyZS1ib290Ijp0cnVlLCJ0cG0tZW5hYmxlZCI6dHJ1ZSwidHBtLXBlcnNpc3RlZCI6dHJ1ZSwidm1VbmlxdWVJZCI6IkM0RkZDNEIzLTlBREYtNDBEMS04Q0NDLTEwMTFFMUJDQ0RCMCJ9fSwieC1tcy1zZXZzbnB2bS1hdXRob3JrZXlkaWdlc3QiOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWJvb3Rsb2FkZXItc3ZuIjo3LCJ4LW1zLXNldnNucHZtLWZhbWlseUlkIjoiMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWd1ZXN0c3ZuIjo2NTU0MSwieC1tcy1zZXZzbnB2bS1ob3N0ZGF0YSI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWlka2V5ZGlnZXN0IjoiMDM1NjIxNTg4MmE4MjUyNzlhODViMzAwYjBiNzQyOTMxZDExM2JmN2UzMmRkZTJlNTBmZmRlN2VjNzQzY2E0OTFlY2RkN2YzMzZkYzI4YTZlMGIyYmI1N2FmN2E0NGEzIiwieC1tcy1zZXZzbnB2bS1pbWFnZUlkIjoiMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWlzLWRlYnVnZ2FibGUiOmZhbHNlLCJ4LW1zLXNldnNucHZtLWxhdW5jaG1lYXN1cmVtZW50IjoiN2M0MjA4NjE0ZDMyNzYzMDI4M2VkOGFhNjUyOTcxZjNkYzI0YzU0NmY2ZWUxMzBkMzJlNGUzYjg0ZjFhYTFmNWVmMmQyMTAxMmQwZmRlMDU2ZDhmOTAwYzM5MmM3NzJjIiwieC1tcy1zZXZzbnB2bS1taWNyb2NvZGUtc3ZuIjo2MiwieC1tcy1zZXZzbnB2bS1taWdyYXRpb24tYWxsb3dlZCI6ZmFsc2UsIngtbXMtc2V2c25wdm0tcmVwb3J0ZGF0YSI6IjU0ODE0ZTlhNjQ0N2JjMWM5MGE5YTExNmYxYjRjYjdlMDU5ZTYzMzQzNDgwY2Q4N2FmMjcxZjc5MjdjOThlMTMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwieC1tcy1zZXZzbnB2bS1yZXBvcnRpZCI6ImRhNjU0YWY3NGZiODIzZjJiM2E1ODMzMzVmOGYzYmUzMmY2ODkwYTdkYjIzMzUyM2RkOWUwNTRmNDQzMDU2OWYiLCJ4LW1zLXNldnNucHZtLXNtdC1hbGxvd2VkIjp0cnVlLCJ4LW1zLXNldnNucHZtLXNucGZ3LXN2biI6MTUsIngtbXMtc2V2c25wdm0tdGVlLXN2biI6MCwieC1tcy1zZXZzbnB2bS12bXBsIjowfSwieC1tcy1wb2xpY3ktaGFzaCI6IndtOW1IbHZUVTgyZThVcW9PeTFZajFGQlJTTmtmZTk5LTY5SVlEcTllV3MiLCJ4LW1zLXJ1bnRpbWUiOnsiY2xpZW50LXBheWxvYWQiOnsibm9uY2UiOiIifSwia2V5cyI6W3siZSI6IkFRQUIiLCJrZXlfb3BzIjpbImVuY3J5cHQiXSwia2lkIjoiVHBtRXBoZW1lcmFsRW5jcnlwdGlvbktleSIsImt0eSI6IlJTQSIsIm4iOiJ6eHN2bWdBQV9rRlBKMjZzYnRfdFhVUDhqcHowMk50YnhRU2hPd0lxa1h6U1hxUGJmV1Vxb1hpUm9idzJqTC01ZTNiQU1LU3J3cmpMU09DcnNtbjdPMnZxVmNBMW9Ucl9ObFE3NEpMMnlBanZVWGFId0dBZVVzbkNfWm51UXltdzNFMXJ4NnNCUUxZWFcwMTRiQjNKX01feFBDaGZfQk9NTGdNTlRzbTNwbGh0eTVNRWJ5OWJBSXp6a2ZPNjZhblhWUWxUVE1xQk43SkJVeU5QSFBWQU82V3N6bzl6YnI1aWhlUUxOZDZLZXNEMHU1VXBYaVo4UU5zUng2cXJCUS12TkVsVXQ2cXRvbC1xVzh4TkdvckxBWlhhZ0FyRVpGTE9aOEZWa0hQWGlMM0wwZ253eDBvb0l5UG5pbk5WY2dLanFYR3pOdnB1ejJGTmNuQU9uS2pKZFEifV19LCJ4LW1zLXZlciI6IjEuMCJ9.COvydLJUjR5voFyG-AUJlEp9fyJNBtbptkgq9p-KNkMlFCorY87VZPDrmITH4gYM5YpYuDk370P81hvd2Pw9COZB1-t9VSaWMJzcyL-T43Sh8nSGNO13kOqDQiHss1907kBiFy2jWngaoxuJvO4BSNFkxL9bsCsEZVSpMDmO9zZkp1Ja7sp-Cptm9rwf5JTfKuWZ4cazn2hUkWbQHBg51b8AeryNzU-35oEhGCIPYqryXv5SY32PB9s-lwh6l7K3t768P817XKF3Szip0TZpgIMoM0GU4oNOnjnFZ3u8DnvuyEim-pCZgP7qpQmJI4lrgI6Sn-jxqTg8q0FUmAkcnQ" def verify(self, token): return True - def can_generate(self) -> bool: - return True - - def can_verify(self) -> bool: - return True - def get_namespace(self) -> str: - return MAA_NAMESPACE - - -if __name__ == "__main__": - m = MAAAuthorizer() - token = m.generate() - print(type(token)) - v = m.verify(token) - print(v) + return MOCK_NAMESPACE diff --git a/nvflare/app_opt/confidential_computing/snp_authorizer.py b/nvflare/app_opt/confidential_computing/snp_authorizer.py index 718c59740b..6f7560267e 100644 --- a/nvflare/app_opt/confidential_computing/snp_authorizer.py +++ b/nvflare/app_opt/confidential_computing/snp_authorizer.py @@ -12,75 +12,47 @@ # See the License for the specific language governing permissions and # limitations under the License. -import jwt -from jwt import PyJWKClient import subprocess import base64 -import traceback +import uuid import os +import logging from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer SNP_NAMESPACE = "x-snp" class SNPAuthorizer(CCAuthorizer): + def __init__(self): + super().__init__() + self.logger = logging.getLogger(self.__class__.__name__) + def generate(self): cmd = ['sudo', 'snpguest', 'report', 'report.bin', 'request.bin'] with open('request.bin', 'wb') as request_file: request_file.write(b'\x01'*64) - cp = subprocess.run(cmd, capture_output=True) - # print(token) + _ = subprocess.run(cmd, capture_output=True) with open('report.bin', 'rb') as report_file: token = base64.b64encode(report_file.read()) - # print(token) return token def verify(self, token): - # print("Now verifying SNP") try: - # if not os.path.exists('./cert'): - # os.mkdir('./cert') - # cmd = ['snpguest', 'fetch', 'ca', 'der', 'genoa', './cert', '--endorser', 'vcek'] - # cp = subprocess.run(cmd, capture_output=True) - # if cp.returncode != 0: - # return False report_bin = base64.b64decode(token) - with open('rcv_report.bin', 'wb') as report_file: + tmp_bin_file = uuid.uuid4().hex + with open(tmp_bin_file, 'wb') as report_file: report_file.write(report_bin) - # cmd = ['snpguest', 'fetch', 'vcek', 'der', 'genoa', './cert', 'rcv_report.bin'] - # cp = subprocess.run(cmd, capture_output=True) - # if cp.returncode != 0: - # return False - # cmd = ['snpguest', 'verify', 'certs', './cert'] - # cp = subprocess.run(cmd, capture_output=True) - # if cp.returncode != 0: - # return False - cmd = ['snpguest', 'verify', 'attestation', './cert', 'rcv_report.bin'] + cmd = ['snpguest', 'verify', 'attestation', './cert', tmp_bin_file] cp = subprocess.run(cmd, capture_output=True) if cp.returncode != 0: return False - print(f"{cp.stdout=}\n{cp.stderr=} Now returning True") - # print(f"{claims=}") return True - except: - print(traceback.format_exc()) + except Exception as e: + self.logger.info(f"Token verification failed {e=}") return False - return True - - def can_generate(self) -> bool: - return True - - def can_verify(self) -> bool: - return True + finally: + if os.path.exists(tmp_bin_file): + os.remove(tmp_bin_file) def get_namespace(self) -> str: return SNP_NAMESPACE - - -if __name__ == "__main__": - m = SNPAuthorizer() - token = m.generate() - print(type(token)) - v = m.verify(token) - print(v) - diff --git a/nvflare/app_opt/confidential_computing/tdx_authorizer.py b/nvflare/app_opt/confidential_computing/tdx_authorizer.py index 21bff9035e..c8a3bb5756 100644 --- a/nvflare/app_opt/confidential_computing/tdx_authorizer.py +++ b/nvflare/app_opt/confidential_computing/tdx_authorizer.py @@ -17,7 +17,7 @@ from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer -TDX_NAMESPACE = "tdx_" +TDX_NAMESPACE = "x-tdx" TDX_CLI_CONFIG = "config.json" TOKEN_FILE = "token.txt" VERIFY_FILE = "verify.txt" From 75ef07f78e10c165b501cc627e1b6cd528ba7b60 Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Wed, 30 Oct 2024 12:24:39 -0700 Subject: [PATCH 5/7] Reformatted --- .../confidential_computing/aci_authorizer.py | 17 +++++++++----- .../confidential_computing/gpu_authorizer.py | 15 +++++++----- .../confidential_computing/maa_authorizer.py | 22 ++++++++++-------- .../confidential_computing/mock_authorizer.py | 3 ++- .../confidential_computing/snp_authorizer.py | 23 ++++++++++--------- 5 files changed, 46 insertions(+), 34 deletions(-) diff --git a/nvflare/app_opt/confidential_computing/aci_authorizer.py b/nvflare/app_opt/confidential_computing/aci_authorizer.py index 1a6a743f69..818e58e27b 100644 --- a/nvflare/app_opt/confidential_computing/aci_authorizer.py +++ b/nvflare/app_opt/confidential_computing/aci_authorizer.py @@ -12,15 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. import json -import requests import time + import jwt +import requests from jwt import PyJWKClient from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer ACI_NAMESPACE = "x-ms" -maa_endpoint = 'sharedeus2.eus2.attest.azure.net' +maa_endpoint = "sharedeus2.eus2.attest.azure.net" + class ACIAuthorizer(CCAuthorizer): def generate(self): @@ -29,7 +31,11 @@ def generate(self): while True: count = count + 1 try: - r = requests.post('http://localhost:8284/attest/maa', data=json.dumps({"maa_endpoint": maa_endpoint, "runtime_data": "ewp9"}), headers={"Content-Type" : "application/json"}) + r = requests.post( + "http://localhost:8284/attest/maa", + data=json.dumps({"maa_endpoint": maa_endpoint, "runtime_data": "ewp9"}), + headers={"Content-Type": "application/json"}, + ) if r.status_code == requests.codes.ok: token = r.json().get("token") break @@ -38,11 +44,11 @@ def generate(self): break time.sleep(2) return token - + def verify(self, token): try: header = jwt.get_unverified_header(token) - alg = header.get('alg') + alg = header.get("alg") jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") signing_key = jwks_client.get_signing_key_from_jwt(token) claims = jwt.decode(token, signing_key.key, algorithms=[alg]) @@ -54,4 +60,3 @@ def verify(self, token): def get_namespace(self) -> str: return ACI_NAMESPACE - diff --git a/nvflare/app_opt/confidential_computing/gpu_authorizer.py b/nvflare/app_opt/confidential_computing/gpu_authorizer.py index be2f607115..1090059380 100644 --- a/nvflare/app_opt/confidential_computing/gpu_authorizer.py +++ b/nvflare/app_opt/confidential_computing/gpu_authorizer.py @@ -13,14 +13,15 @@ # limitations under the License. -from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer -import logging import json -import jwt +import logging import uuid +import jwt from nv_attestation_sdk import attestation +from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer + GPU_NAMESPACE = "x-nv-gpu" default_policy = """{ "version":"1.0", @@ -53,6 +54,8 @@ } } """ + + class GPUAuthorizer(CCAuthorizer): def __init__(self, verifier_url="https://nras.attestation.nvidia.com/v1/attest/gpu", policy_file=None): self._can_generate = True @@ -75,15 +78,15 @@ def generate(self): self.can_generate = False token = "[[],{}]" return token - + def verify(self, eat_token): try: jwt_token = json.loads(eat_token)[1] claims = jwt.decode(jwt_token.get("REMOTE_GPU_CLAIMS"), options={"verify_signature": False}) # With claims, we will retrieve the nonce - nonce = claims.get('eat_nonce') + nonce = claims.get("eat_nonce") self.client.set_nonce(nonce) - self.client.set_token(name='nvflare_node', eat_token=eat_token) + self.client.set_token(name="nvflare_node", eat_token=eat_token) result = self.client.validate_token(self.remote_att_result_policy) except BaseException as e: self.logger.info(f"Token verification failed {e=}") diff --git a/nvflare/app_opt/confidential_computing/maa_authorizer.py b/nvflare/app_opt/confidential_computing/maa_authorizer.py index 0263c59c37..2258dd0350 100644 --- a/nvflare/app_opt/confidential_computing/maa_authorizer.py +++ b/nvflare/app_opt/confidential_computing/maa_authorizer.py @@ -12,29 +12,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +import subprocess + import jwt from jwt import PyJWKClient -import subprocess from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer MAA_NAMESPACE = "x-ms" -maa_endpoint = 'sharedeus2.eus2.attest.azure.net' +maa_endpoint = "sharedeus2.eus2.attest.azure.net" + class MAAAuthorizer(CCAuthorizer): def generate(self): - cmd = ['sudo', 'AttestationClient', '-o', 'token'] + cmd = ["sudo", "AttestationClient", "-o", "token"] cp = subprocess.run(cmd, capture_output=True) # print(f"{cp.stdout=}\n{cp.stderr=}") # print(token) token = cp.stdout return cp.stdout - + def verify(self, token): try: header = jwt.get_unverified_header(token) # print(f"{header=}") - alg = header.get('alg') + alg = header.get("alg") jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") signing_key = jwks_client.get_signing_key_from_jwt(token) claims = jwt.decode(token, signing_key.key, algorithms=[alg]) @@ -56,8 +58,8 @@ def get_namespace(self) -> str: if __name__ == "__main__": - m = MAAAuthorizer() - token = m.generate() - print(type(token)) - v = m.verify(token) - print(v) + m = MAAAuthorizer() + token = m.generate() + print(type(token)) + v = m.verify(token) + print(v) diff --git a/nvflare/app_opt/confidential_computing/mock_authorizer.py b/nvflare/app_opt/confidential_computing/mock_authorizer.py index 18efa5f994..b9e3e99818 100644 --- a/nvflare/app_opt/confidential_computing/mock_authorizer.py +++ b/nvflare/app_opt/confidential_computing/mock_authorizer.py @@ -16,10 +16,11 @@ MOCK_NAMESPACE = "x-mock" + class MockAuthorizer(CCAuthorizer): def generate(self): return "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vc2hhcmVkZXVzMi5ldXMyLmF0dGVzdC5henVyZS5uZXQvY2VydHMiLCJraWQiOiJKMHBBUGRmWFhIcVdXaW1nckg4NTN3TUlkaDUvZkxlMXo2dVNYWVBYQ2EwPSIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDk5NDM0OTYsImlhdCI6MTcwOTkxNDY5NiwiaXNzIjoiaHR0cHM6Ly9zaGFyZWRldXMyLmV1czIuYXR0ZXN0LmF6dXJlLm5ldCIsImp0aSI6IjQ1MGIxNWMxMmRmYzIxMWM5ZWRkOGU4MmFiY2NiZTEzYmMyOTgzZjlhNjAzOTZlMzljZTJmZGIwYjFmNTg1YzEiLCJuYmYiOjE3MDk5MTQ2OTYsInNlY3VyZWJvb3QiOnRydWUsIngtbXMtYXR0ZXN0YXRpb24tdHlwZSI6ImF6dXJldm0iLCJ4LW1zLWF6dXJldm0tYXR0ZXN0YXRpb24tcHJvdG9jb2wtdmVyIjoiMi4wIiwieC1tcy1henVyZXZtLWF0dGVzdGVkLXBjcnMiOlswLDEsMiwzLDQsNSw2LDddLCJ4LW1zLWF6dXJldm0tYm9vdGRlYnVnLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0tZGJ2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1kYnh2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1kZWJ1Z2dlcnNkaXNhYmxlZCI6dHJ1ZSwiwC1tcy1henVyZXZtLWRlZmF1bHQtc2VjdXJlYm9vdGtleXN2YWxpZGF0ZWQiOnRydWUsIngtbXMtYXp1cmV2bS1lbGFtLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0tZmxpZ2h0c2lnbmluZy1lbmFibGVkIjpmYWxzZSwieC1tcy1henVyZXZtLWh2Y2ktcG9saWN5IjowLCJ4LW1zLWF6dXJldm0taHlwZXJ2aXNvcmRlYnVnLWVuYWJsZWQiOmZhbHNlLCJ4LW1zLWF6dXJldm0taXMtd2luZG93cyI6ZmFsc2UsIngtbXMtYXp1cmV2bS1rZXJuZWxkZWJ1Zy1lbmFibGVkIjpmYWxzZSwieC1tcy1henVyZXZtLW9zYnVpbGQiOiJOb3RBcHBsaWNhdGlvbiIsIngtbXMtYXp1cmV2bS1vc2Rpc3RybyI6IlVidW50dSIsIngtbXMtYXp1cmV2bS1vc3R5cGUiOiJMaW51eCIsIngtbXMtYXp1cmV2bS1vc3ZlcnNpb24tbWFqb3IiOjIyLCJ4LW1zLWF6dXJldm0tb3N2ZXJzaW9uLW1pbm9yIjo0LCJ4LW1zLWF6dXJldm0tc2lnbmluZ2Rpc2FibGVkIjp0cnVlLCJ4LW1zLWF6dXJldm0tdGVzdHNpZ25pbmctZW5hYmxlZCI6ZmFsc2UsIngtbXMtYXp1cmV2bS12bWlkIjoiQzRGRkM0QjMtOUFERi00MEQxLThDQ0MtMTAxMUUxQkNDREIwIiwieC1tcy1pc29sYXRpb24tdGVlIjp7IngtbXMtYXR0ZXN0YXRpb24tdHlwZSI6InNldnNucHZtIiwieC1tcy1jb21wbGlhbmNlLXN0YXR1cyI6ImF6dXJlLWNvbXBsaWFudC1jdm0iLCJ4LW1zLXJ1bnRpbWUiOnsia2V5cyI6W3siZSI6IkFRQUIiLCJrZXlfb3BzIjpbInNpZ24iXSwia2lkIjoiSENMQWtQdWIiLCJrdHkiOiJSU0EiLCJuIjoiNEFQakF3QUFwQjE4cnc0bDh4Y0pmQXNpT1pJb1lSdGpYLVdOM0RhdVZ2cWlOSGlNU2RFaFNtWW9CQnVTcUVfa2pHblpZOFRWb2RSRkdJNWtFalR4NmhBZFM1OHIzY2R6OEtYMERmOHZERjF3Y2NjVW52SHJGY3FnRnNGVWs0UHJZZko2eU9nell2bmhvdWFSQlZ4dmZ5bEMwZWZhTUNUUkdES2pZSzhPVV9RcWxFeGIzY19neEJZWGlSZ3dBYWFaZUd4eFNId3U0a3lwZ3hwMlhjWXlHLVU3a3FHc01VWnlkZmM0eUxiS1BQcl9zMUJYUTNSbkFtdTQtblhkdVRmcWlWX2gxbGN4V29fYVhSWkFNdG9hTnVkclkyMVZVV3AzVW5xeFFtdGVGcXplWWpEQm8ta2Fjel9iNG9Gem5OM29aSlVUZmJnb09sTzJzbDc3U05Xa0x3In0seyJlIjoiQVFBQiIsImtleV9vcHMiOlsiZW5jcnlwdCJdLCJraWQiOiJIQ0xFa1B1YiIsImt0eSI6IlJTQSIsIm4iOiJ3c3lxREFBQloySHlTd08wTDFaWXZwVkhOVFVpdXpPaGcyb3Bfa1VpckNqM1M5bEtpT051YmU4RWFsSFVBcUh6bGdEcF84dHdBdGRONGNOUzZUSWdITG94TXpaZUFpaWU1OGd2VGtYdzVqMThmVUY4UEVvT2NXVERFSmRMVXIxWnBEdTRSdUZXbDdkZHNIdFBJRDVqcmt0R21FajBCZVp5NzZWVGFUYU1iamhGRmphUGNCT01fOWNaVHJYdFduSG80WTF1TG53VUJPRzA3T1hrUmlTSjBHZ3phVFhvaFVzVGE4X0w5NDJfeml5QU16STliUnJmUV9JMXY0SFV0M1YzS3laaUZJS3B4X2hWQnZZZUhwcTZBbXlYRTExS0VXek5HMTJaZVNkbzBzMUhudFNidTQ3dUktYklIdnBLaVVQSzBYSGZWbDQwVnNwenJ2MjlqekppR1EifV0sInVzZXItZGF0YSI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwidm0tY29uZmlndXJhdGlvbiI6eyJjb25zb2xlLWVuYWJsZWQiOnRydWUsInJvb3QtY2VydC10aHVtYnByaW50IjoiNm5aWm5ZYUpjNEtxVVpfeXZBLW11Y0ZkWU5vdXZsUG5JVG5OTVhzSGwtMCIsInNlY3VyZS1ib290Ijp0cnVlLCJ0cG0tZW5hYmxlZCI6dHJ1ZSwidHBtLXBlcnNpc3RlZCI6dHJ1ZSwidm1VbmlxdWVJZCI6IkM0RkZDNEIzLTlBREYtNDBEMS04Q0NDLTEwMTFFMUJDQ0RCMCJ9fSwieC1tcy1zZXZzbnB2bS1hdXRob3JrZXlkaWdlc3QiOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWJvb3Rsb2FkZXItc3ZuIjo3LCJ4LW1zLXNldnNucHZtLWZhbWlseUlkIjoiMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWd1ZXN0c3ZuIjo2NTU0MSwieC1tcy1zZXZzbnB2bS1ob3N0ZGF0YSI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWlka2V5ZGlnZXN0IjoiMDM1NjIxNTg4MmE4MjUyNzlhODViMzAwYjBiNzQyOTMxZDExM2JmN2UzMmRkZTJlNTBmZmRlN2VjNzQzY2E0OTFlY2RkN2YzMzZkYzI4YTZlMGIyYmI1N2FmN2E0NGEzIiwieC1tcy1zZXZzbnB2bS1pbWFnZUlkIjoiMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ4LW1zLXNldnNucHZtLWlzLWRlYnVnZ2FibGUiOmZhbHNlLCJ4LW1zLXNldnNucHZtLWxhdW5jaG1lYXN1cmVtZW50IjoiN2M0MjA4NjE0ZDMyNzYzMDI4M2VkOGFhNjUyOTcxZjNkYzI0YzU0NmY2ZWUxMzBkMzJlNGUzYjg0ZjFhYTFmNWVmMmQyMTAxMmQwZmRlMDU2ZDhmOTAwYzM5MmM3NzJjIiwieC1tcy1zZXZzbnB2bS1taWNyb2NvZGUtc3ZuIjo2MiwieC1tcy1zZXZzbnB2bS1taWdyYXRpb24tYWxsb3dlZCI6ZmFsc2UsIngtbXMtc2V2c25wdm0tcmVwb3J0ZGF0YSI6IjU0ODE0ZTlhNjQ0N2JjMWM5MGE5YTExNmYxYjRjYjdlMDU5ZTYzMzQzNDgwY2Q4N2FmMjcxZjc5MjdjOThlMTMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwieC1tcy1zZXZzbnB2bS1yZXBvcnRpZCI6ImRhNjU0YWY3NGZiODIzZjJiM2E1ODMzMzVmOGYzYmUzMmY2ODkwYTdkYjIzMzUyM2RkOWUwNTRmNDQzMDU2OWYiLCJ4LW1zLXNldnNucHZtLXNtdC1hbGxvd2VkIjp0cnVlLCJ4LW1zLXNldnNucHZtLXNucGZ3LXN2biI6MTUsIngtbXMtc2V2c25wdm0tdGVlLXN2biI6MCwieC1tcy1zZXZzbnB2bS12bXBsIjowfSwieC1tcy1wb2xpY3ktaGFzaCI6IndtOW1IbHZUVTgyZThVcW9PeTFZajFGQlJTTmtmZTk5LTY5SVlEcTllV3MiLCJ4LW1zLXJ1bnRpbWUiOnsiY2xpZW50LXBheWxvYWQiOnsibm9uY2UiOiIifSwia2V5cyI6W3siZSI6IkFRQUIiLCJrZXlfb3BzIjpbImVuY3J5cHQiXSwia2lkIjoiVHBtRXBoZW1lcmFsRW5jcnlwdGlvbktleSIsImt0eSI6IlJTQSIsIm4iOiJ6eHN2bWdBQV9rRlBKMjZzYnRfdFhVUDhqcHowMk50YnhRU2hPd0lxa1h6U1hxUGJmV1Vxb1hpUm9idzJqTC01ZTNiQU1LU3J3cmpMU09DcnNtbjdPMnZxVmNBMW9Ucl9ObFE3NEpMMnlBanZVWGFId0dBZVVzbkNfWm51UXltdzNFMXJ4NnNCUUxZWFcwMTRiQjNKX01feFBDaGZfQk9NTGdNTlRzbTNwbGh0eTVNRWJ5OWJBSXp6a2ZPNjZhblhWUWxUVE1xQk43SkJVeU5QSFBWQU82V3N6bzl6YnI1aWhlUUxOZDZLZXNEMHU1VXBYaVo4UU5zUng2cXJCUS12TkVsVXQ2cXRvbC1xVzh4TkdvckxBWlhhZ0FyRVpGTE9aOEZWa0hQWGlMM0wwZ253eDBvb0l5UG5pbk5WY2dLanFYR3pOdnB1ejJGTmNuQU9uS2pKZFEifV19LCJ4LW1zLXZlciI6IjEuMCJ9.COvydLJUjR5voFyG-AUJlEp9fyJNBtbptkgq9p-KNkMlFCorY87VZPDrmITH4gYM5YpYuDk370P81hvd2Pw9COZB1-t9VSaWMJzcyL-T43Sh8nSGNO13kOqDQiHss1907kBiFy2jWngaoxuJvO4BSNFkxL9bsCsEZVSpMDmO9zZkp1Ja7sp-Cptm9rwf5JTfKuWZ4cazn2hUkWbQHBg51b8AeryNzU-35oEhGCIPYqryXv5SY32PB9s-lwh6l7K3t768P817XKF3Szip0TZpgIMoM0GU4oNOnjnFZ3u8DnvuyEim-pCZgP7qpQmJI4lrgI6Sn-jxqTg8q0FUmAkcnQ" - + def verify(self, token): return True diff --git a/nvflare/app_opt/confidential_computing/snp_authorizer.py b/nvflare/app_opt/confidential_computing/snp_authorizer.py index 6f7560267e..ba0650b304 100644 --- a/nvflare/app_opt/confidential_computing/snp_authorizer.py +++ b/nvflare/app_opt/confidential_computing/snp_authorizer.py @@ -12,37 +12,38 @@ # See the License for the specific language governing permissions and # limitations under the License. -import subprocess import base64 -import uuid -import os import logging +import os +import subprocess +import uuid from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer SNP_NAMESPACE = "x-snp" + class SNPAuthorizer(CCAuthorizer): def __init__(self): super().__init__() self.logger = logging.getLogger(self.__class__.__name__) - + def generate(self): - cmd = ['sudo', 'snpguest', 'report', 'report.bin', 'request.bin'] - with open('request.bin', 'wb') as request_file: - request_file.write(b'\x01'*64) + cmd = ["sudo", "snpguest", "report", "report.bin", "request.bin"] + with open("request.bin", "wb") as request_file: + request_file.write(b"\x01" * 64) _ = subprocess.run(cmd, capture_output=True) - with open('report.bin', 'rb') as report_file: + with open("report.bin", "rb") as report_file: token = base64.b64encode(report_file.read()) return token - + def verify(self, token): try: report_bin = base64.b64decode(token) tmp_bin_file = uuid.uuid4().hex - with open(tmp_bin_file, 'wb') as report_file: + with open(tmp_bin_file, "wb") as report_file: report_file.write(report_bin) - cmd = ['snpguest', 'verify', 'attestation', './cert', tmp_bin_file] + cmd = ["snpguest", "verify", "attestation", "./cert", tmp_bin_file] cp = subprocess.run(cmd, capture_output=True) if cp.returncode != 0: return False From 7d9f9ba57c0a3412a6c6ffe567fa2fdabd51d870 Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Wed, 30 Oct 2024 12:32:56 -0700 Subject: [PATCH 6/7] Remove MAA authorizer --- .../confidential_computing/maa_authorizer.py | 65 ------------------- 1 file changed, 65 deletions(-) delete mode 100644 nvflare/app_opt/confidential_computing/maa_authorizer.py diff --git a/nvflare/app_opt/confidential_computing/maa_authorizer.py b/nvflare/app_opt/confidential_computing/maa_authorizer.py deleted file mode 100644 index 2258dd0350..0000000000 --- a/nvflare/app_opt/confidential_computing/maa_authorizer.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. -# -# 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. - -import subprocess - -import jwt -from jwt import PyJWKClient - -from nvflare.app_opt.confidential_computing.cc_authorizer import CCAuthorizer - -MAA_NAMESPACE = "x-ms" -maa_endpoint = "sharedeus2.eus2.attest.azure.net" - - -class MAAAuthorizer(CCAuthorizer): - def generate(self): - cmd = ["sudo", "AttestationClient", "-o", "token"] - cp = subprocess.run(cmd, capture_output=True) - # print(f"{cp.stdout=}\n{cp.stderr=}") - # print(token) - token = cp.stdout - return cp.stdout - - def verify(self, token): - try: - header = jwt.get_unverified_header(token) - # print(f"{header=}") - alg = header.get("alg") - jwks_client = PyJWKClient(f"https://{maa_endpoint}/certs") - signing_key = jwks_client.get_signing_key_from_jwt(token) - claims = jwt.decode(token, signing_key.key, algorithms=[alg]) - if claims: - # print(f"{claims=}") - return True - except: - return False - return True - - def can_generate(self) -> bool: - return True - - def can_verify(self) -> bool: - return True - - def get_namespace(self) -> str: - return MAA_NAMESPACE - - -if __name__ == "__main__": - m = MAAAuthorizer() - token = m.generate() - print(type(token)) - v = m.verify(token) - print(v) From 228cd7ad2a11b6a36ea93a32a875980ad048fb75 Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Mon, 6 Jan 2025 13:59:33 -0800 Subject: [PATCH 7/7] Address PR comments --- nvflare/app_opt/confidential_computing/aci_authorizer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nvflare/app_opt/confidential_computing/aci_authorizer.py b/nvflare/app_opt/confidential_computing/aci_authorizer.py index 818e58e27b..a696eec1d3 100644 --- a/nvflare/app_opt/confidential_computing/aci_authorizer.py +++ b/nvflare/app_opt/confidential_computing/aci_authorizer.py @@ -25,6 +25,10 @@ class ACIAuthorizer(CCAuthorizer): + def __init__(self, retry_count=5, retry_sleep=2): + self.retry_count = retry_count + self.retry_sleep = retry_sleep + def generate(self): count = 0 token = "" @@ -40,9 +44,9 @@ def generate(self): token = r.json().get("token") break except: - if count > 5: + if count > self.retry_count: break - time.sleep(2) + time.sleep(self.retry_sleep) return token def verify(self, token):