├── .gitmodules ├── Makefile.am ├── src ├── Makefile.am ├── x509cert.h ├── tests.h ├── amdcert.h ├── sevcert.h ├── x509cert.cpp ├── crypto.h ├── utilities.h ├── sevcore_win.cpp ├── utilities.cpp ├── sevcore.h ├── commands.h ├── main.cpp ├── amdcert.cpp ├── rmp.h └── sevcore_linux.cpp ├── configure.ac ├── lib └── psp-sev.h ├── deps-install.sh ├── LICENSE └── readme.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # The name of the resulting application after it is build. 2 | bin_PROGRAMS = sevtool 3 | 4 | sevtool_SOURCES = amdcert.cpp commands.cpp crypto.cpp\ 5 | main.cpp sevcert.cpp\ 6 | utilities.cpp tests.cpp x509cert.cpp 7 | if LINUX 8 | sevtool_SOURCES += sevcore_linux.cpp 9 | else 10 | sevtool_SOURCES += sevcore_win.cpp 11 | endif 12 | 13 | # linked libraries 14 | sevtool_LDADD = -lcrypto -lssl -luuid 15 | 16 | # Compilation flags 17 | sevtool_CXXFLAGS = -g -Wall -Wextra -Wconversion -pthread -std=c++11 -I../lib 18 | -------------------------------------------------------------------------------- /src/x509cert.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2020 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef X509CERT_H 18 | #define X509CERT_H 19 | 20 | #include "sevapi.h" 21 | #include 22 | #include 23 | #include 24 | 25 | // Public global functions 26 | void convert_txt_to_der(const std::string in_file_name, const std::string out_file_name); 27 | void convert_der_to_pem(const std::string in_file_name, const std::string out_file_name); 28 | bool read_pem_into_x509(const std::string file_name, X509 **x509_cert); 29 | bool write_x509_pem(const std::string file_name, X509 *x509_cert); 30 | bool x509_validate_signature(X509 *child_cert, X509 *intermediate_cert, X509 *parent_cert); 31 | 32 | #endif /* X509CERT_H */ 33 | -------------------------------------------------------------------------------- /src/tests.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef TESTS_H 18 | #define TESTS_H 19 | 20 | class Tests { 21 | private: 22 | std::string m_output_folder = ""; 23 | int m_verbose_flag = 0; 24 | 25 | bool clear_output_folder(void); 26 | 27 | public: 28 | Tests(std::string output_folder, int verbose_flag); 29 | ~Tests() {}; 30 | 31 | bool test_factory_reset(void); 32 | bool test_platform_status(void); 33 | bool test_pek_gen(void); 34 | bool test_pek_csr(void); 35 | bool test_sign_pek_csr(void); 36 | bool test_pdh_gen(void); 37 | bool test_pdh_cert_export(void); 38 | bool test_pek_cert_import(void); 39 | bool test_get_id(void); 40 | bool test_set_self_owned(void); 41 | bool test_set_externally_owned(void); 42 | bool test_generate_cek_ask(void); 43 | bool test_get_ask_ark(void); 44 | bool test_export_cert_chain(void); 45 | bool test_calc_measurement(void); 46 | bool test_validate_cert_chain(void); 47 | bool test_generate_launch_blob(void); 48 | bool test_package_secret(void); 49 | bool test_export_cert_chain_vcek(void); 50 | bool test_validate_cert_chain_vcek(void); 51 | bool test_all(void); 52 | }; 53 | 54 | #endif /* TESTS_H */ 55 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Init the autoconf process 2 | dnl (Program Name and Version Number) 3 | AC_INIT(sev-tool, 1.7) 4 | 5 | 6 | dnl Safety checks in case user overwritten --srcdir 7 | AC_CONFIG_SRCDIR(./src/sevapi.h) 8 | 9 | dnl Store the auxiliary build tools (e.g., install-sh, config.sub, config.guess) 10 | dnl in this dir (build-aux) 11 | AC_CONFIG_AUX_DIR(build-aux) 12 | 13 | dnl Check that OpenSSL is >= 1.1.0 by looking for version specific APIs 14 | dnl OpenSSL 1.1.1.x uses EVP_PKEY_base_id 15 | dnl OpenSSL 3.0.x renamed to EVP_PKEY_get_base_id 16 | AC_SEARCH_LIBS( 17 | EVP_PKEY_base_id, 18 | crypto, 19 | [], 20 | [AC_SEARCH_LIBS( 21 | EVP_PKEY_get_base_id, 22 | crypto, 23 | [], 24 | [AC_MSG_ERROR( 25 | [Incompatible version of OpenSSL found] 26 | )] 27 | )] 28 | ) 29 | 30 | dnl Commented out because we are currently using sev-tool/lib/psp-sev.h 31 | dnl 32 | dnl Ensure that the SEV header is present in glibc. 33 | dnl AC_CHECK_HEADER(/usr/include/linux/psp-sev.h, [], 34 | dnl [AC_MSG_ERROR([Necessary libraries are missing])]) 35 | 36 | dnl Init automake, and specify this program use relaxed structures. 37 | dnl i.e. this program doesn't follow the gnu coding standards, and doesn't have 38 | dnl ChangeLog, COPYING, AUTHORS, INSTALL, README etc. files. 39 | AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 40 | 41 | AC_CANONICAL_HOST 42 | 43 | build_linux=no 44 | build_windows=no 45 | build_mac=no 46 | 47 | case "${host_os}" in 48 | linux*) 49 | build_linux=yes 50 | ;; 51 | cygwin*|mingw*) 52 | build_windows=yes 53 | ;; 54 | darwin*) 55 | build_mac=yes 56 | ;; 57 | *) 58 | AC_MSG_ERROR(["OS $host_os is not supported"]) 59 | ;; 60 | esac 61 | 62 | AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"]) 63 | AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"]) 64 | AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"]) 65 | 66 | dnl Check for C++ compiler 67 | AC_PROG_CXX 68 | 69 | dnl Tells automake to create a Makefile 70 | dnl See https://www.gnu.org/software/automake/manual/html_node/Requirements.html 71 | AC_CONFIG_FILES([Makefile src/Makefile]) 72 | 73 | dnl Generate the output 74 | AC_OUTPUT 75 | -------------------------------------------------------------------------------- /src/amdcert.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef AMDCERT_H 18 | #define AMDCERT_H 19 | 20 | #include "sevapi.h" 21 | #include "sevcore.h" // for SEVDevice 22 | #include 23 | 24 | constexpr uint32_t AMD_CERT_VERSION = 0x01; 25 | constexpr uint32_t AMD_CERT_ID_SIZE_BYTES = 16; // sizeof(amd_cert:key_id_0 + amd_cert:key_id_1) 26 | constexpr uint32_t AMD_CERT_KEY_BITS_2K = 2048; 27 | constexpr uint32_t AMD_CERT_KEY_BITS_4K = 4096; 28 | constexpr uint32_t AMD_CERT_KEY_BYTES_4K = (AMD_CERT_KEY_BITS_4K/8); 29 | 30 | static constexpr uint8_t amd_root_key_id_naples[AMD_CERT_ID_SIZE_BYTES] = { 31 | 0x1b, 0xb9, 0x87, 0xc3, 0x59, 0x49, 0x46, 0x06, 32 | 0xb1, 0x74, 0x94, 0x56, 0x01, 0xc9, 0xea, 0x5b, 33 | }; 34 | static constexpr uint8_t amd_root_key_id_rome[AMD_CERT_ID_SIZE_BYTES] = { 35 | 0xe6, 0x00, 0x21, 0x22, 0xfb, 0x58, 0x41, 0x93, 36 | 0x99, 0xd1, 0x5f, 0xee, 0x7b, 0x13, 0x13, 0x51 37 | }; 38 | static constexpr uint8_t amd_root_key_id_milan[AMD_CERT_ID_SIZE_BYTES] = { 39 | 0x94, 0xC3, 0x8E, 0x41, 0x77, 0xD0, 0x47, 0x92, 40 | 0x92, 0xA7, 0xAE, 0x67, 0x1D, 0x08, 0x3F, 0xB6 41 | }; 42 | 43 | // Public global functions 44 | static std::string amd_empty = "NULL"; 45 | void print_amd_cert_readable(const amd_cert *cert, std::string &out_str = amd_empty); 46 | void print_amd_cert_hex(const amd_cert *cert, std::string &out_str = amd_empty); 47 | 48 | class AMDCert { 49 | private: 50 | SEVDevice *m_sev_device; 51 | SEV_ERROR_CODE amd_cert_validate_sig(const amd_cert *cert, 52 | const amd_cert *parent, 53 | ePSP_DEVICE_TYPE device_type); 54 | SEV_ERROR_CODE amd_cert_validate_common(const amd_cert *cert); 55 | bool usage_is_valid(AMD_SIG_USAGE usage); 56 | SEV_ERROR_CODE amd_cert_validate(const amd_cert *cert, 57 | const amd_cert *parent, 58 | AMD_SIG_USAGE expected_usage, 59 | ePSP_DEVICE_TYPE device_type); 60 | SEV_ERROR_CODE amd_cert_public_key_hash(const amd_cert *cert, 61 | hmac_sha_256 *hash); 62 | // Retrieves information on device type (naples/rome/milan...) based on key id 63 | ePSP_DEVICE_TYPE get_device_type(const amd_cert *ark); 64 | 65 | public: 66 | AMDCert() {} 67 | ~AMDCert() {}; 68 | 69 | bool key_size_is_valid(size_t size); 70 | SEV_ERROR_CODE amd_cert_validate_ark(const amd_cert *ark); 71 | SEV_ERROR_CODE amd_cert_validate_ask(const amd_cert *ask, 72 | const amd_cert *ark); 73 | size_t amd_cert_get_size(const amd_cert *cert); 74 | SEV_ERROR_CODE amd_cert_export_pub_key(const amd_cert *cert, 75 | sev_cert *pub_key_cert); 76 | SEV_ERROR_CODE amd_cert_init(amd_cert *cert, const uint8_t *buffer); 77 | }; 78 | 79 | #endif /* AMDCERT_H */ 80 | -------------------------------------------------------------------------------- /src/sevcert.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef SEVCERT_H 18 | #define SEVCERT_H 19 | 20 | #include "sevapi.h" 21 | #include 22 | #include 23 | 24 | // Public global functions 25 | static std::string sev_empty = "NULL"; 26 | void print_sev_cert_readable(const sev_cert *cert, 27 | std::string &out_str = sev_empty); 28 | void print_sev_cert_hex(const sev_cert *cert); 29 | void print_cert_chain_buf_readable(const sev_cert_chain_buf *p, 30 | std::string &out_str = sev_empty); 31 | void print_cert_chain_buf_hex(const sev_cert_chain_buf *p); 32 | void read_priv_key_pem_into_rsakey(const std::string file_name, 33 | RSA **rsa_priv_key); 34 | bool read_priv_key_pem_into_eckey(const std::string file_name, 35 | EC_KEY **ec_priv_key); 36 | bool read_priv_key_pem_into_evpkey(const std::string file_name, 37 | EVP_PKEY **evp_priv_key); 38 | bool write_pub_key_pem(const std::string file_name, EVP_PKEY *evp_key_pair); 39 | bool write_priv_key_pem(const std::string file_name, EVP_PKEY *evp_key_pair); 40 | 41 | class SEVCert { 42 | private: 43 | SEV_ERROR_CODE validate_usage(uint32_t Usage); 44 | SEV_ERROR_CODE validate_rsa_pub_key(const sev_cert *cert, 45 | const EVP_PKEY *PublicKey); 46 | SEV_ERROR_CODE validate_public_key(const sev_cert *cert, 47 | const EVP_PKEY *PublicKey); 48 | SEV_ERROR_CODE validate_signature(const sev_cert *child_cert, 49 | const sev_cert *parent_cert, 50 | EVP_PKEY *parent_signing_key); 51 | SEV_ERROR_CODE validate_body(const sev_cert *cert); 52 | 53 | sev_cert *m_child_cert; 54 | 55 | public: 56 | SEVCert(sev_cert *cert) { m_child_cert = cert; } 57 | ~SEVCert() {}; 58 | 59 | const sev_cert *data() { return m_child_cert; } 60 | 61 | bool create_godh_cert(EVP_PKEY **godh_key_pair, 62 | uint8_t api_major, 63 | uint8_t api_minor); 64 | bool create_oca_cert(EVP_PKEY **oca_key_pair, 65 | SEV_SIG_ALGO algo); 66 | bool sign_with_key(uint32_t version, uint32_t pub_key_usage, 67 | uint32_t pub_key_algorithm, EVP_PKEY **priv_key, 68 | uint32_t sig1_usage, const SEV_SIG_ALGO sig1_algo); 69 | SEV_ERROR_CODE compile_public_key_from_certificate(const sev_cert *cert, 70 | EVP_PKEY *evp_pub_key); 71 | SEV_ERROR_CODE decompile_public_key_into_certificate(sev_cert *cert, 72 | EVP_PKEY *evp_pubkey); 73 | SEV_ERROR_CODE verify_sev_cert(const sev_cert *parent_cert1, 74 | const sev_cert *parent_cert2 = NULL); 75 | SEV_ERROR_CODE validate_pek_csr(); 76 | SEV_ERROR_CODE verify_signed_pek_csr(const sev_cert *oca_cert); 77 | }; 78 | 79 | #endif /* SEVCERT_H */ 80 | -------------------------------------------------------------------------------- /src/x509cert.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2020 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #include "utilities.h" 18 | #include "x509cert.h" 19 | #include 20 | #include 21 | #include // memset 22 | #include 23 | #include 24 | #include 25 | 26 | // Print a certificate 27 | // openssl x509 -in certificate.crt -text -noout 28 | 29 | // OpenSSL verify 30 | // openssl verify -trusted ark.pem -untrusted ask.pem vcek.pem 31 | 32 | void convert_txt_to_der(const std::string in_file_name, const std::string out_file_name) 33 | { 34 | std::string cmd = "openssl x509 -outform der -in " + in_file_name + " -out " + out_file_name; 35 | std::string output = ""; 36 | 37 | sev::execute_system_command(cmd, &output); 38 | } 39 | 40 | void convert_der_to_pem(const std::string in_file_name, const std::string out_file_name) 41 | { 42 | std::string cmd = "openssl x509 -inform der -in " + in_file_name + " -out " + out_file_name; 43 | std::string output = ""; 44 | 45 | sev::execute_system_command(cmd, &output); 46 | } 47 | 48 | bool read_pem_into_x509(const std::string file_name, X509 **x509_cert) 49 | { 50 | FILE *pFile = NULL; 51 | pFile = fopen(file_name.c_str(), "re"); 52 | if (!pFile) 53 | return false; 54 | 55 | // printf("Reading from file: %s\n", file_name.c_str()); 56 | *x509_cert = PEM_read_X509(pFile, NULL, NULL, NULL); 57 | if (!x509_cert) { 58 | printf("Error reading x509 from file: %s\n", file_name.c_str()); 59 | fclose(pFile); 60 | return false; 61 | } 62 | fclose(pFile); 63 | return true; 64 | } 65 | 66 | bool write_x509_pem(const std::string file_name, X509 *x509_cert) 67 | { 68 | FILE *pFile = NULL; 69 | pFile = fopen(file_name.c_str(), "wt"); 70 | if (!pFile) 71 | return false; 72 | 73 | // printf("Writing to file: %s\n", file_name.c_str()); 74 | if (PEM_write_X509(pFile, x509_cert) != 1) { 75 | printf("Error writing x509 to file: %s\n", file_name.c_str()); 76 | fclose(pFile); 77 | return false; 78 | } 79 | fclose(pFile); 80 | return true; 81 | } 82 | 83 | bool x509_validate_signature(X509 *child_cert, X509 *intermediate_cert, X509 *parent_cert) 84 | { 85 | bool ret = false; 86 | X509_STORE *store = NULL; 87 | X509_STORE_CTX *store_ctx = NULL; 88 | 89 | do { 90 | // Create the store 91 | store = X509_STORE_new(); 92 | if (!store) 93 | break; 94 | 95 | // Add the parent cert to the store 96 | if (X509_STORE_add_cert(store, parent_cert) != 1) { 97 | printf("Error adding parent_cert to x509_store\n"); 98 | break; 99 | } 100 | 101 | // Add the intermediate cert to the store 102 | if (intermediate_cert) { 103 | if (X509_STORE_add_cert(store, intermediate_cert) != 1) { 104 | printf("Error adding intermediate_cert to x509_store\n"); 105 | break; 106 | } 107 | } 108 | 109 | // Create the store context 110 | store_ctx = X509_STORE_CTX_new(); 111 | if (!store_ctx) { 112 | printf("Error creating x509_store_context\n"); 113 | break; 114 | } 115 | 116 | // Pass the store (parent and intermediate cert) and child cert (that we want to verify) into the store context 117 | if (X509_STORE_CTX_init(store_ctx, store, child_cert, NULL) != 1) { 118 | printf("Error initializing 509_store_context\n"); 119 | break; 120 | } 121 | 122 | // Specify which cert to validate 123 | X509_STORE_CTX_set_cert(store_ctx, child_cert); 124 | 125 | // Verify the certificate 126 | ret = X509_verify_cert(store_ctx); 127 | 128 | // Print out error code 129 | if (ret == 0) 130 | printf("Error verifying cert: %s\n", X509_verify_cert_error_string(X509_STORE_CTX_get_error(store_ctx))); 131 | 132 | if (ret != 1) 133 | break; 134 | 135 | ret = true; 136 | } while (0); 137 | 138 | // Cleanup 139 | if (store_ctx) 140 | X509_STORE_CTX_free(store_ctx); 141 | if (store) 142 | X509_STORE_free(store); 143 | 144 | return ret; 145 | } 146 | -------------------------------------------------------------------------------- /lib/psp-sev.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | /* 3 | * Userspace interface for AMD Secure Encrypted Virtualization (SEV) 4 | * platform management commands. 5 | * 6 | * Copyright (C) 2016-2017 Advanced Micro Devices, Inc. 7 | * 8 | * Author: Brijesh Singh 9 | * 10 | * SEV API specification is available at: https://developer.amd.com/sev/ 11 | */ 12 | 13 | #ifndef __PSP_SEV_USER_H__ 14 | #define __PSP_SEV_USER_H__ 15 | 16 | #include 17 | 18 | /** 19 | * SEV platform commands 20 | */ 21 | enum { 22 | SEV_FACTORY_RESET = 0, 23 | SEV_PLATFORM_STATUS, // 0x1 24 | SEV_PEK_GEN, // 0x2 25 | SEV_PEK_CSR, // 0x3 26 | SEV_PDH_GEN, // 0x4 27 | SEV_PDH_CERT_EXPORT, // 0x5 28 | SEV_PEK_CERT_IMPORT, // 0x6 29 | SEV_GET_ID, /* This command is deprecated, use SEV_GET_ID2 */ 30 | SEV_GET_ID2, // 0x8 31 | 32 | // Subject to change. Current info based on the sev-snp-part2 branch AMDESE/Linux fork. 33 | // https://github.com/AMDESE/linux/blob/sev-snp-part2-v6/include/uapi/linux/psp-sev.h 34 | SEV_SNP_PLATFORM_STATUS, // 0x9 35 | SEV_SNP_SET_EXT_CONFIG, // 0xA 36 | SEV_SNP_GET_EXT_CONFIG, // 0xB 37 | 38 | SEV_MAX, 39 | }; 40 | 41 | /** 42 | * SEV Firmware status code 43 | */ 44 | typedef enum { 45 | SEV_RET_SUCCESS = 0, 46 | SEV_RET_INVALID_PLATFORM_STATE, 47 | SEV_RET_INVALID_GUEST_STATE, 48 | SEV_RET_INAVLID_CONFIG, 49 | SEV_RET_INVALID_LEN, 50 | SEV_RET_ALREADY_OWNED, 51 | SEV_RET_INVALID_CERTIFICATE, 52 | SEV_RET_POLICY_FAILURE, 53 | SEV_RET_INACTIVE, 54 | SEV_RET_INVALID_ADDRESS, 55 | SEV_RET_BAD_SIGNATURE, 56 | SEV_RET_BAD_MEASUREMENT, 57 | SEV_RET_ASID_OWNED, 58 | SEV_RET_INVALID_ASID, 59 | SEV_RET_WBINVD_REQUIRED, 60 | SEV_RET_DFFLUSH_REQUIRED, 61 | SEV_RET_INVALID_GUEST, 62 | SEV_RET_INVALID_COMMAND, 63 | SEV_RET_ACTIVE, 64 | SEV_RET_HWSEV_RET_PLATFORM, 65 | SEV_RET_HWSEV_RET_UNSAFE, 66 | SEV_RET_UNSUPPORTED, 67 | SEV_RET_MAX, 68 | } sev_ret_code; 69 | 70 | /** 71 | * struct sev_user_data_status - PLATFORM_STATUS command parameters 72 | * 73 | * @major: major API version 74 | * @minor: minor API version 75 | * @state: platform state 76 | * @flags: platform config flags 77 | * @build: firmware build id for API version 78 | * @guest_count: number of active guests 79 | */ 80 | struct sev_user_data_status { 81 | __u8 api_major; /* Out */ 82 | __u8 api_minor; /* Out */ 83 | __u8 state; /* Out */ 84 | __u32 flags; /* Out */ 85 | __u8 build; /* Out */ 86 | __u32 guest_count; /* Out */ 87 | } __attribute__((packed)); 88 | 89 | /** 90 | * struct sev_user_data_pek_csr - PEK_CSR command parameters 91 | * 92 | * @address: PEK certificate chain 93 | * @length: length of certificate 94 | */ 95 | struct sev_user_data_pek_csr { 96 | __u64 address; /* In */ 97 | __u32 length; /* In/Out */ 98 | } __attribute__((packed)); 99 | 100 | /** 101 | * struct sev_user_data_cert_import - PEK_CERT_IMPORT command parameters 102 | * 103 | * @pek_address: PEK certificate chain 104 | * @pek_len: length of PEK certificate 105 | * @oca_address: OCA certificate chain 106 | * @oca_len: length of OCA certificate 107 | */ 108 | struct sev_user_data_pek_cert_import { 109 | __u64 pek_cert_address; /* In */ 110 | __u32 pek_cert_len; /* In */ 111 | __u64 oca_cert_address; /* In */ 112 | __u32 oca_cert_len; /* In */ 113 | } __attribute__((packed)); 114 | 115 | /** 116 | * struct sev_user_data_pdh_cert_export - PDH_CERT_EXPORT command parameters 117 | * 118 | * @pdh_address: PDH certificate address 119 | * @pdh_len: length of PDH certificate 120 | * @cert_chain_address: PDH certificate chain 121 | * @cert_chain_len: length of PDH certificate chain 122 | */ 123 | struct sev_user_data_pdh_cert_export { 124 | __u64 pdh_cert_address; /* In */ 125 | __u32 pdh_cert_len; /* In/Out */ 126 | __u64 cert_chain_address; /* In */ 127 | __u32 cert_chain_len; /* In/Out */ 128 | } __attribute__((packed)); 129 | 130 | /** 131 | * struct sev_user_data_get_id - GET_ID command parameters (deprecated) 132 | * 133 | * @socket1: Buffer to pass unique ID of first socket 134 | * @socket2: Buffer to pass unique ID of second socket 135 | */ 136 | struct sev_user_data_get_id { 137 | __u8 socket1[64]; /* Out */ 138 | __u8 socket2[64]; /* Out */ 139 | } __attribute__((packed)); 140 | 141 | /** 142 | * struct sev_user_data_get_id2 - GET_ID command parameters 143 | * @address: Buffer to store unique ID 144 | * @length: length of the unique ID 145 | */ 146 | struct sev_user_data_get_id2 { 147 | __u64 address; /* In */ 148 | __u32 length; /* In/Out */ 149 | } __attribute__((packed)); 150 | 151 | /** 152 | * struct sev_issue_cmd - SEV ioctl parameters 153 | * 154 | * @cmd: SEV commands to execute 155 | * @opaque: pointer to the command structure 156 | * @error: SEV FW return code on failure 157 | */ 158 | struct sev_issue_cmd { 159 | __u32 cmd; /* In */ 160 | __u64 data; /* In */ 161 | __u32 error; /* Out */ 162 | } __attribute__((packed)); 163 | 164 | #define SEV_IOC_TYPE 'S' 165 | #define SEV_ISSUE_CMD _IOWR(SEV_IOC_TYPE, 0x0, struct sev_issue_cmd) 166 | 167 | #endif /* __PSP_USER_SEV_H */ 168 | -------------------------------------------------------------------------------- /src/crypto.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef CRYPTO_H 18 | #define CRYPTO_H 19 | 20 | #include "sevapi.h" 21 | #include "utilities.h" 22 | 23 | #include // memset 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /** 33 | * NIST_KDF 34 | */ 35 | #define NIST_KDF_H_BYTES 32 36 | #define NIST_KDF_H (NIST_KDF_H_BYTES*BITS_PER_BYTE) // 32*8=256 37 | #define NIST_KDF_R sizeof(uint32_t)*BITS_PER_BYTE // 32 38 | 39 | #define SEV_MASTER_SECRET_LABEL "sev-master-secret" 40 | #define SEV_KEK_LABEL "sev-kek" 41 | #define SEV_KIK_LABEL "sev-kik" 42 | #define SEV_CEK_LABEL "sev-chip-endorsement-key" 43 | #define SEV_VCEK_LABEL "sev-versioned-chip-endorsement-key" 44 | 45 | /** 46 | * DIGEST 47 | */ 48 | #define DIGEST_SHA256_SIZE_BYTES (256/8) // 32 49 | #define DIGEST_SHA384_SIZE_BYTES (384/8) // 48 50 | #define DIGEST_SHA512_SIZE_BYTES (512/8) // 64 51 | typedef uint8_t DIGESTSHA256[DIGEST_SHA256_SIZE_BYTES]; 52 | // typedef uint8_t DIGESTSHA384[DIGEST_SHA384_SIZE_BYTES]; 53 | typedef uint8_t DIGESTSHA512[DIGEST_SHA512_SIZE_BYTES]; 54 | 55 | 56 | /** 57 | * ECC 58 | */ 59 | #define ECC_CURVE_SECP256R1_SIZE_BITS 256 60 | #define ECC_CURVE_SECP256R1_SIZE_BYTES (ECC_CURVE_SECP256R1_SIZE_BITS/8) // 32 61 | #define ECC_CURVE_SECP384R1_SIZE_BITS 384 62 | #define ECC_CURVE_SECP384R1_SIZE_BYTES (ECC_CURVE_SECP384R1_SIZE_BITS/8) // 48 63 | 64 | // SEV supported ECC curve size 65 | #define SEV_ECC_CURVE_SIZE_BYTES ECC_CURVE_SECP384R1_SIZE_BYTES 66 | 67 | /** 68 | * For ECC keys generated from extra bits, FIPS 180-4 requires that the 69 | * input contain an additional 64 bits (8 bytes) of random data. 70 | */ 71 | #define ECC_KEYGEN_EXTRA_BITS (64) 72 | #define ECC_KEYGEN_EXTRA_BYTES (ECC_KEYGEN_EXTRA_BITS/8) 73 | 74 | 75 | typedef enum __attribute__((mode(QI))) SHA_TYPE 76 | { 77 | SHA_TYPE_256 = 0, 78 | SHA_TYPE_384 = 1, 79 | } SHA_TYPE; 80 | 81 | // NIST Compliant KDF 82 | bool kdf(uint8_t *key_out, size_t key_out_length, 83 | const uint8_t *key_in, size_t key_in_length, 84 | const uint8_t *label, size_t label_length, 85 | const uint8_t *context, size_t context_length); 86 | 87 | uint8_t *calculate_shared_secret(EVP_PKEY *priv_key, 88 | EVP_PKEY *peer_key, 89 | size_t& shared_key_len_out); 90 | 91 | bool derive_master_secret(aes_128_key master_secret, 92 | EVP_PKEY *godh_priv_key, 93 | const sev_cert *pdh_public, 94 | const uint8_t nonce[sizeof(nonce_128)]); 95 | 96 | bool derive_kek(aes_128_key kek, const aes_128_key master_secret); 97 | bool derive_kik(hmac_key_128 kik, const aes_128_key master_secret); 98 | bool gen_hmac(hmac_sha_256 *out, hmac_key_128 key, uint8_t *msg, size_t msg_len); 99 | 100 | // AES128 encrypt a buffer 101 | bool encrypt(uint8_t *out, const uint8_t *in, size_t length, 102 | const aes_128_key key, const uint8_t iv[128/8]); 103 | 104 | bool generate_ecdh_key_pair(EVP_PKEY **evp_key_pair, SEV_EC curve = SEV_EC_P384); 105 | bool generate_rsa_keypair(EVP_PKEY **evp_key_pair); 106 | 107 | bool digest_sha(const void *msg, size_t msg_len, uint8_t *digest, 108 | size_t digest_len, SHA_TYPE sha_type); 109 | 110 | bool ecdsa_verify(sev_sig *sig, EVP_PKEY **pub_evp_key, uint8_t *digest, size_t length); 111 | 112 | bool sign_message(sev_sig *sig, EVP_PKEY **evp_key_pair, const uint8_t *msg, 113 | size_t length, const SEV_SIG_ALGO algo); 114 | bool verify_message(sev_sig *sig, EVP_PKEY **evp_key_pair, const uint8_t *msg, 115 | size_t length, const SEV_SIG_ALGO algo); 116 | 117 | SEV_ERROR_CODE aes_256_gcm_authenticated_encrypt(const uint8_t *p_key, size_t key_size, 118 | const uint8_t *p_aad, size_t aad_size, 119 | const uint8_t *p_msg, size_t msg_size, 120 | uint8_t *p_out, const uint8_t *p_iv, size_t iv_size, 121 | uint8_t *p_tag); // [out, 16 bytes] 122 | SEV_ERROR_CODE aes_256_gcm_authenticated_decrypt(const uint8_t *p_key, size_t key_size, 123 | const uint8_t *p_aad, size_t aad_size, 124 | const uint8_t *p_msg, size_t msg_size, 125 | uint8_t *p_out, const uint8_t *p_iv, size_t iv_size, 126 | const uint8_t *p_tag); // [in, 16 bytes] 127 | 128 | #endif /* CRYPTO_H */ 129 | -------------------------------------------------------------------------------- /src/utilities.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018-2021 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef UTILITIES_H 18 | #define UTILITIES_H 19 | 20 | #include 21 | 22 | namespace sev 23 | { 24 | #define SEV_DEFAULT_DIR "/usr/psp-sev-assets/" 25 | #define KDS_CERT_SITE "https://kdsintf.amd.com" 26 | #define KDS_DEV_CERT_SITE "https://kdsintfdev.amd.com" 27 | #define KDS_CEK KDS_CERT_SITE "/cek/id/" 28 | #define KDS_VCEK KDS_CERT_SITE "/vcek/v1/" // KDS_VCEK/{product_name}/{hwid}?{tcb parameter list} 29 | #define KDS_VCEK_CERT_CHAIN "cert_chain" // KDS_VCEK/{product_name}/cert_chain 30 | #define KDS_VCEK_CRL "crl" // KDS_VCEK/{product_name}/crl" 31 | 32 | #define PAGE_SIZE 4096 // Todo remove this one? 33 | #define PAGE_SIZE_4K 4096 34 | #define PAGE_SIZE_2M (512*PAGE_SIZE_4K) 35 | 36 | #define IS_ALIGNED(e, x) (0==(((uintptr_t)(e))%(x))) 37 | #define IS_ALIGNED_TO_16_BYTES(e) IS_ALIGNED((e), 16) // 4 bits 38 | #define IS_ALIGNED_TO_32_BYTES(e) IS_ALIGNED((e), 32) // 5 bits 39 | #define IS_ALIGNED_TO_64_BYTES(e) IS_ALIGNED((e), 64) // 6 bits 40 | #define IS_ALIGNED_TO_128_BYTES(e) IS_ALIGNED((e), 128) // 7 bits 41 | #define IS_ALIGNED_TO_4KB(e) IS_ALIGNED((e), 4096) // 12 bits 42 | #define IS_ALIGNED_TO_1MB(e) IS_ALIGNED((e), 0x100000) // 20 bits 43 | #define IS_ALIGNED_TO_2MB(e) IS_ALIGNED((e), 0x200000) // 21 bits 44 | 45 | #define ALIGN_TO_16_BYTES(e) ((((uintptr_t)(e))+0xF)&(~(uintptr_t)0xF)) 46 | #define ALIGN_TO_32_BYTES(e) ((((uintptr_t)(e))+0x1F)&(~(uintptr_t)0x1F)) 47 | #define ALIGN_TO_64_BYTES(e) ((((uintptr_t)(e))+0x3F)&(~(uintptr_t)0x3F)) 48 | 49 | #define BITS_PER_BYTE 8 50 | 51 | static inline void native_cpuid(unsigned int *eax, unsigned int *ebx, 52 | unsigned int *ecx, unsigned int *edx) 53 | { 54 | // ecx is often an input as well as an output. 55 | asm volatile("cpuid" 56 | : "=a" (*eax), 57 | "=b" (*ebx), 58 | "=c" (*ecx), 59 | "=d" (*edx) 60 | : "0" (*eax), "2" (*ecx)); 61 | } 62 | 63 | static inline unsigned int cpuid_ebx(unsigned int op) 64 | { 65 | unsigned int eax = op, ebx = 0, ecx = 0, edx = 0; 66 | 67 | native_cpuid(&eax, &ebx, &ecx, &edx); 68 | return ebx; 69 | } 70 | 71 | /** 72 | * Executes a bash command and returns results as a string 73 | */ 74 | bool execute_system_command(const std::string cmd, std::string *log); 75 | 76 | /** 77 | * Read an entire file in to a buffer, or as much as will fit. 78 | * Return length of file or of buffer, whichever is smaller. 79 | */ 80 | size_t read_file(const std::string file_name, void *buffer, size_t len); 81 | 82 | /** 83 | * Truncate and write (not append) a file from the beginning 84 | * Returns number of bytes written 85 | */ 86 | size_t write_file(const std::string file_name, const void *buffer, size_t len); 87 | 88 | /** 89 | * Returns the file size in number of bytes 90 | * May be used to tell if a file exists 91 | */ 92 | size_t get_file_size(const std::string file_name); 93 | 94 | /** 95 | * Generate some random bytes 96 | */ 97 | void gen_random_bytes(void *bytes, size_t num_bytes); 98 | 99 | /** 100 | * Verify read/write access to an area of memory. 101 | * Used to confirm TMR (trusted memory region) release. 102 | */ 103 | bool verify_access(uint8_t *buf, size_t len); 104 | 105 | /** 106 | * Converts a string of ascii-encoded hex bytes into a Hex array 107 | * Ex. To generate the string, do printf("%02x", myArray) will generate 108 | * "0123456ACF" and this function will put it back into an array 109 | * This function is expecting the input string to be an even number of 110 | * elements not including the null terminator 111 | */ 112 | bool str_to_array(const std::string in_string, uint8_t *array, uint32_t array_size); 113 | 114 | /** 115 | * If you have a buffer (or read in input file) that's in AsciiHexBytes, 116 | * such as the getid output files, this will read it back into a buffer 117 | */ 118 | void ascii_hex_bytes_to_binary(void *out, const char *in_bytes, size_t len); 119 | 120 | /** 121 | * Reverses bytes in a section of memory. Used in validating cert signatures 122 | */ 123 | bool reverse_bytes(uint8_t *bytes, size_t size); 124 | 125 | /** 126 | * Checks if memory is 0. Similar to memcmp but doesn't require a second object 127 | */ 128 | bool is_zero(const uint8_t *ptr, size_t bytes); 129 | } // namespace 130 | 131 | #endif /* UTILITIES_H */ 132 | -------------------------------------------------------------------------------- /src/sevcore_win.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifdef _WIN32 18 | #include "sevcore.h" 19 | #include "utilities.h" 20 | #include 21 | #include // for ioctl() 22 | #include // for mmap() and friends 23 | #include // for errorno 24 | #include // for O_RDWR 25 | #include // for close() 26 | #include // for std::runtime_error() 27 | 28 | SEVDevice::~SEVDevice() 29 | { 30 | if (mFd >= 0) { 31 | close(mFd); 32 | } 33 | mFd = -1; 34 | } 35 | 36 | SEVDevice& SEVDevice::get_sev_device(void) 37 | { 38 | static SEVDevice m_sev_device; 39 | m_sev_device.mFd = open(DEFAULT_SEV_DEVICE, O_RDWR); 40 | if (m_sev_device.mFd < 0) { 41 | throw std::runtime_error("Can't open " + std::string(DEFAULT_SEV_DEVICE) + "!\n"); 42 | } 43 | return m_sev_device; 44 | } 45 | 46 | int SEVDevice::sev_ioctl(int cmd, void *data, int *cmd_ret) 47 | { 48 | int ioctl_ret = -1; 49 | 50 | if (cmd == 0 || data || cmd_ret) { 51 | } 52 | 53 | return ioctl_ret; 54 | } 55 | 56 | int SEVDevice::factory_reset() 57 | { 58 | int cmd_ret = -1; 59 | 60 | return cmd_ret; 61 | } 62 | 63 | int SEVDevice::get_platform_owner(void *data) 64 | { 65 | return 0; 66 | } 67 | 68 | int SEVDevice::get_platform_es(void *data) 69 | { 70 | return 0; 71 | } 72 | 73 | int SEVDevice::platform_status(uint8_t *data) 74 | { 75 | int cmd_ret = -1; 76 | 77 | return cmd_ret; 78 | } 79 | 80 | int SEVDevice::pek_gen() 81 | { 82 | int cmd_ret = -1; 83 | 84 | return cmd_ret; 85 | } 86 | 87 | int SEVDevice::pek_csr(uint8_t *data, void *pek_mem, sev_cert *csr) 88 | { 89 | int cmd_ret = -1; 90 | 91 | return cmd_ret; 92 | } 93 | 94 | int SEVDevice::pdh_gen() 95 | { 96 | int cmd_ret = -1; 97 | 98 | return cmd_ret; 99 | } 100 | 101 | int SEVDevice::pdh_cert_export(uint8_t *data, void *pdh_cert_mem, 102 | void *cert_chain_mem) 103 | { 104 | int cmd_ret = -1; 105 | 106 | return cmd_ret; 107 | } 108 | 109 | // todo. dont want to be reading from a file. use openssl to generate 110 | int SEVDevice::pek_cert_import(uint8_t *data, 111 | sev_cert *pek_csr, 112 | std::string &oca_priv_key_file, 113 | std::string &oca_cert_file) 114 | { 115 | int cmd_ret = -1; 116 | 117 | return cmd_ret; 118 | } 119 | 120 | // Must always pass in 128 bytes array, because of how linux /dev/sev ioctl works 121 | int SEVDevice::get_id(void *data, void *id_mem, uint32_t id_length) 122 | { 123 | int cmd_ret = -1; 124 | 125 | return cmd_ret; 126 | } 127 | 128 | std::string SEVDevice::display_build_info(void) 129 | { 130 | SEVDevice sev_device; 131 | uint8_t status_data[sizeof(sev_platform_status_cmd_buf)]; 132 | sev_platform_status_cmd_buf *status_data_buf = (sev_platform_status_cmd_buf *)&status_data; 133 | int cmd_ret = -1; 134 | 135 | std::string api_major_ver = "API_Major: xxx"; 136 | std::string api_minor_ver = "API_Minor: xxx"; 137 | std::string build_id_ver = "build_id: xxx"; 138 | 139 | cmd_ret = sev_device.platform_status(status_data); 140 | if (cmd_ret != 0) 141 | return ""; 142 | 143 | char major_buf[4], minor_buf[4], build_id_buf[4]; // +1 for Null char 144 | sprintf(major_buf, "%d", status_data_buf->api_major); 145 | sprintf(minor_buf, "%d", status_data_buf->api_minor); 146 | sprintf(build_id_buf, "%d", status_data_buf->build_id); 147 | api_major_ver.replace(11, 3, major_buf); 148 | api_minor_ver.replace(11, 3, minor_buf); 149 | build_id_ver.replace(9, 3, build_id_buf); 150 | 151 | return api_major_ver + ", " + api_minor_ver + ", " + build_id_ver; 152 | } 153 | 154 | int SEVDevice::sys_info() 155 | { 156 | int cmd_ret = 0; 157 | std::string cmd = ""; 158 | std::string output = ""; 159 | 160 | printf("-------------------------System Info-------------------------"); 161 | printf("Coming soon...\n"); 162 | 163 | // Print results of all execute_system_command calls 164 | printf("\n%s", output.c_str()); 165 | 166 | std::string build_info = display_build_info(); 167 | printf("Firmware Version: %s\n", build_info.c_str()); 168 | 169 | printf("-------------------------------------------------------------\n\n"); 170 | 171 | return cmd_ret; 172 | } 173 | 174 | /* 175 | * Note: You can not change the Platform Owner if Guests are running. That means 176 | * the Platform cannot be in the WORKING state here. The ccp Kernel Driver 177 | * will do its best to set the Platform state to whatever is required to 178 | * run each command, but that does not include shutting down Guests to do so. 179 | */ 180 | int SEVDevice::set_self_owned() 181 | { 182 | int cmd_ret = -1; 183 | 184 | return cmd_ret; 185 | } 186 | 187 | #endif 188 | -------------------------------------------------------------------------------- /src/utilities.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #include "utilities.h" 18 | #include 19 | #include // memcpy 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | bool sev::execute_system_command(const std::string cmd, std::string *log) 26 | { 27 | FILE *pipe = popen(cmd.c_str(), "r"); 28 | if (!pipe) { 29 | return false; 30 | } 31 | 32 | while (!feof(pipe)) { 33 | char output[4096]; 34 | size_t count; 35 | if ((count = fread(output, 1, sizeof(output), pipe)) > 0) { 36 | if (log) { 37 | log->append(output, count); 38 | } 39 | } 40 | } 41 | 42 | pclose(pipe); 43 | 44 | return true; 45 | } 46 | 47 | /** 48 | * Read up to len bytes from the beginning of a file 49 | * Returns number of bytes read, or 0 if the file couldn't be opened. 50 | */ 51 | size_t sev::read_file(const std::string file_name, void *buffer, size_t len) 52 | { 53 | std::ifstream file(file_name, std::ios::binary); 54 | if (len > INT_MAX) { 55 | printf("read_file Error: Input length too long\n"); 56 | return 0; 57 | } 58 | std::streamsize slen = (std::streamsize)len; 59 | 60 | if (!file.is_open()) { 61 | printf("read_file Error: Could not open file. " \ 62 | " ensure directory and file exists\n" \ 63 | " file_name: %s\n", file_name.c_str()); 64 | return 0; 65 | } 66 | 67 | file.read((char *)buffer, slen); 68 | size_t count = (size_t)file.gcount(); 69 | file.close(); 70 | 71 | return count; 72 | } 73 | 74 | /** 75 | * Writes len bytes from the beginning of a file. Does NOT append 76 | * Returns number of bytes written, or 0 if the file couldn't be opened. 77 | * ostream CANNOT create a folder, so it has to exist already, to succeed 78 | */ 79 | size_t sev::write_file(const std::string file_name, const void *buffer, size_t len) 80 | { 81 | std::ofstream file(file_name, std::ofstream::out); 82 | if (len > INT_MAX) { 83 | printf("write_file Error: Input length too long\n"); 84 | return 0; 85 | } 86 | std::streamsize slen = (std::streamsize)len; 87 | 88 | if (!file.is_open()) { 89 | printf("write_file Error: Could not open/create file. " \ 90 | "Ensure directory exists\n" \ 91 | " Filename: %s\n", file_name.c_str()); 92 | return 0; 93 | } 94 | printf("Writing to file: %s\n", file_name.c_str()); 95 | 96 | file.write((char *)buffer, slen); 97 | size_t count = (size_t)file.tellp(); 98 | file.close(); 99 | 100 | return count; 101 | } 102 | 103 | /** 104 | * Returns the file size in number of bytes 105 | * May be used to tell if a file exists 106 | */ 107 | size_t sev::get_file_size(const std::string file_name) 108 | { 109 | std::ifstream file(file_name, std::ios::binary | std::ios::ate); 110 | 111 | if (!file.is_open()) { 112 | return 0; 113 | } 114 | 115 | size_t count = (size_t)file.tellg(); 116 | file.close(); 117 | 118 | return count; 119 | } 120 | 121 | void sev::gen_random_bytes(void *bytes, size_t num_bytes) 122 | { 123 | ssize_t num_gen_bytes = 0; 124 | num_gen_bytes = getrandom(bytes, num_bytes, 0); 125 | if (num_gen_bytes != (ssize_t)num_bytes) { 126 | printf("Warning: getrandom failed. Generating random bytes manually\n"); 127 | // Do it manually 128 | uint8_t *addr = (uint8_t *)bytes; 129 | srand((unsigned int)time(NULL)); 130 | while (num_bytes--) { 131 | *addr++ = (uint8_t)(rand() & 0xff); 132 | } 133 | } 134 | } 135 | 136 | bool sev::verify_access(uint8_t *buf, size_t len) 137 | { 138 | uint8_t *master = new uint8_t[len]; 139 | gen_random_bytes(master, len); 140 | memcpy(buf, master, len); 141 | bool ret = memcmp(buf, master, len) == 0; 142 | delete[] master; 143 | return ret; 144 | } 145 | 146 | bool sev::str_to_array(const std::string in_string, uint8_t *array, 147 | uint32_t array_size) 148 | { 149 | std::string substring = ""; 150 | 151 | if (array_size < in_string.size() / 2) { 152 | return false; 153 | } 154 | 155 | for (size_t i = 0; i < in_string.size()/2; i++) { 156 | substring = in_string.substr(i*2, 2); 157 | array[i] = (uint8_t)strtol(substring.c_str(), NULL, 16); 158 | } 159 | 160 | // printf("\nSTRING TO ARRAY: "); 161 | // for (size_t i = 0; i < array_size; i++) { 162 | // printf("%02x", array[i]); 163 | // } 164 | // printf("\n"); 165 | 166 | return true; 167 | } 168 | 169 | void sev::ascii_hex_bytes_to_binary(void *out, const char *in_bytes, size_t len) 170 | { 171 | std::string temp; 172 | 173 | for (size_t i = 0; i < len; i++) { 174 | temp = {in_bytes[i*2], in_bytes[(i*2)+1], '\0'}; 175 | ((uint8_t *)out)[i] = (uint8_t)stoi(temp, NULL, 16); 176 | } 177 | } 178 | 179 | bool sev::reverse_bytes(uint8_t *bytes, size_t size) 180 | { 181 | uint8_t *start = bytes; 182 | uint8_t *end = bytes + size - 1; 183 | 184 | if (!bytes) 185 | return false; 186 | 187 | while (start < end) { 188 | uint8_t byte = *start; 189 | *start = *end; 190 | *end = byte; 191 | start++; 192 | end--; 193 | } 194 | 195 | return true; 196 | } 197 | 198 | bool sev::is_zero(const uint8_t *ptr, size_t bytes) 199 | { 200 | uint8_t val = 0; 201 | for (size_t i = 0; i < bytes; i++) { 202 | val |= ptr[i]; 203 | } 204 | return (val == 0); 205 | } 206 | -------------------------------------------------------------------------------- /src/sevcore.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef SEVCORE_H 18 | #define SEVCORE_H 19 | 20 | #include "rmp.h" 21 | #include "sevapi.h" 22 | #include "sevcert.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | const std::string DEFAULT_SEV_DEVICE = "/dev/sev"; 31 | 32 | #define AMD_SEV_DEVELOPER_SITE "https://developer.amd.com/sev/" 33 | #define ASK_ARK_PATH_SITE "https://download.amd.com/developer/eula/sev/" 34 | 35 | const std::string ASK_ARK_NAPLES_FILE = "ask_ark_naples.cert"; 36 | const std::string ASK_ARK_ROME_FILE = "ask_ark_rome.cert"; 37 | const std::string ASK_ARK_MILAN_FILE = "ask_ark_milan.cert"; 38 | const std::string ASK_ARK_NAPLES_SITE = ASK_ARK_PATH_SITE + ASK_ARK_NAPLES_FILE; 39 | const std::string ASK_ARK_ROME_SITE = ASK_ARK_PATH_SITE + ASK_ARK_ROME_FILE; 40 | const std::string ASK_ARK_MILAN_SITE = ASK_ARK_PATH_SITE + ASK_ARK_MILAN_FILE; 41 | 42 | constexpr uint32_t NAPLES_FAMILY = 0x17UL; // 23 43 | constexpr uint32_t NAPLES_MODEL_LOW = 0x00UL; 44 | constexpr uint32_t NAPLES_MODEL_HIGH = 0x0FUL; 45 | constexpr uint32_t ROME_FAMILY = 0x17UL; // 23 46 | constexpr uint32_t ROME_MODEL_LOW = 0x30UL; 47 | constexpr uint32_t ROME_MODEL_HIGH = 0x3FUL; 48 | constexpr uint32_t MILAN_FAMILY = 0x19UL; // 25 49 | constexpr uint32_t MILAN_MODEL_LOW = 0x00UL; 50 | constexpr uint32_t MILAN_MODEL_HIGH = 0x0FUL; 51 | 52 | enum __attribute__((mode(QI))) ePSP_DEVICE_TYPE { 53 | PSP_DEVICE_TYPE_INVALID = 0, 54 | PSP_DEVICE_TYPE_NAPLES = 1, 55 | PSP_DEVICE_TYPE_ROME = 2, 56 | PSP_DEVICE_TYPE_MILAN = 3, 57 | }; 58 | 59 | /** 60 | * A system physical address that should always be invalid. 61 | * Used to test the SEV FW detects such invalid addresses and returns the 62 | * correct error return value. 63 | */ 64 | constexpr uint64_t INVALID_ADDRESS = (0xFFF00000018); // Needs to be bigger than 0xFFCFFFFFFFF (16TB memory) 65 | constexpr uint32_t BAD_ASID = ((uint32_t)~0); 66 | constexpr uint32_t BAD_DEVICE_TYPE = ((uint32_t)~0); 67 | constexpr uint32_t BAD_FAMILY_MODEL = ((uint32_t)~0); 68 | 69 | // Platform Status Buffer flags param was split up into owner/ES in API v0.17 70 | constexpr uint8_t PLAT_STAT_OWNER_OFFSET = 0; 71 | constexpr uint8_t PLAT_STAT_CONFIGES_OFFSET = 8; 72 | constexpr uint32_t PLAT_STAT_OWNER_MASK = (1U << PLAT_STAT_OWNER_OFFSET); 73 | constexpr uint32_t PLAT_STAT_ES_MASK = (1U << PLAT_STAT_CONFIGES_OFFSET); 74 | 75 | namespace sev 76 | { 77 | // Global Functions that don't require ioctls 78 | void get_family_model(uint32_t *family, uint32_t *model); 79 | ePSP_DEVICE_TYPE get_device_type(void); 80 | bool min_api_version(unsigned platform_major, unsigned platform_minor, 81 | unsigned api_major, unsigned api_minor); 82 | int get_ask_ark(const std::string output_folder, const std::string cert_file); 83 | int get_ask_ark_pem(const std::string output_folder, const std::string cert_chain_file, 84 | const std::string ask_file, const std::string ark_file); 85 | int zip_certs(const std::string output_folder, const std::string zip_name, 86 | const std::string files_to_zip); 87 | } // namespace 88 | 89 | // Class to access the special SEV FW API test suite driver. 90 | class SEVDevice { 91 | private: 92 | int mFd; 93 | 94 | inline int get_fd(void) { return mFd; } 95 | int sev_ioctl(int cmd, void *data, int *cmd_ret); 96 | 97 | std::string display_build_info(void); 98 | 99 | // Do NOT create ANY other constructors or destructors of any kind. 100 | SEVDevice(void) = default; 101 | 102 | // Delete the copy and assignment operators which 103 | // may be automatically created by the compiler. The user 104 | // should not be able to modify the SEVDevice, as it is unique. 105 | SEVDevice(const SEVDevice&) = delete; 106 | SEVDevice& operator=(const SEVDevice&) = delete; 107 | 108 | public: 109 | // Singleton Constructor - Threadsafe in C++ 11 and greater. 110 | static SEVDevice& get_sev_device(void); 111 | 112 | // Do NOT create ANY other constructors or destructors of any kind. 113 | ~SEVDevice(void); 114 | 115 | /* 116 | * Format for below input variables: 117 | * data is a uint8_t pointer to an empty buffer the size of the cmd_buffer 118 | * All other variables are specific input/output variables for that command 119 | * Each function sets the params in data to the input/output variables of 120 | * the function 121 | */ 122 | int factory_reset(void); 123 | int platform_status(uint8_t *data); 124 | int pek_gen(void); 125 | int pek_csr(uint8_t *data, void *pek_mem, sev_cert *csr); 126 | int pdh_gen(void); 127 | int pdh_cert_export(uint8_t *data, void *pdh_cert_mem, 128 | void *cert_chain_mem); 129 | int pek_cert_import(uint8_t *data, sev_cert *pek_csr, 130 | sev_cert *oca_cert); 131 | int get_id(void *data, void *id_mem, uint32_t id_length = 0); 132 | 133 | int sys_info(); 134 | int set_self_owned(void); 135 | int get_platform_owner(void *data); 136 | int get_platform_es(void *data); 137 | int generate_cek_ask(const std::string output_folder, 138 | const std::string cert_file); 139 | int generate_vcek_ask(const std::string output_folder, 140 | const std::string vcek_der_file, 141 | const std::string vcek_pem_file); 142 | int request_platform_status(snp_platform_status_buffer *plat_status); 143 | void request_tcb_data(snp_tcb_version &tcb_data); 144 | }; 145 | 146 | #endif /* SEVCORE_H */ 147 | -------------------------------------------------------------------------------- /src/commands.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef COMMANDS_H 18 | #define COMMANDS_H 19 | 20 | #include "sevapi.h" // for hmac_sha_256, nonce_128, aes_128_key 21 | #include "sevcore.h" // for SEVDevice 22 | #include // for EVP_PKEY 23 | #include // for SHA256_DIGEST_LENGTH 24 | #include 25 | 26 | const std::string PDH_FILENAME = "pdh.cert"; // PDH signed by PEK 27 | const std::string PDH_READABLE_FILENAME = "pdh_readable.txt"; 28 | const std::string PEK_FILENAME = "pek.cert"; // PEK signed by CEK 29 | const std::string PEK_READABLE_FILENAME = "pek_readable.txt"; 30 | const std::string OCA_FILENAME = "oca.cert"; // OCA signed by P.O. 31 | const std::string OCA_READABLE_FILENAME = "oca_readable.cert"; 32 | const std::string CEK_FILENAME = "cek.cert"; // CEK signed by ASK 33 | const std::string CEK_READABLE_FILENAME = "cek_readable.cert"; 34 | const std::string ASK_FILENAME = "ask.cert"; // ASK signed by ARK 35 | const std::string ASK_READABLE_FILENAME = "ask_readable.cert"; 36 | const std::string ARK_FILENAME = "ark.cert"; // ARK self-signed 37 | const std::string ARK_READABLE_FILENAME = "ark_readable.cert"; 38 | 39 | const std::string VCEK_DER_FILENAME = "vcek.der"; 40 | const std::string VCEK_PEM_FILENAME = "vcek.pem"; 41 | const std::string VCEK_CERT_CHAIN_PEM_FILENAME = "cert_chain.pem"; 42 | const std::string VCEK_ASK_PEM_FILENAME = "ask.pem"; 43 | const std::string VCEK_ARK_PEM_FILENAME = "ark.pem"; 44 | 45 | const std::string CERTS_ZIP_FILENAME = "certs_export"; // export_cert_chain 46 | const std::string CERTS_VCEK_ZIP_FILENAME = "certs_export_vcek"; // export_cert_chain_vcek 47 | const std::string ASK_ARK_FILENAME = "ask_ark.cert"; // get_ask_ark 48 | const std::string PEK_CSR_HEX_FILENAME = "pek_csr.cert"; // pek_csr 49 | const std::string PEK_CSR_READABLE_FILENAME = "pek_csr_readable.txt"; // pek_csr 50 | const std::string SIGNED_PEK_CSR_FILENAME = "pek_csr.signed.cert"; // sign_pek_csr 51 | const std::string CERT_CHAIN_HEX_FILENAME = "cert_chain.cert"; // pdh_cert_export 52 | const std::string CERT_CHAIN_READABLE_FILENAME = "cert_chain_readable.txt"; // pdh_cert_export 53 | const std::string GET_ID_S0_FILENAME = "getid_s0_out.txt"; // get_id 54 | const std::string GET_ID_S1_FILENAME = "getid_s1_out.txt"; // get_id 55 | const std::string CALC_MEASUREMENT_READABLE_FILENAME = "calc_measurement_out.txt"; // calc_measurement 56 | const std::string CALC_MEASUREMENT_FILENAME = "calc_measurement_out.bin"; // calc_measurement 57 | const std::string LAUNCH_BLOB_FILENAME = "launch_blob.bin"; // generate_launch_blob 58 | const std::string GUEST_OWNER_DH_FILENAME = "godh.cert"; // generate_launch_blob 59 | const std::string GUEST_TK_FILENAME = "tmp_tk.bin"; // generate_launch_blob 60 | const std::string SECRET_FILENAME = "secret.txt"; // package_secret 61 | const std::string PACKAGED_SECRET_FILENAME = "packaged_secret.bin"; // package_secret 62 | const std::string PACKAGED_SECRET_HEADER_FILENAME = "packaged_secret_header.bin";// package_secret 63 | const std::string ATTESTATION_REPORT_FILENAME = "attestation_report.bin"; // validate_attestation 64 | const std::string GUEST_REPORT_FILENAME = "guest_report.bin"; // validate_guest_report 65 | 66 | constexpr uint32_t BITS_PER_BYTE = 8; 67 | constexpr uint32_t NIST_KDF_H_BYTES = 32; 68 | constexpr uint32_t NIST_KDF_H = (NIST_KDF_H_BYTES*BITS_PER_BYTE); // 32*8=256 69 | constexpr uint32_t NIST_KDF_R = sizeof(uint32_t)*BITS_PER_BYTE; // 32 70 | 71 | constexpr uint8_t SEV_MASTER_SECRET_LABEL[] = "sev-master-secret"; 72 | constexpr uint8_t SEV_KEK_LABEL[] = "sev-kek"; 73 | constexpr uint8_t SEV_KIK_LABEL[] = "sev-kik"; 74 | 75 | constexpr auto LAUNCH_MEASURE_CTX = 0x4; 76 | 77 | struct measurement_t { 78 | uint8_t meas_ctx; // LAUNCH_MEASURE_CTX 79 | uint8_t api_major; 80 | uint8_t api_minor; 81 | uint8_t build_id; 82 | uint32_t policy; // SEV_POLICY 83 | uint8_t digest[SHA256_DIGEST_LENGTH]; // gctx_ld 84 | nonce_128 mnonce; 85 | aes_128_key tik; 86 | }; 87 | 88 | enum ccp_required_t { 89 | CCP_REQ = 0, 90 | CCP_NOT_REQ = 1, 91 | }; 92 | 93 | class Command { 94 | private: 95 | SEVDevice *m_sev_device; 96 | tek_tik m_tk; // Unencrypted TIK/TEK. wrap_tk is this enc with KEK 97 | hmac_sha_256 m_measurement; // Measurement. Used in LaunchSecret header HMAC 98 | std::string m_output_folder = ""; 99 | int m_verbose_flag = 0; 100 | 101 | int calculate_measurement(measurement_t *user_data, hmac_sha_256 *final_meas); 102 | int generate_all_certs(void); 103 | int generate_all_certs_vcek(void); 104 | int import_all_certs(sev_cert *pdh, sev_cert *pek, sev_cert *oca, 105 | sev_cert *cek, amd_cert *ask, amd_cert *ark); 106 | bool kdf(uint8_t *key_out, size_t key_out_length, const uint8_t *key_in, 107 | size_t key_in_length, const uint8_t *label, size_t label_length, 108 | const uint8_t *context, size_t context_length); 109 | uint8_t *calculate_shared_secret(EVP_PKEY *priv_key, EVP_PKEY *peer_key, 110 | size_t& shared_key_len_out); 111 | bool derive_master_secret(aes_128_key master_secret, 112 | EVP_PKEY *godh_priv_key, 113 | const sev_cert *pdh_public, 114 | const uint8_t nonce[sizeof(nonce_128)]); 115 | bool derive_kek(aes_128_key kek, const aes_128_key master_secret); 116 | bool derive_kik(hmac_key_128 kik, const aes_128_key master_secret); 117 | bool gen_hmac(hmac_sha_256 *out, hmac_key_128 key, uint8_t *msg, size_t msg_len); 118 | bool encrypt(uint8_t *out, const uint8_t *in, size_t length, 119 | const aes_128_key Key, const uint8_t IV[128/8]); 120 | int build_session_buffer(sev_session_buf *buf, uint32_t guest_policy, 121 | EVP_PKEY *godh_priv_key, sev_cert *pdh_pub); 122 | int encrypt_with_tek(uint8_t *encrypted_mem, const uint8_t *secret_mem, 123 | size_t secret_mem_size, const iv_128 iv); 124 | bool create_launch_secret_header(sev_hdr_buf *out_header, iv_128 *iv, 125 | uint8_t *buf, size_t buffer_len, 126 | uint32_t hdr_flags, uint8_t api_major, 127 | uint8_t api_minor); 128 | 129 | public: 130 | Command(); 131 | Command(std::string output_folder, int verbose_flag, ccp_required_t ccp = CCP_REQ); 132 | ~Command(); 133 | 134 | int factory_reset(void); 135 | int platform_status(void); 136 | int pek_gen(void); 137 | int pek_csr(void); 138 | int pdh_gen(void); 139 | int pdh_cert_export(void); 140 | int pek_cert_import(std::string signed_pek_csr_file, std::string oca_cert_file); 141 | int get_id(void); 142 | 143 | // Non-ioctl (custom) commands 144 | int sys_info(void); 145 | int get_platform_owner(void); 146 | int get_platform_es(void); 147 | int sign_pek_csr(std::string pek_csr_file, std::string oca_priv_key_file); 148 | int set_self_owned(void); 149 | int set_externally_owned(std::string oca_priv_key_file); 150 | int generate_cek_ask(void); 151 | int get_ask_ark(void); 152 | int export_cert_chain(void); 153 | int export_cert_chain_vcek(void); 154 | int calc_measurement(measurement_t *user_data); 155 | int validate_cert_chain(void); 156 | int generate_launch_blob(uint32_t policy); 157 | int package_secret(void); 158 | int validate_attestation(void); 159 | int validate_guest_report(void); 160 | int validate_cert_chain_vcek(void); 161 | }; 162 | 163 | #endif /* COMMANDS_H */ 164 | -------------------------------------------------------------------------------- /deps-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to build AMD SEV Tool 3 | 4 | ############################################################################### 5 | # Copyright 2022 Advanced Micro Devices, Inc. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | ############################################################################### 19 | 20 | NEED_DEPS=0 21 | INSTALLER="" 22 | SSL_DEV="" 23 | AUTO_CONF="autoconf" 24 | UUID_NAME="libuuid-devel" 25 | 26 | # Set to 1 to enable debugging or pass -d or --debug. 27 | if [ "$(echo "${1}" | grep -E '^\-{0,2}d(ebug)?$')" != "" ] 28 | then 29 | DEBUGGING=1 30 | else 31 | DEBUGGING=0 32 | fi 33 | 34 | if [ ${DEBUGGING} -eq 1 ] 35 | then 36 | debug() 37 | { 38 | echo -n "[ DEBUG ] LINE " 39 | echo "$@" 40 | } 41 | else 42 | debug() 43 | { 44 | echo -n "" 45 | } 46 | fi 47 | 48 | find_distribution_details() 49 | { 50 | OS_RELEASE=$(cat /etc/os-release) 51 | debug "${LINENO}" ":" "OS_RELEASE => " "${OS_RELEASE}" 52 | 53 | # Regular Expression to read /etc/os-release for major distributions. 54 | ID_RE='ID\(\_LIKE\)\?\=\"\?[[:alpha:]]\+\([[:space:]][[:alpha:]]\*\)\?\"\?' 55 | debug "${LINENO}" ":" "ID_RE => ${ID_RE}" 56 | 57 | # Read /etc/os-release to find the distribution base. 58 | DIST_BASE=$(grep "${ID_RE}" /etc/os-release) 59 | debug "${LINENO}" ":" "DIST_BASE =>" "${DIST_BASE}" 60 | 61 | if [ "$(echo "${DIST_BASE}" | grep 'suse')" != "" ] || 62 | [ "$(echo "${DIST_BASE}" | grep 'sles')" != "" ] 63 | then 64 | # Cover all SLE and openSUSE distributions. 65 | debug "${LINENO}" ":" "Distribution recognized as SUSE based" 66 | 67 | INSTALLER="zypper" 68 | SSL_DEV="libopenssl-devel" 69 | GCC_CPP="gcc-c++" 70 | elif [ "$(echo "${DIST_BASE}" | grep 'debian')" != "" ] || 71 | [ "$(echo "${DIST_BASE}" | grep 'ubuntu')" != "" ] 72 | then 73 | # Cover all Debian and Ubuntu based distributions. 74 | debug "${LINENO}" ":" "Distribution recognized as Debian or Ubuntu based" 75 | 76 | INSTALLER="apt-get" 77 | SSL_DEV="libssl-dev" 78 | UUID_NAME="uuid-dev" 79 | GCC_CPP="g++" 80 | elif [ "$(echo "${DIST_BASE}" | grep 'fedora')" != "" ] || 81 | [ "$(echo "${DIST_BASE}" | grep 'rhel')" != "" ] 82 | then 83 | # Cover all Redhat based distributions. 84 | debug "${LINENO}" ":" "Distribution recognized as Fedora or Rhel based" 85 | 86 | INSTALLER="yum" 87 | SSL_DEV="openssl-devel" 88 | GCC_CPP="gcc-c++" 89 | else 90 | debug "${LINENO}" ":" "Regular expression could not match: \n" "${OS_RELEASE}" 91 | echo "Distribution not recognized. Please manually install "\ 92 | "libelf libraries, make, zip, gcc, g++, git, wget, automake, autoconf, and libssl-dev." >&2 93 | exit 1 94 | fi 95 | 96 | debug "${LINENO}" ":" "INSTALLER => ${INSTALLER}" 97 | debug "${LINENO}" ":" "SSL_DEV => ${SSL_DEV}" 98 | debug "${LINENO}" ":" "GCC_CPP => ${GCC_CPP}" 99 | debug "${LINENO}" ":" "UUID_NAME => ${UUID_NAME}" 100 | } 101 | 102 | older_than_bionic() 103 | { 104 | VERSION_NUMBER=$(grep 'VERSION_ID=' /etc/os-release | sed s%[^0-9]%%ig) 105 | IS_OLDER=0 106 | 107 | debug "${LINENO}" ":" "VERSION_NUMBER => ${VERSION_NUMBER}" 108 | 109 | if [ "${VERSION_NUMBER}" -lt 1804 ] 110 | then 111 | IS_OLDER=1 112 | fi 113 | 114 | debug "${LINENO}" ":" "IS_OLDER => ${IS_OLDER}" 115 | echo "${IS_OLDER}" 116 | } 117 | 118 | check_dependencies() 119 | { 120 | debug "${LINENO}" ":" "Checking for dependencies..." 121 | 122 | # Check for all required dependancies 123 | if [ "${INSTALLER}" = "zypper" ] || [ "${INSTALLER}" = "yum" ] 124 | then 125 | if [ "$(rpm -q 'git' 2>&1 | grep 'not installed')" != "" ] || 126 | [ "$(rpm -q 'make' 2>&1 | grep 'not installed')" != "" ] || 127 | [ "$(rpm -q 'gcc' 2>&1 | grep 'not installed')" != "" ] || 128 | [ "$(rpm -q 'zip' 2>&1 | grep 'not installed')" != "" ] || 129 | [ "$(rpm -q 'wget' 2>&1 | grep 'not installed')" != "" ] || 130 | [ "$(rpm -q ${UUID_NAME} 2>&1 | grep 'not installed')" != "" ] || 131 | [ "$(rpm -q 'automake' 2>&1 | grep 'not installed')" != "" ] || 132 | [ "$(rpm -q ${AUTO_CONF} 2>&1 | grep 'not installed')" != "" ] || 133 | [ "$(rpm -q ${SSL_DEV} 2>&1 | grep 'not installed')" != "" ] || 134 | [ "$(rpm -q ${GCC_CPP} 2>&1 | grep 'not installed')" != "" ] 135 | then 136 | debug "${LINENO}" ":" "A dependency is missing, setting flag!" 137 | NEED_DEPS=1 138 | fi 139 | elif [ "${INSTALLER}" = "apt-get" ] 140 | then 141 | 142 | if [ "$(older_than_bionic)" = "1" ] 143 | then 144 | AUTO_CONF="autoreconf" 145 | fi 146 | 147 | if [ "$(dpkg -l 'git' 2>&1 | grep 'no packages')" != "" ] || 148 | [ "$(dpkg -l 'make' 2>&1 | grep 'no packages')" != "" ] || 149 | [ "$(dpkg -l 'gcc' 2>&1 | grep 'no packages')" != "" ] || 150 | [ "$(dpkg -l 'zip' 2>&1 | grep 'no packages')" != "" ] || 151 | [ "$(dpkg -l 'wget' 2>&1 | grep 'no packages')" != "" ] || 152 | [ "$(dpkg -l ${UUID_NAME} 2>&1 | grep 'no packages')" != "" ] || 153 | [ "$(dpkg -l 'automake' 2>&1 | grep 'no packages')" != "" ] || 154 | [ "$(dpkg -l ${AUTO_CONF} 2>&1 | grep 'no packages')" != "" ] || 155 | [ "$(dpkg -l ${SSL_DEV} 2>&1 | grep 'no packages')" != "" ] || 156 | [ "$(dpkg -l ${GCC_CPP} 2>&1 | grep 'no packages')" != "" ] 157 | then 158 | debug "${LINENO}" ":" "A dependency is missing, setting flag!" 159 | NEED_DEPS=1 160 | fi 161 | fi 162 | } 163 | 164 | fcomp() 165 | { 166 | debug "${LINENO}" ":" "Attempting to determine which version number is greater." 167 | RETVAL=0 168 | 169 | if [ -n "${1}" ] && [ -n "${2}" ] 170 | then 171 | debug "${LINENO}" ":" "Both arguments are non-zero values." 172 | debug "${LINENO}" ":" "\${1} (SYSTEM_SSL_VERSION) => ${1}" 173 | debug "${LINENO}" ":" "\${2} (ACCEPTED_SSL_VERSION) => ${2}" 174 | 175 | if [ "${1%.*}" = "${2%.*}" ] && [ ! "${1#*.}" \< "${2#*.}" ] 176 | then 177 | debug "${LINENO}" ":" "The system SSL version is new enough to use." 178 | RETVAL=1 179 | elif [ "${1%.*}" \> "${2%.*}" ] 180 | then 181 | debug "${LINENO}" ":" "The system SSL version is new enough to use." 182 | RETVAL=1 183 | fi 184 | else 185 | debug "${LINENO}" ":" "An error occured while attempting to parse the SSL version number." 186 | debug "${LINENO}" ":" "\${1} (SYSTEM_SSL_VERSION) => ${1}" 187 | debug "${LINENO}" ":" "\${2} (ACCEPTED_SSL_VERSIPN) => ${2}" 188 | fi 189 | 190 | return ${RETVAL} 191 | } 192 | 193 | check_ssl() 194 | { 195 | SSL_VERSION="1.1.0j" 196 | ACCEPTED_SSL_VERSION="1.1.0" 197 | ACCEPTED_SSL_VER_TRUNK="${ACCEPTED_SSL_VERSION:0:3}" 198 | SYSTEM_SSL_VERSION="$(openssl version | awk '{print $2}' | sed 's/[a-zA-Z-]//g')" 199 | SYSTEM_SSL_VER_TRUNK="${SYSTEM_SSL_VERSION:0:3}" 200 | 201 | debug "${LINENO}" ":" "ACCEPTED_SSL_VER_TRUNK => ${ACCEPTED_SSL_VER_TRUNK}" 202 | debug "${LINENO}" ":" "SYSTEM_SSL_VER_TRUNK => ${SYSTEM_SSL_VER_TRUNK}" 203 | 204 | CURRENT_DIR=$(pwd) 205 | 206 | debug "${LINENO}" ":" "CURRENT_DIR => ${CURRENT_DIR}" 207 | fcomp "${SYSTEM_SSL_VER_TRUNK}" "${ACCEPTED_SSL_VER_TRUNK}" 208 | FCOMP_RETURN=$? 209 | 210 | if [ "${FCOMP_RETURN}" = "0" ] && 211 | [ ! -d ./openssl/ ] 212 | then 213 | debug "${LINENO}" ":" "Local directory of openssl not detected..." 214 | echo "Your version of openssl is not new enough!" 215 | echo "Would you like to build a self-contained instance of the required openssl version" 216 | printf "(internet connection required)? [y/N] " 217 | read -r ssl_response 218 | 219 | case ${ssl_response} in 220 | [yY]*) 221 | debug "${LINENO}" ":" "User responded with YES." 222 | 223 | echo "Downloading, compiling, and building against openssl version ${SSL_VERSION}" 224 | 225 | # Download an acceptable version of openssl 226 | wget https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz 227 | 228 | # create openssl directory 229 | mkdir -p openssl 230 | 231 | # Extract the tarball. 232 | tar -xf openssl-${SSL_VERSION}.tar.gz -C openssl --strip-components 1 233 | 234 | # Removing the tarball. 235 | rm -f openssl-${SSL_VERSION}.tar.gz 236 | 237 | # Enter the openssl directory, and build the library. 238 | cd openssl/ || exit 239 | ./config 240 | make -j64 241 | 242 | cd "${CURRENT_DIR}" || exit 243 | 244 | # Remove system ssl libraries from src Makefile.am 245 | sed -i 's/^\# linked.*$//g' src/Makefile.am 246 | sed -i 's/^sevtool_LDADD.*$//g' src/Makefile.am 247 | 248 | # Add local ssl libraries to the src Makefile.am 249 | echo "SSL_DIR=../openssl" >> src/Makefile.am 250 | echo "sevtool_LDADD = \$(SSL_DIR)/libcrypto.a -ldl -luuid" >> src/Makefile.am 251 | echo "sevtool_CXXFLAGS += -isystem \$(SSL_DIR)/include -isystem \$(SSL_DIR)" >> src/Makefile.am 252 | ;; 253 | *) 254 | debug "${LINENO}" ":" "User responded with no." 255 | echo "You will need to make sure you manually install all required dependencies." 256 | ;; 257 | esac 258 | elif [ "${FCOMP_RETURN}" = "0" ] && 259 | [ -d ./openssl/ ] 260 | then 261 | debug "${LINENO}" ":" "Local directory of openssl detected..." 262 | echo "Your version of openssl is not new enough!" 263 | printf "Would you like to locally compile and build against the appropriate version? [y/N] " 264 | read -r ssl_response 265 | 266 | case ${ssl_response} in 267 | [yY]*) 268 | # Enter the openssl directory, and rebuild the library. 269 | cd openssl/ || exit 270 | ./config 271 | make -j64 272 | 273 | # No adjustments to the automake file should be necessary as they were already done once. 274 | 275 | cd "${CURRENT_DIR}" || exit 276 | ;; 277 | *) 278 | debug "${LINENO}" ":" "User responded with no." 279 | echo "You will need to make sure you manually install all required dependencies." 280 | ;; 281 | esac 282 | else 283 | debug "${LINENO}" ":" "Proper version of openssl detected as system install." 284 | fi 285 | } 286 | 287 | main() 288 | { 289 | find_distribution_details 290 | check_dependencies 291 | 292 | # Install dependencies if they are needed. 293 | if [ ${NEED_DEPS} -eq 1 ] 294 | then 295 | echo "One or more required software dependencies are missing on your system." 296 | printf "Would you like to have them automatically installed? [y/N] " 297 | read -r response 298 | 299 | case ${response} in 300 | [yY]*) 301 | debug "${LINENO}" ":" "User responded with YES." 302 | debug "${LINENO}" ":" "Running Command: \"sudo ${INSTALLER} install -y git make gcc "\ 303 | "zip wget ${UUID_NAME} automake ${AUTO_CONF} ${SSL_DEV} ${GCC_CPP}\"" 304 | sudo ${INSTALLER} install -y git make gcc zip wget ${UUID_NAME} automake ${AUTO_CONF} ${SSL_DEV} ${GCC_CPP} 305 | ;; 306 | *) 307 | debug "${LINENO}" ":" "User responded with no." 308 | echo "You will need to make sure you manually install all required dependencies." 309 | ;; 310 | esac 311 | fi 312 | 313 | check_ssl 314 | 315 | echo "Once all dependencies are met, you should be able to run \"autoreconf -if && ./configure && make\" to compile the sevtool." 316 | 317 | } 318 | 319 | main 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #include "commands.h" // has measurement_t 18 | #include "tests.h" // for test_all 19 | #include "utilities.h" // for str_to_array 20 | #include // for getopt_long 21 | #include 22 | #include 23 | 24 | const char help_array[] = "The following commands are supported:\n" \ 25 | " sevtool -[global opts] --[command] [command opts]\n" \ 26 | "(Please see the readme file for more detailed information)\n" \ 27 | "Platform Owner commands:\n" \ 28 | " factory_reset\n" \ 29 | " platform_status\n" \ 30 | " pek_gen\n" \ 31 | " pek_csr\n" \ 32 | " pdh_gen\n" \ 33 | " pdh_cert_export\n" \ 34 | " pek_cert_import\n" \ 35 | " Input params:\n" \ 36 | " pek_csr.signed.cert file\n" \ 37 | " oca.cert file\n" \ 38 | " get_id\n" \ 39 | " sign_pek_csr\n" \ 40 | " Input params:\n" \ 41 | " pek_csr.cert file\n" \ 42 | " [oca private key].pem file\n" \ 43 | " set_self_owned\n" \ 44 | " set_externally_owned\n" \ 45 | " Input params:\n" \ 46 | " [oca private key].pem file\n" \ 47 | " generate_cek_ask\n" \ 48 | " get_ask_ark\n" \ 49 | " export_cert_chain\n" \ 50 | "Guest Owner commands:\n" \ 51 | " calc_measurement\n" \ 52 | " Input params (all in ascii-encoded hex bytes):\n" \ 53 | " uint8_t meas_ctx\n" \ 54 | " uint8_t api_major\n" \ 55 | " uint8_t api_minor\n" \ 56 | " uint8_t build_id\n" \ 57 | " uint32_t policy\n" \ 58 | " uint32_t digest\n" \ 59 | " uint8_t m_nonce[128/8]\n" \ 60 | " uint8_t gctx_tik[128/8]\n" \ 61 | " validate_cert_chain\n" \ 62 | " generate_launch_blob\n" \ 63 | " Input params:\n" \ 64 | " uint32_t policy\n" \ 65 | " package_secret\n" \ 66 | " validate_attestation\n" \ 67 | " validate_guest_report\n" \ 68 | " validate_cert_chain_vcek\n" \ 69 | " export_cert_chain_vcek\n" \ 70 | ; 71 | 72 | /* Flag set by '--verbose' */ 73 | static int verbose_flag = 0; 74 | 75 | static struct option long_options[] = 76 | { 77 | /* These options set a flag. */ 78 | {"verbose", no_argument, &verbose_flag, 1}, 79 | {"brief", no_argument, &verbose_flag, 0}, 80 | 81 | /* These options don't set a flag. We distinguish them by their indices. */ 82 | /* Platform Owner commands */ 83 | {"factory_reset", no_argument, 0, 'a'}, 84 | {"platform_status", no_argument, 0, 'b'}, 85 | {"pek_gen", no_argument, 0, 'c'}, 86 | {"pek_csr", no_argument, 0, 'd'}, 87 | {"pdh_gen", no_argument, 0, 'e'}, 88 | {"pdh_cert_export", no_argument, 0, 'f'}, 89 | {"pek_cert_import", required_argument, 0, 'g'}, 90 | {"get_id", no_argument, 0, 'j'}, 91 | {"set_self_owned", no_argument, 0, 'k'}, 92 | {"set_externally_owned", required_argument, 0, 'l'}, 93 | {"generate_cek_ask", no_argument, 0, 'm'}, 94 | {"export_cert_chain", no_argument, 0, 'p'}, 95 | {"export_cert_chain_vcek", no_argument, 0, 'q'}, 96 | {"sign_pek_csr", required_argument, 0, 's'}, 97 | /* Guest Owner commands */ 98 | {"get_ask_ark", no_argument, 0, 'n'}, 99 | {"calc_measurement", required_argument, 0, 't'}, 100 | {"validate_cert_chain", no_argument, 0, 'u'}, 101 | {"generate_launch_blob", required_argument, 0, 'v'}, 102 | {"package_secret", no_argument, 0, 'w'}, 103 | {"validate_attestation", no_argument, 0, 'x'}, // SEV attestation command 104 | {"validate_guest_report", no_argument, 0, 'y'}, // SNP GuestRequest ReportRequest 105 | {"validate_cert_chain_vcek", no_argument, 0, 'z'}, 106 | 107 | /* Run tests */ 108 | {"test_all", no_argument, 0, 'T'}, 109 | 110 | {"help", no_argument, 0, 'H'}, 111 | {"sys_info", no_argument, 0, 'I'}, 112 | {"ofolder", required_argument, 0, 'O'}, 113 | {0, 0, 0, 0} 114 | }; 115 | 116 | int main(int argc, char **argv) 117 | { 118 | int c = 0; 119 | int option_index = 0; /* getopt_long stores the option index here. */ 120 | std::string output_folder = "./"; 121 | 122 | int cmd_ret = 0xFFFF; 123 | 124 | while ((c = getopt_long (argc, argv, "hio:", long_options, &option_index)) != -1) 125 | { 126 | switch (c) { 127 | case 'h': // help 128 | case 'H': { 129 | printf("%s\n", help_array); 130 | cmd_ret = 0; 131 | break; 132 | } 133 | case 'i': // sys_info 134 | case 'I': { 135 | Command cmd(output_folder, verbose_flag); 136 | cmd_ret = cmd.sys_info(); // Display system info 137 | break; 138 | } 139 | case 'o': // ofolder 140 | case 'O': { 141 | output_folder = optarg; 142 | output_folder += "/"; 143 | 144 | // Check that output folder exists, and immediately stop if not 145 | std::string cmd = "if test -d " + output_folder + " ; then echo \"exist\"; else echo \"no\"; fi"; 146 | std::string output = ""; 147 | if (!sev::execute_system_command(cmd, &output)) { 148 | printf("Error. Output directory %s existance check failed.\n", output_folder.c_str()); 149 | return false; 150 | } 151 | 152 | if (strncmp(output.c_str(), "exists", 2) != 0) { 153 | printf("Error. Output directory %s does not exist. " \ 154 | "Please manually create it and try again\n", output_folder.c_str()); 155 | return false; 156 | } 157 | 158 | break; 159 | } 160 | case 'a': { // PLATFORM_RESET 161 | Command cmd(output_folder, verbose_flag); 162 | cmd_ret = cmd.factory_reset(); 163 | break; 164 | } 165 | case 'b': { // PLATFORM_STATUS 166 | Command cmd(output_folder, verbose_flag); 167 | cmd_ret = cmd.platform_status(); 168 | break; 169 | } 170 | case 'c': { // PEK_GEN 171 | Command cmd(output_folder, verbose_flag); 172 | cmd_ret = cmd.pek_gen(); 173 | break; 174 | } 175 | case 'd': { // PEK_CSR 176 | Command cmd(output_folder, verbose_flag); 177 | cmd_ret = cmd.pek_csr(); 178 | break; 179 | } 180 | case 'e': { // PDH_GEN 181 | Command cmd(output_folder, verbose_flag); 182 | cmd_ret = cmd.pdh_gen(); 183 | break; 184 | } 185 | case 'f': { // PDH_CERT_EXPORT 186 | Command cmd(output_folder, verbose_flag); 187 | cmd_ret = cmd.pdh_cert_export(); 188 | break; 189 | } 190 | case 'g': { // PEK_CERT_IMPORT 191 | optind--; // Can't use option_index because it doesn't account for '-' flags 192 | if (argc - optind != 2) { 193 | printf("Error: Expecting exactly 2 args for pek_cert_import\n"); 194 | return false; 195 | } 196 | std::string signed_pek_csr_file = argv[optind++]; 197 | std::string oca_cert_file = argv[optind++]; 198 | 199 | Command cmd(output_folder, verbose_flag); 200 | cmd_ret = cmd.pek_cert_import(signed_pek_csr_file, oca_cert_file); 201 | break; 202 | } 203 | case 'j': { // GET_ID 204 | Command cmd(output_folder, verbose_flag); 205 | cmd_ret = cmd.get_id(); 206 | break; 207 | } 208 | case 'k': { // SET_SELF_OWNED 209 | Command cmd(output_folder, verbose_flag); 210 | cmd_ret = cmd.set_self_owned(); 211 | break; 212 | } 213 | case 'l': { // SET_EXTERNALLY_OWNED 214 | optind--; // Can't use option_index because it doesn't account for '-' flags 215 | if (argc - optind != 1) { 216 | printf("Error: Expecting exactly 1 arg for set_externally_owned\n"); 217 | return false; 218 | } 219 | 220 | std::string oca_priv_key_file = argv[optind++]; 221 | Command cmd(output_folder, verbose_flag); 222 | cmd_ret = cmd.set_externally_owned(oca_priv_key_file); 223 | break; 224 | } 225 | case 'm': { // GENERATE_CEK_ASK 226 | Command cmd(output_folder, verbose_flag); 227 | cmd_ret = cmd.generate_cek_ask(); 228 | break; 229 | } 230 | case 'n': { // GET_ASK_ARK 231 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 232 | cmd_ret = cmd.get_ask_ark(); 233 | break; 234 | } 235 | case 'p': { // EXPORT_CERT_CHAIN 236 | Command cmd(output_folder, verbose_flag); 237 | cmd_ret = cmd.export_cert_chain(); 238 | break; 239 | } 240 | case 'q': { // EXPORT_CERT_CHAIN_VCEK 241 | Command cmd(output_folder, verbose_flag); 242 | cmd_ret = cmd.export_cert_chain_vcek(); 243 | break; 244 | } 245 | case 's': { // SIGN_PEK_CSR 246 | optind--; 247 | if (argc - optind != 2) { 248 | printf("Error: Expecting exactly 2 args for pek_cert_import\n"); 249 | return false; 250 | } 251 | std::string pek_csr_file = argv[optind++]; 252 | std::string oca_priv_key_file = argv[optind++]; 253 | 254 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 255 | cmd_ret = cmd.sign_pek_csr(pek_csr_file, oca_priv_key_file); 256 | break; 257 | } 258 | case 't': { // CALC_MEASUREMENT 259 | optind--; // Can't use option_index because it doesn't account for '-' flags 260 | if (argc - optind != 8) { 261 | printf("Error: Expecting exactly 8 args for calc_measurement\n"); 262 | return false; 263 | } 264 | 265 | measurement_t user_data; 266 | user_data.meas_ctx = (uint8_t)strtol(argv[optind++], NULL, 16); 267 | user_data.api_major = (uint8_t)strtol(argv[optind++], NULL, 16); 268 | user_data.api_minor = (uint8_t)strtol(argv[optind++], NULL, 16); 269 | user_data.build_id = (uint8_t)strtol(argv[optind++], NULL, 16); 270 | user_data.policy = (uint32_t)strtol(argv[optind++], NULL, 16); 271 | sev::str_to_array(std::string(argv[optind++]), (uint8_t *)&user_data.digest, sizeof(user_data.digest)); 272 | sev::str_to_array(std::string(argv[optind++]), (uint8_t *)&user_data.mnonce, sizeof(user_data.mnonce)); 273 | sev::str_to_array(std::string(argv[optind++]), (uint8_t *)&user_data.tik, sizeof(user_data.tik)); 274 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 275 | cmd_ret = cmd.calc_measurement(&user_data); 276 | break; 277 | } 278 | case 'u': { // VALIDATE_CERT_CHAIN 279 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 280 | cmd_ret = cmd.validate_cert_chain(); 281 | break; 282 | } 283 | case 'v': { // GENERATE_LAUNCH_BLOB 284 | optind--; // Can't use option_index because it doesn't account for '-' flags 285 | if (argc - optind != 1) { 286 | printf("Error: Expecting exactly 1 arg for generate_launch_blob\n"); 287 | return false; 288 | } 289 | 290 | uint32_t guest_policy = (uint8_t)strtol(argv[optind++], NULL, 16); 291 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 292 | cmd_ret = cmd.generate_launch_blob(guest_policy); 293 | break; 294 | } 295 | case 'w': { // PACKAGE_SECRET 296 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 297 | cmd_ret = cmd.package_secret(); 298 | break; 299 | } 300 | case 'x': { // VALIDATE_ATTESTATION 301 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 302 | cmd_ret = cmd.validate_attestation(); 303 | break; 304 | } 305 | case 'y': { // VALIDATE_GUEST_REPORT 306 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 307 | cmd_ret = cmd.validate_guest_report(); 308 | break; 309 | } 310 | case 'z': { // VALIDATE_CERT_CHAIN_VCEK 311 | Command cmd(output_folder, verbose_flag, CCP_NOT_REQ); 312 | cmd_ret = cmd.validate_cert_chain_vcek(); 313 | break; 314 | } 315 | case 'T': { // Run Tests 316 | Tests test(output_folder, verbose_flag); 317 | cmd_ret = (test.test_all() == 0); // 0 = fail, 1 = pass 318 | break; 319 | } 320 | case 0: 321 | case 1 : { 322 | // Verbose/brief 323 | break; 324 | } 325 | default: { 326 | fprintf(stderr, "Unrecognised option: -%c\n", optopt); 327 | return false; 328 | } 329 | } 330 | } 331 | 332 | if (cmd_ret == 0) { 333 | printf("\nCommand Successful\n"); 334 | } 335 | else if (cmd_ret == 0xFFFF) { 336 | printf("\nCommand not supported/recognized. Possibly bad formatting\n"); 337 | } 338 | else { 339 | printf("\nCommand Unsuccessful: 0x%02x\n", cmd_ret); 340 | } 341 | 342 | return 0; 343 | } 344 | -------------------------------------------------------------------------------- /src/amdcert.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #include "amdcert.h" 18 | #include "crypto.h" 19 | #include "utilities.h" // reverse_bytes 20 | #include // memset 21 | #include // SHA256_CTX 22 | 23 | /** 24 | * If out_str is passed in, fill up the string, else prints to std::out 25 | */ 26 | void print_amd_cert_readable(const amd_cert *cert, std::string &out_str) 27 | { 28 | char out[sizeof(amd_cert)*3+500]; // 2 chars per byte + 1 space + ~500 extra chars for text 29 | 30 | sprintf(out, "%-15s%08x\n", "Version:", cert->version); // uint32_t 31 | sprintf(out+strlen(out), "%-15s%016lx\n", "key_id_0:", cert->key_id_0); // uint64_t 32 | sprintf(out+strlen(out), "%-15s%016lx\n", "key_id_1:", cert->key_id_1); // uint64_t 33 | sprintf(out+strlen(out), "%-15s%016lx\n", "certifying_id_0:", cert->certifying_id_0); // uint64_t 34 | sprintf(out+strlen(out), "%-15s%016lx\n", "certifying_id_1:", cert->certifying_id_1); // uint64_t 35 | sprintf(out+strlen(out), "%-15s%08x\n", "key_usage:", cert->key_usage); // uint32_t 36 | sprintf(out+strlen(out), "%-15s%016lx\n", "reserved_0:", cert->reserved_0); // uint64_t 37 | sprintf(out+strlen(out), "%-15s%016lx\n", "reserved_1:", cert->reserved_1); // uint64_t 38 | sprintf(out+strlen(out), "%-15s%08x\n", "pub_exp_size:", cert->pub_exp_size); // uint32_t 39 | sprintf(out+strlen(out), "%-15s%08x\n", "modulus_size:", cert->modulus_size); // uint32_t 40 | sprintf(out+strlen(out), "\nPubExp:\n"); 41 | for (size_t i = 0; i < (size_t)(cert->pub_exp_size/8); i++) { // bytes to uint8 42 | sprintf(out+strlen(out), "%02X ", ((uint8_t *)&cert->pub_exp)[i] ); 43 | } 44 | sprintf(out+strlen(out), "\nModulus:\n"); 45 | for (size_t i = 0; i < (size_t)(cert->modulus_size/8); i++) { // bytes to uint8 46 | sprintf(out+strlen(out), "%02X ", ((uint8_t *)&cert->modulus)[i] ); 47 | } 48 | sprintf(out+strlen(out), "\nSig:\n"); 49 | for (size_t i = 0; i < (size_t)(cert->modulus_size/8); i++) { // bytes to uint8 50 | sprintf(out+strlen(out), "%02X ", ((uint8_t *)&cert->sig)[i] ); 51 | } 52 | sprintf(out+strlen(out), "\n"); 53 | 54 | if (out_str == "NULL") { 55 | printf("%s\n", out); 56 | } 57 | else { 58 | out_str += out; 59 | } 60 | } 61 | 62 | /** 63 | * AMD Certs are unions because key sizes can be different. So, you can't just 64 | * print out the memory, you need to print each parameter based off its correct 65 | * size 66 | * Note: there are no spaces in this printout because this function is also used 67 | * to write to a .cert file, not just printing to the screen 68 | */ 69 | void print_amd_cert_hex(const amd_cert *cert, std::string &out_str) 70 | { 71 | char out[sizeof(amd_cert)*2]; // 2 chars per byte 72 | size_t fixed_offset = offsetof(amd_cert, pub_exp); // 64 bytes 73 | 74 | out[0] = '\0'; // Gotta get the sprintf started 75 | 76 | // Print fixed parameters 77 | for (size_t i = 0; i < fixed_offset; i++) { 78 | sprintf(out+strlen(out), "%02X", ((uint8_t *)&cert->version)[i] ); 79 | } 80 | // Print pub_exp 81 | for (size_t i = 0; i < (size_t)(cert->pub_exp_size/8); i++) { // bytes to uint8 82 | sprintf(out+strlen(out), "%02X", ((uint8_t *)&cert->pub_exp)[i] ); 83 | } 84 | // Print nModulus 85 | for (size_t i = 0; i < (size_t)(cert->modulus_size/8); i++) { // bytes to uint8 86 | sprintf(out+strlen(out), "%02X", ((uint8_t *)&cert->modulus)[i] ); 87 | } 88 | // Print Sig 89 | for (size_t i = 0; i < (size_t)(cert->modulus_size/8); i++) { // bytes to uint8 90 | sprintf(out+strlen(out), "%02X", ((uint8_t *)&cert->sig)[i] ); 91 | } 92 | 93 | if (out_str == "NULL") { 94 | printf("%s\n\n\n", out); 95 | } 96 | else { 97 | out_str += out; 98 | } 99 | } 100 | 101 | // Obtain information on device type from provided Root certificate. 102 | ePSP_DEVICE_TYPE AMDCert::get_device_type(const amd_cert *ark) 103 | { 104 | ePSP_DEVICE_TYPE ret = PSP_DEVICE_TYPE_INVALID; 105 | if(!ark) return ret; 106 | if(memcmp(&ark->key_id_0, amd_root_key_id_rome, sizeof(ark->key_id_0 + ark->key_id_1)) == 0) { 107 | return PSP_DEVICE_TYPE_ROME; 108 | } 109 | if(memcmp(&ark->key_id_0, amd_root_key_id_naples, sizeof(ark->key_id_0 + ark->key_id_1)) == 0) { 110 | return PSP_DEVICE_TYPE_NAPLES; 111 | } 112 | if(memcmp(&ark->key_id_0, amd_root_key_id_milan, sizeof(ark->key_id_0 + ark->key_id_1)) == 0) { 113 | return PSP_DEVICE_TYPE_MILAN; 114 | } 115 | return ret; 116 | } 117 | 118 | /** 119 | * This function takes Bits, NOT Bytes 120 | */ 121 | bool AMDCert::key_size_is_valid(size_t size) 122 | { 123 | return (size == AMD_CERT_KEY_BITS_2K) || (size == AMD_CERT_KEY_BITS_4K); 124 | } 125 | 126 | SEV_ERROR_CODE AMDCert::amd_cert_validate_sig(const amd_cert *cert, 127 | const amd_cert *parent, 128 | ePSP_DEVICE_TYPE device_type) 129 | { 130 | SEV_ERROR_CODE cmd_ret = ERROR_INVALID_CERTIFICATE; 131 | hmac_sha_256 sha_digest_256; 132 | hmac_sha_512 sha_digest_384; 133 | SHA_TYPE algo = SHA_TYPE_256; 134 | uint8_t *sha_digest = NULL; 135 | size_t sha_length = 0; 136 | 137 | RSA *rsa_pub_key = NULL; 138 | BIGNUM *modulus = NULL; 139 | BIGNUM *pub_exp = NULL; 140 | EVP_MD_CTX* md_ctx = NULL; 141 | uint32_t sig_len = cert->modulus_size/8; 142 | 143 | uint32_t digest_len = 0; 144 | uint8_t decrypted[AMD_CERT_KEY_BYTES_4K] = {0}; // TODO wrong length 145 | uint8_t signature[AMD_CERT_KEY_BYTES_4K] = {0}; 146 | uint32_t fixed_offset = offsetof(amd_cert, pub_exp); // 64 bytes 147 | 148 | do { 149 | if (!cert || !parent) { 150 | cmd_ret = ERROR_INVALID_PARAM; 151 | break; 152 | } 153 | 154 | // Set SHA_TYPE to 256 bit or 384 bit depending on device_type 155 | if (device_type == PSP_DEVICE_TYPE_NAPLES) { 156 | algo = SHA_TYPE_256; 157 | sha_digest = sha_digest_256; 158 | sha_length = sizeof(hmac_sha_256); 159 | } 160 | else /*if (ROME/MILAN)*/ { 161 | algo = SHA_TYPE_384; 162 | sha_digest = sha_digest_384; 163 | sha_length = sizeof(hmac_sha_512); 164 | } 165 | 166 | // Memzero all the buffers 167 | memset(sha_digest, 0, sha_length); 168 | memset(decrypted, 0, sizeof(decrypted)); 169 | memset(signature, 0, sizeof(signature)); 170 | 171 | // New up the RSA key 172 | rsa_pub_key = RSA_new(); 173 | 174 | // Convert the parent to an RSA key to pass into RSA_verify 175 | modulus = BN_lebin2bn((uint8_t *)&parent->modulus, parent->modulus_size/8, NULL); // n // New's up BigNum 176 | pub_exp = BN_lebin2bn((uint8_t *)&parent->pub_exp, parent->pub_exp_size/8, NULL); // e 177 | if (RSA_set0_key(rsa_pub_key, modulus, pub_exp, NULL) != 1) 178 | break; 179 | 180 | md_ctx = EVP_MD_CTX_create(); 181 | if (EVP_DigestInit(md_ctx, (algo == SHA_TYPE_256) ? EVP_sha256() : EVP_sha384()) <= 0) 182 | break; 183 | if (EVP_DigestUpdate(md_ctx, cert, fixed_offset) <= 0) // Calls SHA256_UPDATE 184 | break; 185 | if (EVP_DigestUpdate(md_ctx, &cert->pub_exp, cert->pub_exp_size/8) <= 0) 186 | break; 187 | if (EVP_DigestUpdate(md_ctx, &cert->modulus, cert->modulus_size/8) <= 0) 188 | break; 189 | EVP_DigestFinal(md_ctx, sha_digest, &digest_len); 190 | 191 | // Swap the bytes of the signature 192 | memcpy(signature, &cert->sig, parent->modulus_size/8); 193 | if (!sev::reverse_bytes(signature, parent->modulus_size/8)) 194 | break; 195 | 196 | // Now we will verify the signature. Start by a RAW decrypt of the signature 197 | if (RSA_public_decrypt(sig_len, signature, decrypted, rsa_pub_key, RSA_NO_PADDING) == -1) 198 | break; 199 | 200 | // Verify the data 201 | // SLen of -2 means salt length is recovered from the signature 202 | if (RSA_verify_PKCS1_PSS(rsa_pub_key, sha_digest, 203 | (algo == SHA_TYPE_256) ? EVP_sha256() : EVP_sha384(), 204 | decrypted, -2) != 1) 205 | { 206 | break; 207 | } 208 | 209 | cmd_ret = STATUS_SUCCESS; 210 | } while (0); 211 | 212 | // Free the keys and contexts 213 | if (rsa_pub_key) 214 | RSA_free(rsa_pub_key); 215 | 216 | if (md_ctx) 217 | EVP_MD_CTX_free(md_ctx); 218 | 219 | return cmd_ret; 220 | } 221 | 222 | SEV_ERROR_CODE AMDCert::amd_cert_validate_common(const amd_cert *cert) 223 | { 224 | SEV_ERROR_CODE cmd_ret = STATUS_SUCCESS; 225 | 226 | do { 227 | if (!cert) { 228 | cmd_ret = ERROR_INVALID_PARAM; 229 | break; 230 | } 231 | 232 | if (cert->version != AMD_CERT_VERSION || 233 | !key_size_is_valid(cert->modulus_size) || // bits 234 | !key_size_is_valid(cert->pub_exp_size)) // bits 235 | { 236 | cmd_ret = ERROR_INVALID_CERTIFICATE; 237 | } 238 | } while (0); 239 | 240 | return cmd_ret; 241 | } 242 | 243 | bool AMDCert::usage_is_valid(AMD_SIG_USAGE usage) 244 | { 245 | return (usage == AMD_USAGE_ARK) || (usage == AMD_USAGE_ASK); // ARK, ASK 246 | } 247 | 248 | SEV_ERROR_CODE AMDCert::amd_cert_validate(const amd_cert *cert, 249 | const amd_cert *parent, 250 | AMD_SIG_USAGE expected_usage, 251 | ePSP_DEVICE_TYPE device_type) 252 | { 253 | SEV_ERROR_CODE cmd_ret = STATUS_SUCCESS; 254 | const uint8_t *key_id = NULL; 255 | 256 | do { 257 | if (!cert || !usage_is_valid(expected_usage)) { 258 | cmd_ret = ERROR_INVALID_PARAM; 259 | break; 260 | } 261 | 262 | // Validate the signature before using any certificate fields 263 | if (parent) { 264 | cmd_ret = amd_cert_validate_sig(cert, parent, device_type); 265 | if (cmd_ret != STATUS_SUCCESS) 266 | break; 267 | } 268 | 269 | // Validate the fixed data 270 | cmd_ret = amd_cert_validate_common(cert); 271 | if (cmd_ret != STATUS_SUCCESS) 272 | break; 273 | 274 | // If there is no parent, then the certificate must be self-certified 275 | key_id = parent ? (uint8_t *)&parent->key_id_0 : (uint8_t *)&cert->key_id_0; 276 | 277 | if (cert->key_usage != expected_usage || 278 | memcmp(&cert->certifying_id_0, key_id, sizeof(cert->certifying_id_0 + cert->certifying_id_1)) != 0) 279 | { 280 | cmd_ret = ERROR_INVALID_CERTIFICATE; 281 | } 282 | } while (0); 283 | 284 | return cmd_ret; 285 | } 286 | 287 | SEV_ERROR_CODE AMDCert::amd_cert_public_key_hash(const amd_cert *cert, 288 | hmac_sha_256 *hash) 289 | { 290 | SEV_ERROR_CODE cmd_ret = ERROR_INVALID_CERTIFICATE; 291 | hmac_sha_256 tmp_hash; 292 | // size_t hash_size = sizeof(tmp_hash); 293 | SHA256_CTX ctx; 294 | uint32_t fixed_offset = offsetof(amd_cert, pub_exp); // 64 bytes 295 | 296 | do { 297 | if (!cert || !hash) { 298 | cmd_ret = ERROR_INVALID_PARAM; 299 | break; 300 | } 301 | 302 | memset(&tmp_hash, 0, sizeof(tmp_hash)); 303 | 304 | // Calculate the hash of the public key 305 | if (SHA256_Init(&ctx) != 1) 306 | break; 307 | 308 | if (SHA256_Update(&ctx, cert, fixed_offset) != 1) 309 | break; 310 | 311 | if (SHA256_Update(&ctx, &cert->pub_exp, cert->pub_exp_size/8) != 1) 312 | break; 313 | 314 | if (SHA256_Update(&ctx, &cert->modulus, cert->modulus_size/8) != 1) 315 | break; 316 | 317 | if (SHA256_Final((uint8_t *)&tmp_hash, &ctx) != 1) 318 | break; 319 | 320 | // Copy the hash to the output 321 | memcpy(hash, &tmp_hash, sizeof(hmac_sha_256)); 322 | 323 | cmd_ret = STATUS_SUCCESS; 324 | } while (0); 325 | 326 | return cmd_ret; 327 | } 328 | 329 | SEV_ERROR_CODE AMDCert::amd_cert_validate_ark(const amd_cert *ark) 330 | { 331 | SEV_ERROR_CODE cmd_ret = STATUS_SUCCESS; 332 | hmac_sha_256 hash; 333 | hmac_sha_256 fused_hash; 334 | const uint8_t *amd_root_key_id = NULL; 335 | ePSP_DEVICE_TYPE device_type = get_device_type(ark); 336 | 337 | do { 338 | if (!ark) { 339 | cmd_ret = ERROR_INVALID_PARAM; 340 | break; 341 | } 342 | 343 | memset(&hash, 0, sizeof(hash)); 344 | memset(&fused_hash, 0, sizeof(fused_hash)); 345 | 346 | // Validate the certificate. Check for self-signed ARK 347 | cmd_ret = amd_cert_validate(ark, ark, AMD_USAGE_ARK, device_type); // Rome 348 | if (cmd_ret != STATUS_SUCCESS) { 349 | // Not a self-signed ARK. Check the ARK without a signature 350 | cmd_ret = amd_cert_validate(ark, NULL, AMD_USAGE_ARK, device_type); // Naples 351 | if (cmd_ret != STATUS_SUCCESS) 352 | break; 353 | } 354 | 355 | if (device_type == PSP_DEVICE_TYPE_NAPLES) 356 | amd_root_key_id = amd_root_key_id_naples; 357 | else if (device_type == PSP_DEVICE_TYPE_ROME) 358 | amd_root_key_id = amd_root_key_id_rome; 359 | else //if (device_type == PSP_DEVICE_TYPE_MILAN) 360 | amd_root_key_id = amd_root_key_id_milan; 361 | 362 | if (memcmp(&ark->key_id_0, amd_root_key_id, sizeof(ark->key_id_0 + ark->key_id_1)) != 0) { 363 | cmd_ret = ERROR_INVALID_CERTIFICATE; 364 | break; 365 | } 366 | 367 | // We have to trust the ARK from the website, as there is no way to 368 | // validate it further, here. It is trustable due to being transmitted 369 | // over https 370 | } while (0); 371 | 372 | return cmd_ret; 373 | } 374 | 375 | SEV_ERROR_CODE AMDCert::amd_cert_validate_ask(const amd_cert *ask, const amd_cert *ark) 376 | { 377 | ePSP_DEVICE_TYPE device_type = get_device_type(ark); 378 | return amd_cert_validate(ask, ark, AMD_USAGE_ASK, device_type); // ASK 379 | } 380 | 381 | /** 382 | * Bytes, NOT bits 383 | */ 384 | size_t AMDCert::amd_cert_get_size(const amd_cert *cert) 385 | { 386 | size_t size = 0; 387 | uint32_t fixed_offset = offsetof(amd_cert, pub_exp); // 64 bytes 388 | 389 | if (cert) { 390 | size = fixed_offset + (cert->pub_exp_size + 2*cert->modulus_size)/8; 391 | } 392 | return size; 393 | } 394 | 395 | /** 396 | * The verify_sev_cert function takes in a parent of an sev_cert not 397 | * an amd_cert, so need to pull the pubkey out of the amd_cert and 398 | * place it into a tmp sev_cert to help validate the cek 399 | */ 400 | SEV_ERROR_CODE AMDCert::amd_cert_export_pub_key(const amd_cert *cert, 401 | sev_cert *pub_key_cert) 402 | { 403 | SEV_ERROR_CODE cmd_ret = STATUS_SUCCESS; 404 | 405 | do { 406 | if (!cert || !pub_key_cert) { 407 | cmd_ret = ERROR_INVALID_PARAM; 408 | break; 409 | } 410 | 411 | memset(pub_key_cert, 0, sizeof(*pub_key_cert)); 412 | 413 | // Todo. This has the potential for issues if we keep the key size 414 | // 4k and change the SHA type on the next gen 415 | if (cert->modulus_size == AMD_CERT_KEY_BITS_2K) { // Naples 416 | pub_key_cert->pub_key_algo = SEV_SIG_ALGO_RSA_SHA256; 417 | } 418 | else if (cert->modulus_size == AMD_CERT_KEY_BITS_4K) { // Rome 419 | pub_key_cert->pub_key_algo = SEV_SIG_ALGO_RSA_SHA384; 420 | } 421 | 422 | pub_key_cert->pub_key_usage = cert->key_usage; 423 | pub_key_cert->pub_key.rsa.modulus_size = cert->modulus_size; 424 | memcpy(pub_key_cert->pub_key.rsa.pub_exp, &cert->pub_exp, cert->pub_exp_size/8); 425 | memcpy(pub_key_cert->pub_key.rsa.modulus, &cert->modulus, cert->modulus_size/8); 426 | } while (0); 427 | 428 | return cmd_ret; 429 | } 430 | 431 | /** 432 | * Initialize an amd_cert object from a (.cert file) buffer 433 | * 434 | * Parameters: 435 | * cert [out] AMD certificate object, 436 | * buffer [in] buffer containing the raw AMD certificate 437 | */ 438 | SEV_ERROR_CODE AMDCert::amd_cert_init(amd_cert *cert, const uint8_t *buffer) 439 | { 440 | SEV_ERROR_CODE cmd_ret = STATUS_SUCCESS; 441 | amd_cert tmp; 442 | uint32_t fixed_offset = offsetof(amd_cert, pub_exp); // 64 bytes 443 | uint32_t pub_exp_offset = fixed_offset; // 64 bytes 444 | uint32_t modulus_offset = 0; // 2k or 4k bits 445 | uint32_t sig_offset = 0; // 2k or 4k bits 446 | 447 | do { 448 | if (!cert || !buffer) { 449 | cmd_ret = ERROR_INVALID_PARAM; 450 | break; 451 | } 452 | 453 | memset(&tmp, 0, sizeof(tmp)); 454 | 455 | // Copy the fixed body data from the temporary buffer 456 | memcpy(&tmp, buffer, fixed_offset); 457 | 458 | modulus_offset = pub_exp_offset + (tmp.pub_exp_size/8); 459 | sig_offset = modulus_offset + (tmp.modulus_size/8); // Mod size as def in spec 460 | 461 | // Initialize the remainder of the certificate 462 | memcpy(&tmp.pub_exp, (void *)(buffer + pub_exp_offset), tmp.pub_exp_size/8); 463 | memcpy(&tmp.modulus, (void *)(buffer + modulus_offset), tmp.modulus_size/8); 464 | memcpy(&tmp.sig, (void *)(buffer + sig_offset), tmp.modulus_size/8); 465 | 466 | memcpy(cert, &tmp, sizeof(*cert)); 467 | } while (0); 468 | 469 | return cmd_ret; 470 | } 471 | -------------------------------------------------------------------------------- /src/rmp.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2019-2021 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #ifndef RMP_H 18 | #define RMP_H 19 | 20 | #include "sevapi.h" 21 | #include 22 | #include // For size_t 23 | 24 | enum DRAM_PAGE_SIZE : uint32_t 25 | { 26 | DRAM_PAGE_SIZE_4K = 0 << 0, 27 | DRAM_PAGE_SIZE_2M = 1 << 0, 28 | 29 | DRAM_PAGE_SIZE_LIMIT = 2 << 0, 30 | }; 31 | 32 | #define RMP_NUM_ASID_COUNTERS 2048 33 | /** 34 | * RMP Table Struture: 35 | * 16Kbytes total (rmp_asid_counters) 36 | * rmp_entry tables () 37 | */ 38 | typedef struct rmp_asid_counters 39 | { 40 | uint64_t counters[RMP_NUM_ASID_COUNTERS]; 41 | } rmp_asid_counters_t; 42 | 43 | #define VMPL_DISABLED 0 44 | #define VMPL_ENABLED 1 45 | 46 | #define MIN_VMPL_PERM 0x00 47 | #define MAX_VMPL_PERM 0x0F 48 | 49 | // Define the location of the GPA in RMP entry, bit 12 to bit 50 50 | #define RMP_ENTRY_GPA_SHIFT (12ULL) 51 | 52 | typedef struct rmp_fields // TODO look into using uin32_t for some, question about packing 53 | { 54 | uint64_t assigned : 1; 55 | uint64_t page_size : 1; 56 | uint64_t immutable : 1; 57 | uint64_t subpage_count : 9; 58 | uint64_t gpa : 39; 59 | uint64_t asid : 10; 60 | uint64_t vmsa : 1; 61 | uint64_t validated : 1; 62 | uint64_t lock : 1; 63 | } rmp_fields_t; 64 | 65 | typedef union rmp_entry 66 | { 67 | rmp_fields_t f; 68 | uint64_t val; 69 | } rmp_entry_t; 70 | static_assert(sizeof(rmp_entry_t) == sizeof(uint64_t), "Error, static assertion failed"); 71 | 72 | // 6.1 Page Security Attributes 73 | typedef union vmpl_perm_mask 74 | { 75 | struct 76 | { 77 | uint32_t read : 1; // If page is readable by the VMPL 78 | uint32_t write : 1; // If page is writable by the VMPL 79 | uint32_t exec_user : 1; // If page is executable by the VMPL at CPL3 80 | uint32_t exec_supervisor : 1; // If page is executable by the VMPL at CPL0, CPL1, or CPL2. 81 | uint32_t reserved : 4; 82 | } __attribute__((packed)) f; 83 | uint8_t val; 84 | } __attribute__((packed)) vmpl_perm_mask_t; 85 | static_assert(sizeof(vmpl_perm_mask_t) == sizeof(uint8_t), "Error, static assertion failed"); 86 | 87 | typedef union vmpl_entry 88 | { 89 | struct 90 | { 91 | vmpl_perm_mask_t vmpl0; 92 | vmpl_perm_mask_t vmpl1; 93 | vmpl_perm_mask_t vmpl2; 94 | vmpl_perm_mask_t vmpl3; 95 | uint8_t reserved[4]; 96 | } __attribute__((packed)) f; 97 | uint64_t val; 98 | } vmpl_entry_t; 99 | static_assert(sizeof(vmpl_entry_t) == sizeof(uint64_t), "Error, static assertion failed"); 100 | 101 | typedef struct rmp_vmpl_entry 102 | { 103 | rmp_entry_t rmp; 104 | vmpl_entry_t vmpl; 105 | } rmp_vmpl_entry_t; 106 | static_assert(sizeof(rmp_vmpl_entry_t) == 2*sizeof(uint64_t), "Error, static assertion failed"); 107 | 108 | /** 109 | * 3.1 Metadata Entries (MDATA) 110 | * Metadata entry within a metadata page. Each entry is 64 bytes, a page is 4k 111 | * Not bit-for-bit compatible 112 | */ 113 | typedef struct mdata_perm_mask 114 | { 115 | // uint64_t of the following 116 | uint32_t valid : 1; // bit 0 117 | uint32_t page_validated : 1; // bit 1 118 | uint32_t vmsa : 1; // bit 2 119 | uint32_t metadata : 1; // bit 3 120 | uint32_t page_size : 1; // 0 = 4k, 1 = 2MB, bit 4 121 | uint32_t reserved1 : 7; // bits 5 to 11 reserved 122 | uint64_t gpa : 52; // bits 12 to 63 123 | } mdata_perm_mask_t; 124 | static_assert(sizeof(mdata_perm_mask_t) == sizeof(uint64_t), "Error, static assertion failed"); 125 | 126 | typedef union snp_metadata_entry 127 | { 128 | mdata_perm_mask_t f; 129 | uint64_t val; 130 | } snp_metadata_entry_t; 131 | static_assert(sizeof(snp_metadata_entry_t) == sizeof(uint64_t), "Error, static assertion failed"); 132 | 133 | typedef struct snp_metadata_page // MDATA 134 | { 135 | uint64_t software_data; // 00h 136 | uint64_t iv; // 08h 137 | uint8_t auth_tag[16]; // 10h 138 | snp_metadata_entry_t mdata_entry; // 20h 139 | vmpl_entry_t vmpl; // 28h 140 | uint64_t reserved2; // 30h 141 | uint64_t reserved3; // 38h 142 | } snp_metadata_page_t; 143 | static_assert(sizeof(snp_metadata_page_t) == 8*sizeof(uint64_t), "Error, static assertion failed"); 144 | 145 | #define RMP_ASID_COUNTERS_SIZE (sizeof(rmp_asid_counters_t)) 146 | #define RMP_ENTRY_SIZE (sizeof(rmp_entry_t)) // 64 bits, 8 bytes 147 | #define VMPL_ENTRY_SIZE (sizeof(vmpl_entry_t)) // 64 bits, 8 bytes 148 | #define RMP_VMPL_ENTRY_SIZE (sizeof(rmp_vmpl_entry_t)) 149 | #define SNP_METADATA_ENTRY_SIZE (sizeof(snp_metadata_page_t)) // 64 bytes 150 | 151 | /** 152 | * 3.2 TCB Version 153 | * A version string that represents the version of the firmware 154 | */ 155 | typedef union snp_tcb_version // TCB 156 | { 157 | struct 158 | { 159 | uint8_t boot_loader; // SVN of PSP bootloader 160 | uint8_t tee; // SVN of PSP operating system 161 | uint8_t reserved[4]; 162 | uint8_t snp; // SVN of SNP firmware 163 | uint8_t microcode; // Lowest current patch level of all the cores 164 | } __attribute__((packed)) f; 165 | uint64_t val; 166 | } __attribute__((packed)) snp_tcb_version_t; 167 | static_assert(sizeof(snp_tcb_version_t) == sizeof(uint64_t), "Error, static assertion failed"); 168 | 169 | // 6.2 Page States 170 | typedef enum snp_page_state 171 | { // Controlled by: SW HW SW SW HW SW HW 172 | // Page State Assigned Validated ASID Immutable Lock GPA VMSA 173 | SNP_PAGE_STATE_INVALID = 0x0, 174 | SNP_PAGE_STATE_DEFAULT = 0x1, 175 | SNP_PAGE_STATE_HYPERVISOR = 0x2, // 0 0 0 0 - - - 176 | SNP_PAGE_STATE_FIRMWARE = 0x3, // 1 0 0 1 - 0 0 177 | SNP_PAGE_STATE_RECLAIM = 0x4, // 1 0 0 0 0 - - 178 | SNP_PAGE_STATE_CONTEXT = 0x5, // 1 0 0 1 - 0 1 179 | SNP_PAGE_STATE_METADATA = 0x6, // 1 0 0 1 - >0 - 180 | SNP_PAGE_STATE_PRE_GUEST = 0x7, // 1 0 >0 1 - - - 181 | SNP_PAGE_STATE_PRE_SWAP = 0x8, // 1 1 >0 1 - - - 182 | SNP_PAGE_STATE_GUEST_INVALID = 0x9, // 1 0 >0 0 - - - 183 | SNP_PAGE_STATE_GUEST_VALID = 0xA, // 1 1 >0 0 - - - 184 | 185 | SNP_PAGE_STATE_LIMIT, 186 | } snp_page_state_t; 187 | 188 | #define SNP_GMSG_MAX_HDR_VERSION 1 189 | #define SNP_GMSG_MAX_MSG_VERSION_CPUID_REQ 1 190 | #define SNP_GMSG_MAX_MSG_VERSION_CPUID_RSP 1 191 | #define SNP_GMSG_MAX_MSG_VERSION_KEY_REQ 1 192 | #define SNP_GMSG_MAX_MSG_VERSION_KEY_RSP 1 193 | #define SNP_GMSG_MAX_MSG_VERSION_REPORT_REQ 1 194 | #define SNP_GMSG_MAX_MSG_VERSION_REPORT_RSP 1 195 | #define SNP_GMSG_MAX_MSG_VERSION_EXPORT_REQ 1 196 | #define SNP_GMSG_MAX_MSG_VERSION_EXPORT_RSP 1 197 | #define SNP_GMSG_MAX_MSG_VERSION_IMPORT_REQ 1 198 | #define SNP_GMSG_MAX_MSG_VERSION_IMPORT_RSP 1 199 | #define SNP_GMSG_MAX_MSG_VERSION_ABSORB_REQ 1 200 | #define SNP_GMSG_MAX_MSG_VERSION_ABSORB_RSP 1 201 | #define SNP_GMSG_MAX_MSG_VERSION_VMRK_REQ 1 202 | #define SNP_GMSG_MAX_MSG_VERSION_VMRK_RSP 1 203 | #define SNP_GMSG_MAX_MSG_VERSION_ABSORB_NOMA_REQ 1 204 | #define SNP_GMSG_MAX_MSG_VERSION_ABSORB_NOMA_RSP 1 205 | typedef struct snp_guest_message_header // GMSG 206 | { 207 | uint8_t auth_tag[32]; 208 | uint64_t msg_seqno; // Message sequence number 209 | uint8_t reserved[8]; 210 | uint8_t algo; // The AEAD used to encrypt this message 211 | uint8_t hdr_version; 212 | uint32_t hdr_size : 16; 213 | uint8_t msg_type; 214 | uint8_t msg_version; 215 | uint32_t msg_size : 16; 216 | uint32_t reserved2; 217 | uint8_t msg_vmpck; // The ID of the VMPCK used to protect this message 218 | uint8_t reserved3; 219 | uint8_t reserved4[2]; 220 | uint8_t reserved5[0x60-0x40]; 221 | uint8_t payload; // Start of payload. Need to fill in an SEVMem page with this structure 222 | } __attribute__((packed)) snp_guest_message_header_t; 223 | static_assert(sizeof(snp_guest_message_header_t) == 0x61, "Error, static assertion failed"); 224 | 225 | #define SNP_GMSG_HDR_AAD_SIZE (offsetof(snp_guest_message_header_t, payload)-offsetof(snp_guest_message_header_t, algo)) 226 | static_assert(SNP_GMSG_HDR_AAD_SIZE == (0x60-0x30), "Error, static assertion failed"); 227 | 228 | // AEAD Algorithm Encodings 229 | enum 230 | { 231 | AEAD_ALGO_INVALID = 0, 232 | AEAD_ALGO_AES_256_GCM = 1, 233 | }; 234 | 235 | // 8.1 CPUID Reporting 236 | // Request page is the same as launch update cpuid page 237 | typedef struct snp_launch_update_cpuid_page_t snp_msg_cpuid_req_t; 238 | 239 | typedef struct snp_msg_cpuid_rsp 240 | { 241 | uint32_t status; 242 | uint32_t count; 243 | uint64_t reserved; 244 | snp_cpuid_function_t cpuid_function[SNP_CPUID_COUNT_MAX]; 245 | } snp_msg_cpuid_rsp_t; 246 | 247 | // 8.2 Key Derivation 248 | typedef struct snp_msg_key_req 249 | { 250 | uint32_t root_key_select : 1; 251 | uint32_t reserved : 31; 252 | uint32_t reserved2; 253 | uint64_t guest_field_select; 254 | uint32_t vmpl; 255 | uint32_t guest_svn; 256 | snp_tcb_version_t tcb_version; 257 | } snp_msg_key_req_t; 258 | 259 | #define KEY_REQ_LABEL "gmsg-keyreq" 260 | 261 | typedef struct snp_mix_data 262 | { 263 | uint32_t root_key_select : 1; // bit 0 264 | uint32_t idblock_key_select : 2; // bits 1 to 2 265 | uint32_t reserved : 29; // bits 3 to 31 266 | uint32_t reserved2; 267 | uint64_t gfs; 268 | uint32_t vmpl; 269 | uint32_t guest_svn; 270 | snp_tcb_version_t tcb_version; 271 | uint64_t guest_policy; 272 | uint8_t image_id[16]; 273 | uint8_t family_id[16]; 274 | uint8_t measurement[32]; 275 | uint8_t host_data[32]; 276 | uint8_t idblock_key[32]; 277 | } __attribute__((packed)) snp_mix_data_t; 278 | 279 | // GUEST_FIELD_SELECT fields 280 | #define SNP_GUEST_FIELD_GUEST_POLICY_FLAG (1<<0) // The guest policy will be mixed into the key 281 | #define SNP_GUEST_FIELD_IMAGE_ID_FLAG (1<<1) // The image ID of the guest will be mixed into the key 282 | #define SNP_GUEST_FIELD_FAMILY_ID_FLAG (1<<2) // The family ID of the guest will be mixed into the key 283 | #define SNP_GUEST_FIELD_MEASUREMENT_FLAG (1<<3) // The measurement of the guest during launch will be mixed into the key 284 | #define SNP_GUEST_FIELD_GUEST_SVN_FLAG (1<<4) // The guest-provided SVN will be mixed into the key 285 | #define SNP_GUEST_FIELD_TCB_VERSION_FLAG (1<<5) // The guest-provided TCB version string will be mixed into the key 286 | 287 | typedef struct snp_msg_key_rsp 288 | { 289 | uint32_t status; // 0x0 Success, 0x16 Invalid parameters 290 | uint8_t reserved[0x20-0x4]; 291 | uint8_t derived_key[32]; // The requested derived key if STATUS is 0h 292 | } snp_msg_key_rsp_t; 293 | 294 | // 7.3 Attestation 295 | typedef struct snp_msg_report_req 296 | { 297 | uint8_t report_data[64]; // Guest-provided data for the attestation report 298 | uint32_t vmpl; // The VMPL to put into the attestation report 299 | uint8_t reserved[0x60-0x44]; 300 | } snp_msg_report_req_t; 301 | 302 | typedef struct snp_attestation_report_platform_info 303 | { 304 | uint32_t smt_en : 1; 305 | uint64_t reserved : 63; 306 | } __attribute__((packed)) snp_platform_info_t; 307 | static_assert(sizeof(snp_platform_info_t) == sizeof(uint64_t), "Error, static assertion failed"); 308 | 309 | #define SNP_GMSG_MAX_REPORT_VERSION 1 310 | typedef struct snp_attestation_report 311 | { 312 | uint32_t version; /* 0h */ 313 | uint32_t guest_svn; /* 4h */ 314 | uint64_t policy; /* 8h */ 315 | uint8_t family_id[16]; /* 10h */ 316 | uint8_t image_id[16]; /* 20h */ 317 | uint32_t vmpl; /* 30h */ 318 | uint32_t signature_algo; /* 34h */ 319 | snp_tcb_version_t tcb_version; /* 38h */ 320 | snp_platform_info_t platform_info; /* 40h */ 321 | uint32_t author_key_en : 1; /* 48h */ 322 | uint32_t reserved : 31; 323 | uint32_t reserved2; /* 4C */ 324 | uint8_t report_data[64]; /* 50h */ 325 | uint8_t measurement[48]; /* 90h */ 326 | uint8_t host_data[32]; /* C0h */ 327 | uint8_t id_key_digest[48]; /* E0h */ 328 | uint8_t author_key_digest[48]; /* 110h */ 329 | uint8_t report_id[32]; /* 140h */ 330 | uint8_t report_id_ma[32]; /* 160h */ 331 | snp_tcb_version_t reported_tcb; /* 180h */ 332 | uint8_t reserved3[0x1A0-0x188]; /* 188h-19Fh */ 333 | uint8_t chip_id[64]; /* 1A0h */ 334 | uint64_t committed_tcb; /* 1E0h */ 335 | uint8_t current_build; /* 1E8h */ 336 | uint8_t current_minor; /* 1E9h */ 337 | uint8_t current_major; /* 1EAh */ 338 | uint8_t reserved4; /* 1EBh */ 339 | uint8_t committed_build; /* 1ECh */ 340 | uint8_t committed_minor; /* 1EDh */ 341 | uint8_t committed_major; /* 1EEh */ 342 | uint8_t reserved5; /* 1EFh */ 343 | uint64_t launch_tcb; /* 1F0h */ 344 | uint8_t reserved6[0x2A0-0x1F8]; /* 1F8h-29Fh */ 345 | uint8_t signature[0x4A0-0x2A0]; /* 2A0h-49Fh */ 346 | } __attribute__((packed)) snp_attestation_report_t; 347 | static_assert(sizeof(snp_attestation_report_t) == 0x4A0, "Error, static assertion failed"); 348 | 349 | typedef struct snp_msg_report_rsp 350 | { 351 | uint32_t status; 352 | uint32_t report_size; 353 | uint8_t reserved[0x20-0x08]; 354 | snp_attestation_report_t report; 355 | } __attribute__((packed)) snp_msg_report_rsp_t; 356 | 357 | // 7.4 VM Export 358 | #define SNP_GMSG_MAX_XPORT_GCTX_VERSION 2 // Export, Import, Absorb, Absorb_NoMA 359 | typedef struct snp_msg_export_req 360 | { 361 | uint64_t gctx_paddr; 362 | uint32_t imi_en : 1; 363 | uint32_t reserved : 31; 364 | uint32_t reserved2; 365 | } snp_msg_export_req_t; 366 | 367 | typedef struct snp_msg_gctx 368 | { 369 | uint8_t ld[48]; 370 | uint8_t oek[32]; 371 | uint8_t vmpck0[32]; 372 | uint8_t vmpck1[32]; 373 | uint8_t vmpck2[32]; 374 | uint8_t vmpck3[32]; 375 | uint8_t vmrk[32]; 376 | uint8_t host_data[32]; 377 | uint8_t id_key_digest[48]; 378 | uint8_t author_key_digest[48]; 379 | uint8_t report_id[32]; 380 | uint8_t imd[48]; 381 | uint64_t msg_count0; 382 | uint64_t msg_count1; 383 | uint64_t msg_count2; 384 | uint64_t msg_count3; 385 | snp_metadata_page_t root_md_entry; 386 | uint32_t author_key_en : 1; /* bit 0 */ 387 | uint32_t id_block_en : 1; /* bit 1 */ 388 | uint64_t reserved : 60; /* bits 61:2 */ 389 | uint64_t policy; 390 | uint8_t state; 391 | uint64_t oek_iv_count; 392 | snp_launch_finish_id_block id_block; 393 | uint8_t gosvw[16]; 394 | uint8_t reserved2[0x300-0x2B0]; 395 | } snp_msg_gctx_t; 396 | static_assert(sizeof(snp_msg_gctx_t) == 0x300, "Error, static assertion failed"); 397 | 398 | typedef struct snp_msg_export_rsp 399 | { 400 | uint32_t status; 401 | uint32_t gctx_size; 402 | uint32_t gctx_version; 403 | uint8_t reserved[0x20-0x0C]; 404 | snp_msg_gctx_t gctx; 405 | } snp_msg_export_rsp_t; 406 | 407 | // 7.5 VM Import 408 | typedef struct snp_msg_import_req 409 | { 410 | uint64_t gctx_paddr; 411 | uint32_t in_gctx_size; 412 | uint32_t in_gctx_version; 413 | uint8_t reserved[0x20-0x10]; 414 | snp_msg_gctx_t incoming_gctx; 415 | } snp_msg_import_req_t; 416 | 417 | typedef struct snp_msg_import_rsp 418 | { 419 | uint32_t status; 420 | uint8_t reserved[0x10-0x4]; 421 | } snp_msg_import_rsp_t; 422 | 423 | // 7.6 VM Absorb 424 | typedef struct snp_msg_absorb_req 425 | { 426 | uint64_t gctx_paddr; 427 | uint32_t in_gctx_size; 428 | uint32_t in_gctx_version; 429 | uint8_t reserved[0x20-0x10]; 430 | snp_msg_gctx_t incoming_gctx; 431 | } snp_msg_absorb_req_t; 432 | 433 | typedef struct snp_msg_absorb_rsp 434 | { 435 | uint32_t status; 436 | uint8_t reserved[0x10-0x4]; 437 | } snp_msg_absorb_rsp_t; 438 | 439 | // 7.7 VM Absorb - No MA 440 | typedef struct snp_msg_absorb_noma_req 441 | { 442 | uint64_t reserved; 443 | uint32_t in_gctx_size; 444 | uint32_t in_gctx_version; 445 | uint8_t reserved2[0x20-0x10]; 446 | snp_msg_gctx_t incoming_gctx; 447 | } snp_msg_absorb_noma_req_t; 448 | 449 | typedef struct snp_msg_absorb_noma_rsp 450 | { 451 | uint32_t status; 452 | uint8_t reserved[0x10-0x4]; 453 | } snp_msg_absorb_noma_rsp_t; 454 | 455 | // 7.8 VMRK Message 456 | typedef struct snp_msg_vmrk_req 457 | { 458 | uint64_t gctx_paddr; 459 | uint8_t reserved[0x20-0x08]; 460 | uint8_t vmrk[32]; 461 | } snp_msg_vmrk_req_t; 462 | 463 | typedef struct snp_msg_vmrk_rsp 464 | { 465 | uint32_t status; 466 | uint8_t reserved[0x10-0x4]; 467 | } snp_msg_vmrk_rsp_t; 468 | 469 | // 9.21 Data Structores and Encodings 470 | // typedef enum snp_guest_message 471 | // { 472 | // SNP_MSG_INVALID = 0x0, 473 | // SNP_MSG_CPUID_REQ = 0x1, 474 | // SNP_MSG_CPUID_RSP = 0x2, 475 | // SNP_MSG_KEY_REQ = 0x3, 476 | // SNP_MSG_KEY_RSP = 0x4, 477 | // SNP_MSG_REPORT_REQ = 0x5, 478 | // SNP_MSG_REPORT_RSP = 0x6, 479 | // SNP_MSG_EXPORT_REQ = 0x7, 480 | // SNP_MSG_EXPORT_RSP = 0x8, 481 | // SNP_MSG_IMPORT_REQ = 0x9, 482 | // SNP_MSG_IMPORT_RSP = 0xA, 483 | // SNP_MSG_ABSORB_REQ = 0xB, 484 | // SNP_MSG_ABSORB_RSP = 0xC, 485 | // SNP_MSG_VMRK_REQ = 0xD, 486 | // SNP_MSG_VMRK_RSP = 0xE, 487 | // SNP_MSG_ABSORB_NOMA_REQ = 0xF, 488 | // SNP_MSG_ABSORB_NOMA_RSP = 0x10, 489 | 490 | // SNP_MSG_LIMIT, 491 | // } snp_guest_message_t; 492 | 493 | #define PADDR_INVALID ~(0x0ull) /* -1 */ 494 | 495 | #endif /* RMP_H */ 496 | -------------------------------------------------------------------------------- /src/sevcore_linux.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright 2018 Advanced Micro Devices, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **************************************************************************/ 16 | 17 | #include "sevapi.h" 18 | #ifdef __linux__ 19 | #include "sevcore.h" 20 | #include "utilities.h" 21 | #include "psp-sev.h" 22 | #include "rmp.h" 23 | #include "x509cert.h" 24 | #include // for ioctl() 25 | #include // for mmap() and friends 26 | #include // for std::rename 27 | #include // for errorno 28 | #include // for O_RDWR 29 | #include // for close() 30 | #include 31 | #include // for std::runtime_error() 32 | 33 | // -------------- Global Functions that don't require ioctls -------------- // 34 | void sev::get_family_model(uint32_t *family, uint32_t *model) 35 | { 36 | std::string cmd = ""; 37 | std::string fam_str = ""; 38 | std::string model_str = ""; 39 | 40 | cmd = "LANG=C lscpu | grep -E \"^CPU family:\" | awk {'print $3'}"; 41 | sev::execute_system_command(cmd, &fam_str); 42 | cmd = "LANG=C lscpu | grep -E \"^Model:\" | awk {'print $2'}"; 43 | sev::execute_system_command(cmd, &model_str); 44 | 45 | *family = std::stoi(fam_str, NULL, 10); 46 | *model = std::stoi(model_str, NULL, 10); 47 | } 48 | 49 | ePSP_DEVICE_TYPE sev::get_device_type(void) 50 | { 51 | uint32_t family = 0; 52 | uint32_t model = 0; 53 | 54 | sev::get_family_model(&family, &model); 55 | 56 | if (family == NAPLES_FAMILY && (int)model >= (int)NAPLES_MODEL_LOW && model <= NAPLES_MODEL_HIGH) { 57 | return PSP_DEVICE_TYPE_NAPLES; 58 | } 59 | else if (family == ROME_FAMILY && model >= ROME_MODEL_LOW && model <= ROME_MODEL_HIGH) { 60 | return PSP_DEVICE_TYPE_ROME; 61 | } 62 | else if (family == MILAN_FAMILY && (int)model >= (int)MILAN_MODEL_LOW && model <= MILAN_MODEL_HIGH) { 63 | return PSP_DEVICE_TYPE_MILAN; 64 | } 65 | else 66 | return PSP_DEVICE_TYPE_INVALID; 67 | } 68 | 69 | /** 70 | * Verify current FW is >= API version major.minor 71 | * Returns true if the firmware API version is at least major.minor 72 | * Has to be an offline comparison (can't call platform_status itself because 73 | * it needs to be used in calc_measurement) 74 | */ 75 | bool sev::min_api_version(unsigned platform_major, unsigned platform_minor, 76 | unsigned api_major, unsigned api_minor) 77 | { 78 | if ((platform_major < api_major) || 79 | (platform_major == api_major && platform_minor < api_minor)) 80 | return false; 81 | else 82 | return true; 83 | } 84 | 85 | int sev::get_ask_ark(const std::string output_folder, const std::string cert_file) 86 | { 87 | int cmd_ret = SEV_RET_UNSUPPORTED; 88 | std::string cmd = "wget "; 89 | std::string output = ""; 90 | ePSP_DEVICE_TYPE device_type = PSP_DEVICE_TYPE_INVALID; 91 | std::string cert_w_path = ""; 92 | 93 | do { 94 | cmd += "-O " + output_folder + cert_file + " "; 95 | cert_w_path = output_folder + cert_file; 96 | 97 | // Don't re-download the CEK from the KDS server if you already have it 98 | if (sev::get_file_size(cert_w_path) != 0) { 99 | // printf("ASK_ARK already exists, not re-downloading\n"); 100 | cmd_ret = SEV_RET_SUCCESS; 101 | break; 102 | } 103 | 104 | device_type = get_device_type(); 105 | if (device_type == PSP_DEVICE_TYPE_NAPLES) { 106 | cmd += ASK_ARK_NAPLES_SITE; 107 | } 108 | else if (device_type == PSP_DEVICE_TYPE_ROME) { 109 | cmd += ASK_ARK_ROME_SITE; 110 | } 111 | else if (device_type == PSP_DEVICE_TYPE_MILAN) { 112 | cmd += ASK_ARK_MILAN_SITE; 113 | } 114 | else { 115 | printf("Error: Unable to determine Platform type. " \ 116 | "Detected %i\n", (uint32_t)device_type); 117 | break; 118 | } 119 | 120 | // Download the certificate from the AMD server 121 | if (!sev::execute_system_command(cmd, &output)) { 122 | printf("Error: pipe not opened for system command\n"); 123 | cmd_ret = SEV_RET_UNSUPPORTED; 124 | break; 125 | } 126 | 127 | // Check if the file got downloaded 128 | if (sev::get_file_size(cert_w_path) == 0) { 129 | printf("Error: command to get ask_ark cert failed\n"); 130 | cmd_ret = SEV_RET_UNSUPPORTED; 131 | break; 132 | } 133 | 134 | cmd_ret = SEV_RET_SUCCESS; 135 | } while (0); 136 | 137 | return cmd_ret; 138 | } 139 | 140 | int sev::get_ask_ark_pem(const std::string output_folder, const std::string cert_chain_file, 141 | const std::string ask_file, const std::string ark_file) 142 | { 143 | int cmd_ret = SEV_RET_UNSUPPORTED; 144 | struct stat file_details; 145 | std::string cmd = "wget "; 146 | std::string output = ""; 147 | std::string cert_chain_w_path = output_folder + cert_chain_file; 148 | std::string ask_w_path = output_folder + ask_file; 149 | std::string ark_w_path = output_folder + ark_file; 150 | 151 | do { 152 | cmd += "-O " + cert_chain_w_path; // Really ASK and ARK 153 | cmd += " \""; 154 | cmd += KDS_VCEK; 155 | cmd += "Milan/"; 156 | cmd += KDS_VCEK_CERT_CHAIN; 157 | cmd += "\""; 158 | 159 | // Don't re-download the CEK from the KDS server if you already have it 160 | if (sev::get_file_size(cert_chain_w_path) != 0) { 161 | // printf("ASK_ARK pem already exists, not re-downloading\n"); 162 | cmd_ret = SEV_RET_SUCCESS; 163 | break; 164 | } 165 | 166 | // Download the certificate from the AMD server 167 | if (!sev::execute_system_command(cmd, &output)) { 168 | printf("Error: pipe not opened for system command\n"); 169 | cmd_ret = SEV_RET_UNSUPPORTED; 170 | break; 171 | } 172 | 173 | // Check if the file got downloaded 174 | if (sev::get_file_size(cert_chain_w_path) == 0) { 175 | printf("Error: command to get ask_ark cert failed\n"); 176 | cmd_ret = SEV_RET_UNSUPPORTED; 177 | break; 178 | } 179 | 180 | // Create the required SEV assets directory if it doesn't exist. 181 | if (stat(SEV_DEFAULT_DIR, &file_details) == -1) { 182 | if (errno == ENOENT) { 183 | if (mkdir(SEV_DEFAULT_DIR, 0775) != -1) { 184 | printf("Info: Created missing directory: %s\n", SEV_DEFAULT_DIR); 185 | } else { 186 | fprintf(stderr, "Error: Unable to create required directory: %s\n", SEV_DEFAULT_DIR); 187 | } 188 | } 189 | } 190 | 191 | // Split it from ask_ark into 2 separate pem files 192 | cmd = "csplit -z -f " SEV_DEFAULT_DIR "cert_chain- "; 193 | cmd += cert_chain_w_path; 194 | cmd += " '/-----BEGIN CERTIFICATE-----/' '{*}'"; 195 | if (!execute_system_command(cmd, &output)) { 196 | printf("Error: pipe not opened for system command\n"); 197 | break; 198 | } 199 | 200 | // Move the file from "cert_chain-xx" to something known (cert_chain_w_path) 201 | if (std::rename(SEV_DEFAULT_DIR "cert_chain-00", ask_w_path.c_str()) != 0) { 202 | printf("Error: renaming vcek cert chain file\n"); 203 | break; 204 | } 205 | if (std::rename(SEV_DEFAULT_DIR "cert_chain-01", ark_w_path.c_str()) != 0) { 206 | printf("Error: renaming vcek cert chain file\n"); 207 | break; 208 | } 209 | 210 | cmd_ret = SEV_RET_SUCCESS; 211 | } while (0); 212 | 213 | return cmd_ret; 214 | } 215 | 216 | int sev::zip_certs(const std::string output_folder, const std::string zip_name, 217 | const std::string files_to_zip) 218 | { 219 | int cmd_ret = SEV_RET_SUCCESS; 220 | std::string cmd = ""; 221 | std::string output = ""; 222 | std::string error = "zip error"; 223 | 224 | cmd = "zip -j " + output_folder + zip_name + " " + files_to_zip; 225 | sev::execute_system_command(cmd, &output); 226 | 227 | if (output.find(error) != std::string::npos) { 228 | printf("Error when zipping up files!"); 229 | cmd_ret = -1; 230 | } 231 | 232 | return cmd_ret; 233 | } 234 | 235 | // -------------------------- SEVDevice Functions -------------------------- // 236 | SEVDevice::~SEVDevice() 237 | { 238 | if (mFd >= 0) { 239 | close(mFd); 240 | } 241 | mFd = -1; 242 | } 243 | 244 | SEVDevice& SEVDevice::get_sev_device(void) 245 | { 246 | static SEVDevice m_sev_device; 247 | m_sev_device.mFd = open(DEFAULT_SEV_DEVICE.c_str(), O_RDWR); 248 | if (m_sev_device.mFd < 0) { 249 | throw std::runtime_error("Can't open " + std::string(DEFAULT_SEV_DEVICE) + "!\n"); 250 | } 251 | return m_sev_device; 252 | } 253 | 254 | int SEVDevice::sev_ioctl(int cmd, void *data, int *cmd_ret) 255 | { 256 | int ioctl_ret = -1; 257 | sev_issue_cmd arg; 258 | 259 | arg.cmd = (uint32_t)cmd; 260 | arg.data = (uint64_t)data; 261 | 262 | if (cmd == SEV_GET_ID) { 263 | /* 264 | * Note: There is a cache alignment bug in Naples SEV Firmware 265 | * version < 0.17.19 where it will sometimes return the wrong 266 | * value of P0. This happens when it's the first command run after 267 | * a bootup or when it's run a few seconds after switching between 268 | * self-owned and externally-owned (both directions). 269 | */ 270 | sev_user_data_status status_data; // Platform Status 271 | *cmd_ret = platform_status((uint8_t *)&status_data); 272 | if (*cmd_ret != 0) 273 | return ioctl_ret; 274 | 275 | if (status_data.api_major == 0 && status_data.api_minor <= 17 && 276 | status_data.build < 19) { 277 | printf("Adding a 5 second delay to account for Naples GetID bug...\n"); 278 | ioctl_ret = ioctl(get_fd(), SEV_ISSUE_CMD, &arg); 279 | usleep(5000000); // 5 seconds 280 | } 281 | } 282 | 283 | ioctl_ret = ioctl(get_fd(), SEV_ISSUE_CMD, &arg); 284 | *cmd_ret = arg.error; 285 | // if (ioctl_ret != 0) { // Sometimes you expect it to fail 286 | // printf("Error: cmd %#x ioctl_ret=%d (%#x)\n", cmd, ioctl_ret, arg.error); 287 | // } 288 | 289 | return ioctl_ret; 290 | } 291 | 292 | int SEVDevice::factory_reset() 293 | { 294 | uint32_t data; // Can't pass null 295 | int cmd_ret = SEV_RET_UNSUPPORTED; 296 | 297 | // Set struct to 0 298 | memset(&data, 0, sizeof(data)); 299 | 300 | sev_ioctl(SEV_FACTORY_RESET, &data, &cmd_ret); 301 | 302 | return (int)cmd_ret; 303 | } 304 | 305 | int SEVDevice::get_platform_owner(void *data) 306 | { 307 | return ((sev_user_data_status *)data)->flags & PLAT_STAT_OWNER_MASK; 308 | } 309 | 310 | int SEVDevice::get_platform_es(void *data) 311 | { 312 | return ((sev_user_data_status *)data)->flags & PLAT_STAT_ES_MASK; 313 | } 314 | 315 | int SEVDevice::platform_status(uint8_t *data) 316 | { 317 | int cmd_ret = SEV_RET_UNSUPPORTED; 318 | 319 | // Set struct to 0 320 | memset(data, 0, sizeof(sev_user_data_status)); 321 | 322 | sev_ioctl(SEV_PLATFORM_STATUS, data, &cmd_ret); 323 | 324 | return (int)cmd_ret; 325 | } 326 | 327 | int SEVDevice::pek_gen() 328 | { 329 | uint32_t data; // Can't pass null 330 | int cmd_ret = SEV_RET_UNSUPPORTED; 331 | 332 | // Set struct to 0 333 | memset(&data, 0, sizeof(data)); 334 | 335 | sev_ioctl(SEV_PEK_GEN, &data, &cmd_ret); 336 | 337 | return (int)cmd_ret; 338 | } 339 | 340 | 341 | int SEVDevice::pek_csr(uint8_t *data, void *pek_mem, sev_cert *csr) 342 | { 343 | int cmd_ret = SEV_RET_UNSUPPORTED; 344 | int ioctl_ret = -1; 345 | sev_user_data_pek_csr *data_buf = (sev_user_data_pek_csr *)data; 346 | SEVCert csr_obj(csr); 347 | 348 | // Set struct to 0 349 | memset(data_buf, 0, sizeof(sev_user_data_pek_csr)); 350 | 351 | do { 352 | // Populate PEKCSR buffer with CSRLength = 0 353 | data_buf->address = (uint64_t)pek_mem; 354 | data_buf->length = 0; 355 | 356 | // Send the command. This is to get the MinSize length. If you 357 | // already know it, then you don't have to send the command twice 358 | ioctl_ret = sev_ioctl(SEV_PEK_CSR, data_buf, &cmd_ret); 359 | if (ioctl_ret != -1) 360 | break; 361 | 362 | // Verify the results. Now the CSRLength will be updated to MinSize 363 | if (cmd_ret != SEV_RET_INVALID_LEN) 364 | break; 365 | 366 | // Send the command again with CSRLength=MinSize 367 | ioctl_ret = sev_ioctl(SEV_PEK_CSR, data_buf, &cmd_ret); 368 | if (ioctl_ret != 0) 369 | break; 370 | 371 | // Verify the CSR complies to API specification 372 | memcpy(csr, (sev_cert*)data_buf->address, sizeof(sev_cert)); 373 | if (csr_obj.validate_pek_csr() != STATUS_SUCCESS) { 374 | cmd_ret = SEV_RET_INVALID_CERTIFICATE; 375 | break; 376 | } 377 | } while (0); 378 | 379 | return (int)cmd_ret; 380 | } 381 | 382 | int SEVDevice::pdh_gen() 383 | { 384 | uint32_t data; // Can't pass null 385 | int cmd_ret = SEV_RET_UNSUPPORTED; 386 | 387 | // Set struct to 0 388 | memset(&data, 0, sizeof(data)); 389 | 390 | sev_ioctl(SEV_PDH_GEN, &data, &cmd_ret); 391 | 392 | return (int)cmd_ret; 393 | } 394 | 395 | int SEVDevice::pdh_cert_export(uint8_t *data, void *pdh_cert_mem, 396 | void *cert_chain_mem) 397 | { 398 | int cmd_ret = SEV_RET_UNSUPPORTED; 399 | int ioctl_ret = -1; 400 | sev_user_data_pdh_cert_export *data_buf = (sev_user_data_pdh_cert_export *)data; 401 | 402 | // Set struct to 0 403 | memset(data_buf, 0, sizeof(sev_user_data_pdh_cert_export)); 404 | 405 | do { 406 | data_buf->pdh_cert_address = (uint64_t)pdh_cert_mem; 407 | data_buf->pdh_cert_len = sizeof(sev_cert); 408 | data_buf->cert_chain_address = (uint64_t)cert_chain_mem; 409 | data_buf->cert_chain_len = sizeof(sev_cert_chain_buf); 410 | 411 | // Send the command 412 | ioctl_ret = sev_ioctl(SEV_PDH_CERT_EXPORT, data_buf, &cmd_ret); 413 | if (ioctl_ret != 0) 414 | break; 415 | 416 | } while (0); 417 | 418 | return (int)cmd_ret; 419 | } 420 | 421 | int SEVDevice::pek_cert_import(uint8_t *data, sev_cert *signed_pek_csr, 422 | sev_cert *oca_cert) 423 | { 424 | int cmd_ret = SEV_RET_UNSUPPORTED; 425 | int ioctl_ret = -1; 426 | sev_user_data_pek_cert_import *data_buf = (sev_user_data_pek_cert_import *)data; 427 | memset(data_buf, 0, sizeof(sev_user_data_pek_cert_import)); 428 | 429 | do { 430 | // Verify signed CSR complies to API specification 431 | SEVCert cert_obj(signed_pek_csr); 432 | if (cert_obj.verify_signed_pek_csr(oca_cert) != STATUS_SUCCESS) { 433 | cmd_ret = SEV_RET_INVALID_CERTIFICATE; 434 | break; 435 | } 436 | 437 | data_buf->pek_cert_address = (uint64_t)signed_pek_csr; 438 | data_buf->pek_cert_len = sizeof(sev_cert); 439 | data_buf->oca_cert_address = (uint64_t)oca_cert; 440 | data_buf->oca_cert_len = sizeof(sev_cert); 441 | 442 | // Send the command 443 | ioctl_ret = sev_ioctl(SEV_PEK_CERT_IMPORT, data_buf, &cmd_ret); 444 | if (ioctl_ret != 0) 445 | break; 446 | 447 | } while (0); 448 | 449 | return (int)cmd_ret; 450 | } 451 | 452 | // Must always pass in 128 bytes array, because of how linux /dev/sev ioctl works 453 | int SEVDevice::get_id(void *data, void *id_mem, uint32_t id_length) 454 | { 455 | int cmd_ret = SEV_RET_UNSUPPORTED; 456 | int ioctl_ret = -1; 457 | sev_user_data_get_id id_buf; // Linux buffer is different than API spec. Don't point it to *data 458 | 459 | // Set struct to 0 460 | memset(&id_buf, 0, sizeof(sev_user_data_get_id)); 461 | 462 | do { 463 | if (id_length != 128) { // Linux is hard-coded to 128 bytes 464 | id_length = 64; // PSP returns length of 1 ID, if length isn't correct 465 | cmd_ret = SEV_RET_INVALID_LEN; 466 | break; 467 | } 468 | 469 | // Send the command 470 | ioctl_ret = sev_ioctl(SEV_GET_ID, &id_buf, &cmd_ret); 471 | if (ioctl_ret != 0) 472 | break; 473 | 474 | // Copy the resulting IDs into the real buffer allocated for them 475 | memcpy(id_mem, &id_buf, id_length); 476 | } while (0); 477 | 478 | // The other functions in this file can do a direct mapping of the Linux 479 | // struct to the SEV API struct in sevapi.h, however, for this function, 480 | // this Linux struct doesn't match (at all) the API 481 | // Hard coded hack mapping to sevapi.h. Don't want to include sevapi.h in this file 482 | ((uint64_t *)data)[0] = (uint64_t)id_mem; // Set address of id_mem as 64 bit PAddr from sevapi.h 483 | ((uint32_t *)data)[2] = id_length; // 3rd 32-bit chunk in the cmd_buf 484 | 485 | return (int)cmd_ret; 486 | } 487 | 488 | std::string SEVDevice::display_build_info(void) 489 | { 490 | SEVDevice sev_device; 491 | uint8_t status_data[sizeof(sev_platform_status_cmd_buf)]; 492 | sev_platform_status_cmd_buf *status_data_buf = (sev_platform_status_cmd_buf *)&status_data; 493 | int cmd_ret = -1; 494 | 495 | std::string api_major_ver = "API_Major: xxx"; 496 | std::string api_minor_ver = "API_Minor: xxx"; 497 | std::string build_id_ver = "BuildID: xxx"; 498 | 499 | cmd_ret = sev_device.platform_status(status_data); 500 | if (cmd_ret != 0) 501 | return ""; 502 | 503 | char major_buf[4], minor_buf[4], build_id_buf[4]; // +1 for Null char 504 | sprintf(major_buf, "%d", status_data_buf->api_major); 505 | sprintf(minor_buf, "%d", status_data_buf->api_minor); 506 | sprintf(build_id_buf, "%d", status_data_buf->build_id); 507 | api_major_ver.replace(11, 3, major_buf); 508 | api_minor_ver.replace(11, 3, minor_buf); 509 | build_id_ver.replace(9, 3, build_id_buf); 510 | 511 | return api_major_ver + ", " + api_minor_ver + ", " + build_id_ver; 512 | } 513 | 514 | int SEVDevice::sys_info() 515 | { 516 | int cmd_ret = SEV_RET_SUCCESS; 517 | std::string cmd = ""; 518 | std::string output = ""; 519 | uint32_t family = 0; 520 | uint32_t model = 0; 521 | 522 | printf("-------------------------System Info-------------------------"); 523 | // Exec bash commands to get info on user's platform and append to the output string 524 | cmd = "echo -n 'Hostname: '; hostname"; 525 | sev::execute_system_command(cmd, &output); 526 | cmd = "echo -n 'BIOS Version: '; dmidecode -s bios-version"; 527 | sev::execute_system_command(cmd, &output); 528 | cmd = "echo -n 'BIOS Release Date: '; dmidecode -s bios-release-date"; 529 | sev::execute_system_command(cmd, &output); 530 | cmd = "echo -n 'SMT/Multi-Threading Status Per Socket: \n'; LANG=C lscpu | grep -E \"^CPU\\(s\\):|Thread\\(s\\) per core|Core\\(s\\) per socket|Socket\\(s\\)\""; 531 | sev::execute_system_command(cmd, &output); 532 | cmd = "echo -n 'Processor Frequency (all sockets): \n'; dmidecode -s processor-frequency"; 533 | sev::execute_system_command(cmd, &output); 534 | cmd = "echo -n 'Operating System: '; cat /etc/os-release | grep \"PRETTY_NAME=\" | sed 's/.*=//'"; // cat /etc/issue 535 | sev::execute_system_command(cmd, &output); 536 | cmd = "echo -n 'Kernel Version: '; uname -r"; 537 | sev::execute_system_command(cmd, &output); 538 | cmd = "echo -n 'Git Commit #: '; cat \"../.git/refs/heads/master\""; 539 | sev::execute_system_command(cmd, &output); 540 | 541 | // Print results of all execute_system_command calls 542 | printf("\n%s", output.c_str()); 543 | 544 | std::string build_info = display_build_info(); 545 | printf("Firmware Version: %s\n", build_info.c_str()); 546 | 547 | sev::get_family_model(&family, &model); 548 | printf("Platform Family 0x%02x, Model 0x%02x\n", family, model); 549 | 550 | printf("-------------------------------------------------------------\n\n"); 551 | 552 | return (int)cmd_ret; 553 | } 554 | 555 | /** 556 | * Note: You can not change the Platform Owner if Guests are running. That means 557 | * the Platform cannot be in the WORKING state here. The ccp Kernel Driver 558 | * will do its best to set the Platform state to whatever is required to 559 | * run each command, but that does not include shutting down Guests to do so. 560 | */ 561 | int SEVDevice::set_self_owned() 562 | { 563 | sev_user_data_status status_data; // Platform Status 564 | int cmd_ret = SEV_RET_UNSUPPORTED; 565 | 566 | cmd_ret = platform_status((uint8_t *)&status_data); 567 | if (cmd_ret != SEV_RET_SUCCESS) { 568 | return cmd_ret; 569 | } 570 | 571 | if (get_platform_owner(&status_data) != PLATFORM_STATUS_OWNER_SELF) { 572 | switch (status_data.state) { 573 | case SEV_PLATFORM_WORKING: 574 | break; // Can't Change Owner. Guests are running! 575 | case SEV_PLATFORM_UNINIT: { 576 | cmd_ret = factory_reset(); // Change owner from ext to self-owned 577 | if (cmd_ret != SEV_RET_SUCCESS) { 578 | return cmd_ret; 579 | } 580 | break; 581 | } 582 | case SEV_PLATFORM_INIT: { 583 | cmd_ret = pek_gen(); // Self-owned to different self-owned 584 | if (cmd_ret != SEV_RET_SUCCESS) { 585 | return cmd_ret; 586 | } 587 | break; 588 | } 589 | default: 590 | break; // Unrecognized Platform state! 591 | } 592 | } 593 | 594 | return (int)cmd_ret; 595 | } 596 | 597 | int SEVDevice::generate_cek_ask(const std::string output_folder, 598 | const std::string cert_file) 599 | { 600 | int cmd_ret = SEV_RET_UNSUPPORTED; 601 | int ioctl_ret = -1; 602 | sev_user_data_get_id id_buf; 603 | std::string cmd = "wget "; 604 | std::string output = ""; 605 | std::string to_cert_w_path = output_folder + cert_file; 606 | 607 | // Set struct to 0 608 | memset(&id_buf, 0, sizeof(sev_user_data_get_id)); 609 | 610 | do { 611 | cmd += "-P " + output_folder + " "; 612 | cmd += KDS_CEK; 613 | 614 | // Get the ID of the Platform 615 | // Send the command 616 | ioctl_ret = sev_ioctl(SEV_GET_ID, &id_buf, &cmd_ret); 617 | if (ioctl_ret != 0) 618 | break; 619 | 620 | // Copy the resulting IDs into the real buffer allocated for them 621 | // Note that Linux referrs to P0 and P1 as socket1 and socket2 (which is incorrect). 622 | // So below, we are getting the ID for P0, which is the first socket 623 | char id0_buf[sizeof(id_buf.socket1)*2+1] = {0}; // 2 chars per byte +1 for null term 624 | for (uint8_t i = 0; i < sizeof(id_buf.socket1); i++) 625 | { 626 | sprintf(id0_buf+strlen(id0_buf), "%02x", id_buf.socket1[i]); 627 | } 628 | cmd += id0_buf; 629 | 630 | // Don't re-download the CEK from the KDS server if you already have it 631 | if (sev::get_file_size(to_cert_w_path) != 0) { 632 | // printf("CEK already exists, not re-downloading\n"); 633 | cmd_ret = SEV_RET_SUCCESS; 634 | break; 635 | } 636 | 637 | // The AMD KDS server only accepts requests every 10 seconds 638 | std::string cert_w_path = output_folder + id0_buf; 639 | bool cert_found = false; 640 | int sec_to_sleep = 6; 641 | int retries = 0; 642 | int max_retries = (int)((10/sec_to_sleep)+2); 643 | while (!cert_found && retries <= max_retries) { 644 | if (!sev::execute_system_command(cmd, &output)) { 645 | printf("Error: pipe not opened for system command\n"); 646 | cmd_ret = SEV_RET_UNSUPPORTED; 647 | break; 648 | } 649 | 650 | // Check if the file got downloaded 651 | if (sev::get_file_size(cert_w_path) != 0) { 652 | cert_found = true; 653 | break; 654 | } 655 | sleep(sec_to_sleep); 656 | printf("Trying again\n"); 657 | retries++; 658 | } 659 | if (!cert_found) { 660 | printf("Error: command to get cek_ask cert failed\n"); 661 | cmd_ret = SEV_RET_UNSUPPORTED; 662 | break; 663 | } 664 | 665 | // Copy the file from (get_id) name to something known (cert_file) 666 | if (std::rename(cert_w_path.c_str(), to_cert_w_path.c_str()) != 0) { 667 | printf("Error: renaming cek cert file\n"); 668 | cmd_ret = SEV_RET_UNSUPPORTED; 669 | break; 670 | } 671 | } while (0); 672 | 673 | return cmd_ret; 674 | } 675 | 676 | int SEVDevice::generate_vcek_ask(const std::string output_folder, 677 | const std::string vcek_der_file, 678 | const std::string vcek_pem_file) 679 | { 680 | int cmd_ret = SEV_RET_UNSUPPORTED; 681 | int ioctl_ret = -1; 682 | sev_user_data_get_id id_buf; 683 | // char cmd[235]; 684 | std::string cmd = "wget "; 685 | std::string output = ""; 686 | std::string der_cert_w_path = output_folder + vcek_der_file; 687 | std::string pem_cert_w_path = output_folder + vcek_pem_file; 688 | 689 | // Set struct to 0 690 | memset(&id_buf, 0, sizeof(sev_user_data_get_id)); 691 | 692 | do { 693 | cmd += "-O " + der_cert_w_path + " \""; 694 | cmd += KDS_VCEK; 695 | cmd += "Milan/"; 696 | 697 | 698 | // Get the ID of the Platform 699 | // Send the command 700 | ioctl_ret = sev_ioctl(SEV_GET_ID, &id_buf, &cmd_ret); 701 | if (ioctl_ret != 0) 702 | break; 703 | 704 | // Copy the resulting IDs into the real buffer allocated for them 705 | // Note that Linux referrs to P0 and P1 as socket1 and socket2 (which is incorrect). 706 | // So below, we are getting the ID for P0, which is the first socket 707 | char id0_buf[sizeof(id_buf.socket1)*2+1] = {0}; // 2 chars per byte +1 for null term 708 | for (uint8_t i = 0; i < sizeof(id_buf.socket1); i++) 709 | { 710 | sprintf(id0_buf+strlen(id0_buf), "%02x", id_buf.socket1[i]); 711 | } 712 | cmd += id0_buf; 713 | // Create a container to store the TCB Version in. 714 | snp_tcb_version tcb_data = {.val = 0}; 715 | 716 | // Get the TCB version of the Platform 717 | request_tcb_data(tcb_data); 718 | 719 | cmd += "?blSPL=" + std::to_string(tcb_data.f.boot_loader); 720 | cmd += "&teeSPL=" + std::to_string(tcb_data.f.tee); 721 | cmd += "&snpSPL=" + std::to_string(tcb_data.f.snp); 722 | cmd += "&ucodeSPL=" + std::to_string(tcb_data.f.microcode) + "\""; 723 | 724 | // Don't re-download the VCEK from the KDS server if you already have it 725 | if (sev::get_file_size(pem_cert_w_path) != 0) { 726 | // printf("VCEK already exists, not re-downloading\n"); 727 | cmd_ret = SEV_RET_SUCCESS; 728 | break; 729 | } 730 | 731 | // The AMD KDS server only accepts requests every 10 seconds 732 | bool cert_found = false; 733 | int sec_to_sleep = 6; 734 | int retries = 0; 735 | int max_retries = (int)((10/sec_to_sleep)+2); 736 | while (!cert_found && retries <= max_retries) { 737 | if (!sev::execute_system_command(cmd, &output)) { 738 | printf("Error: pipe not opened for system command\n"); 739 | cmd_ret = SEV_RET_UNSUPPORTED; 740 | break; 741 | } 742 | 743 | // Check if the file got downloaded 744 | if (sev::get_file_size(der_cert_w_path) != 0) { 745 | cert_found = true; 746 | break; 747 | } 748 | sleep(sec_to_sleep); 749 | printf("Trying again\n"); 750 | retries++; 751 | } 752 | if (!cert_found) { 753 | printf("Error: command to get vcek_ask cert failed\n"); 754 | cmd_ret = SEV_RET_UNSUPPORTED; 755 | break; 756 | } 757 | 758 | // Convert the file from a DER to a PEM file 759 | convert_der_to_pem(der_cert_w_path, pem_cert_w_path); 760 | } while (0); 761 | 762 | return cmd_ret; 763 | } 764 | 765 | int SEVDevice::request_platform_status(snp_platform_status_buffer *plat_status) { 766 | int cmd_ret = SEV_RET_UNSUPPORTED; 767 | 768 | memset(plat_status, 0, sizeof(snp_platform_status_buffer)); 769 | 770 | sev_ioctl(SEV_SNP_PLATFORM_STATUS, plat_status, &cmd_ret); 771 | 772 | return cmd_ret; 773 | } 774 | 775 | void SEVDevice::request_tcb_data(snp_tcb_version &tcb_data) { 776 | snp_platform_status_buffer plat_status; 777 | int ioctl_return = request_platform_status(&plat_status); 778 | if (ioctl_return != 0) { 779 | fprintf( 780 | stderr, 781 | "Error: SNP_PLATFORM_STATUS failure: %s\n", 782 | std::strerror(ioctl_return) 783 | ); 784 | } else { 785 | tcb_data.val = plat_status.reported_tcb; 786 | } 787 | } 788 | 789 | 790 | #endif 791 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ⚠ Deprecated ⚠ 2 | 3 | With the release of tooling by the [VirTEE](https://virtee.io) organization, this tool is now considered deprecated. Consult the following table for replacement tooling: 4 | 5 | |Technologies|Replacement| 6 | |-:|:-| 7 | |Complete Library Support|[sev](https://github.com/virtee/sev)| 8 | |Legacy SEV and SEV-ES|[sevctl](https://github.com/virtee/sevctl)| 9 | |SEV-SNP Guest Tooling|[snpguest](https://github.com/virtee/snpguest)| 10 | |SEV-SNP Host Tooling|[snphost](https://github.com/virtee/snphost)| 11 | 12 | 13 | # How to Download and Run SEV-Tool 14 |   15 | Version: v20 16 | Updated: 2021-09-17 17 |   18 |   19 | 20 | ## Related Docs 21 | - The SEV API can be found here: https://developer.amd.com/sev/ 22 | 23 | ## Uses 24 | - SEV-Tool may be used by either a Guest Owner or a Platform Owner. Guest Owner commands will not talk to the SEV firmware and therefore are not required to be running on a Platform that supports SEV. Platform Owner commands talk directly to the SEV firmware and therefore must be running on an OS and Platform that supports SEV. 25 | 26 | ## OS Requirements for Guest Owners 27 | - All Linux distros are supported. Communication with the SEV firmware through the Linux ccp kernel driver is not required. 28 | ## OS Requirements for Platform Owners 29 | - Your Kernel must support SEV. 30 | - SME/SEV OS Support 31 | ``` 32 | SEV Guest SEV Host 33 | (VM) (Hypervisor) 34 | Linux® 4.15 Y 35 | Linux® 4.16 Y Y 36 | RHEL 7.6 Y 37 | RHEL 8 Y Y 38 | Fedora 28 Y Y 39 | SLES 15 Y Y 40 | Ubuntu 18.04 Y 41 | Ubuntu 10.10, 19.04 Y Y 42 | Oracle Linux UEK 5 Y Y 43 | VMWare - Support in Upcoming version of vSphere 44 | OpenStack - Support Upstream 45 | ``` 46 | - If running Linux, the ccp Kernel driver must be running and supported, as that is how the SEV-Tool communicates to the firmware. To tell if your Kernel supports SEV and the ccp driver is working correctly, run a dmesg and look for the following line: 47 | ```sh 48 | $ ccp [xxxx:xx:xx.x]: SEV API:x.xx build:x 49 | ``` 50 | For example, in Ubuntu 18.10, Kernel 4.18.0-15-generic, you will see something similar to 51 | ```sh 52 | $ ccp [0000:01:00.2]: SEV API:0.17 build:5 53 | ``` 54 | This means that the ccp driver was able to run the Init command against the SEV firmware. 55 | Note: You might also see a dmesg line noting that "Direct firmware load for amd/sev.fw failed with error -2". This just means that the firmware file is not there for the ccp driver to run the Download_Firmware command on startup, and you will be running with the SEV firmware that is provided in the BIOS. This is totally normal. 56 | - Note if running Linux, it is recommended that your OS come with a Kernel that supports SEV by default (Ubuntu 18.10 or later, etc) to have the latest Kernel headers and libc. If you start with an older Kernel and use a Kernel upgrade utility (ex: ukuu in Ubuntu) to update the Kernel manually, this will give you the newest Kernel headers, but you will have an old version of libc, which processed the older Kernel headers, not the new ones. It’s (probably) possible to update libc and have it process the new Kernel Headers, but it’s a lot of work. 57 | #### User Space Requirements 58 | - OpenSSL 1.1.1 or newer is required to compile the SEV-Tool. Note that OpenSSL 3.x changed the way RSA is handled and is not currently supported. 59 | - Ubuntu 16.04's user space only officially supports OpenSSL 1.0.x. It is possible to manually download and install the OpenSSL libraries to work around this issue. Go to the following links to download the packages and run the following command to install them. 60 | - https://packages.ubuntu.com/bionic-updates/amd64/libssl-dev/download 61 | - https://packages.ubuntu.com/bionic-updates/amd64/libssl1.1/download 62 | - sudo dpkg -i [DEB_PACKAGE] 63 | - __OR__ you may run the `deps-install.sh` script to meet this requirement (see below). 64 | - Ubuntu 18.04 might not come with OpenSSL 1.1.x pre-installed, so it will need to updated through apt-get 65 | 66 | ## Downloading the SEV-Tool 67 | 1. Boot into a Kernel that supports SEV (see above to confirm your Kernel supports SEV) 68 | 2. Install git, make, gcc, g++, and openssl dependencies 69 | - In most cases, you can run `deps-install.sh`. 70 | ```sh 71 | $ bash deps-install.sh 72 | ``` 73 | - If you would like to manually install dependencies, and are running Debian, Ubuntu 74 | ```sh 75 | $ sudo apt install git make gcc g++ -y --allow-unauthenticated 76 | ``` 77 | - Otherwise, use the method that is supported by your OS 78 | 2. The Github is located at: [SEV-Tool Github](https://github.com/AMDESE/SEV-Tool). Do a git clone with SSH 79 | ```sh 80 | $ git clone git@github.com:AMDESE/sev-tool.git 81 | ``` 82 | 3. Compile the SEV-Tool. 83 | - Running the build script does the following things: 84 | - Downloads, configs, and builds the OpenSSL Git code (submodule init/update) 85 | - Cleans and builds the SEV-Tool 86 | - To run the build script 87 | ```sh 88 | $ cd sev-tool 89 | $ autoreconf -vif && ./configure && make && cp src/sevtool . 90 | ``` 91 | 92 | ## How to Run the SEV-Tool 93 | 1. Pull latest changes from Git for any new added/modified tests 94 | ```sh 95 | $ cd sev-tool 96 | $ git pull 97 | $ autoreconf -vif && ./configure && make && mv src/sevtool . 98 | ``` 99 | 2. Run the tool with the help flag (-h or --help): 100 | ```sh 101 | $ sudo ./sevtool -h 102 | ``` 103 | - The help menu (and also the documentation below) will provide you with instructions on input parameters, etc 104 | 105 | ## Input flag format 106 | - The input flag format for every command is as follows and will be explained further in the coming sections 107 | ```sh 108 | $ sudo ./sevtool [optional_input_flags] [command_flag] [required_command_arguments] 109 | ``` 110 | 111 | ## Optional Input Flags for Every Command 112 | * The -h or --help flag will display the help menu to the user 113 | ```sh 114 | $ sudo ./sevtool -h 115 | ``` 116 | * The --sys_info flag will display the system information to the user such as: BIOS version, BIOS release date, SMT status, processor frequency, OS, Kernel version, Git commit number of the SEV-Tool, etc 117 | ```sh 118 | $ sudo ./sevtool --sys_info --get_id 119 | ``` 120 | * The --verbose and --brief flags will turn on/off displaying the out certs/IDs/etc to the screen on commands such as pek_csr, pdh_cert_export, get_id, etc 121 | ```sh 122 | $ sudo ./sevtool --verbose --sys_info --get_id 123 | $ sudo ./sevtool --brief --pek_csr 124 | ``` 125 | * Certain commands support the --ofolder flag which will allow the user to select the output folder for the certs exported by the command. See specific command for details 126 | 127 | ## Proposed Provisioning Steps 128 | ##### Platform Owner 129 | 1. Generate your OCA. Please see the API spec for key/certificate specifications. 130 | ```sh 131 | $ openssl ecparam -genkey -name secp384r1 -noout -out ec384-key-pair.pem 132 | $ openssl ec -in ec384-key-pair.pem -pubout -out ec384pub.pem 133 | ``` 134 | 2. Get Platform and connect to Internet 135 | 3. Install SEV-supported operating system 136 | 4. Confirm that SEV is supported (using steps in [OS Requirements](#os-requirements)) 137 | 5. Make a folder for the SEV-Tool to import/export certs/IDs from/to (pass into commands with the --ofolder flag) 138 | 6. Run the get_id command. As a simple check if running when 2 processors, make sure the returned IDs are different by using the --verbose flag 139 | 7. Get the CEK_ASK from the AMD KDS server by running the generate_cek_ask command 140 | - Note: the CEK certificate will be different every time you pull it from the KDS sever. The server re-generates/re-signs the cert every time instead of storing a static cert 141 | 8. Run the pek_csr command to generate a certificate signing request for your PEK. This will allow you to take ownership of the platform. 142 | 9. Run the sign_pek_csr command to sign the CSR with the provided OCA private key (can be performed on OCA platform). 143 | 10. Run the pek_cert_import command 144 | 11. Run the pdh_cert_export command 145 | 12. Run the get_ask_ark command 146 | 13. Run the export_cert_chain command to export the PDH down to the ARK (AMD root) and zip it up 147 | 14. Save the complete cert chain to send to the Guest Owners (GO's) 148 | 15. Make available UEFI image for guests 149 | 150 | ##### Guest Owner 151 | 1. Make a folder for the SEV-Tool to import/export certs/IDs from/to (pass into commands with the --ofolder flag) 152 | 2. Get UEFI image from the Platform Owner 153 | 3. (Out of scope) Confirm the UEFI image is trustable. 154 | 4. Get cert chain (PDH through ARK) from the Platform Owner (PO) and unzip them into a local folder 155 | 5. Run the validate_cert_chain command to verify the cert chain from the PDH down to the ARK (AMD root) 156 | - Download certificate and extract the respective ARK and ASK and use these for the verification process, not the ones provided by the PO. Example below: 157 | ```sh 158 | # Example for naples and rome processors 159 | device_type=naples 160 | if [[ $device_type == naples ]]; ask_size=832; else device_type=rome; ask_size=1600; fi 161 | wget https://developer.amd.com/wp-content/resources/ask_ark_$device_type.cert 162 | head -c $ask_size ask_ark_$device_type.cert > ask.cert 163 | dd < /dev/zero bs=$ask_size count=1 > ark.cert 164 | dd conv=notrunc if=ask_ark_$device_type.cert of=ark.cert skip=$ask_size iflag=skip_bytes 165 | ``` 166 | 6. (Out of scope) Verify OCA cert chain from the Platform Owner 167 | 7. Run the generate_launch_blob command 168 | - Reads in Platform Diffie-Hellman key (PDH cert from Platform) and generates new public/private Guest Owner Diffie-Hellman keypair. The DH key exchange is completed when the PO calls Launch_Start using the GODH public key. 169 | 8. Send the blob and the Guest Owner's DH public key to the Platform Owner so it can launch your Guests 170 | 9. Get the measurement from the Platform Owner 171 | 10. Run the calc_measurement command and verify the measurement from the Platform owner matches what you calculated/expected 172 | - The UEFI image is the digest param that we hash, so we know the Platform Owner isn't modifying that 173 | 11. Run the package_secret command 174 | 12. Send the secret(s) to the Platform Owner 175 | 13. Give "ready to run" approval to the Platform Owner 176 | 177 | ##### Hypervisor 178 | This is the flow that the Hypervisor will take to prepare the guest 179 | 1. After receiving the launch blob and the GO Diffie-Hellman public key from the Guest Owner, the Hypervisor can launch (call Launch_Start on) the guest 180 | 2. Call Launch_Update_Data and Activate, etc 181 | 3. Call Launch_Measure and send the measurement received from the PSP to the Guest Owner so it can verify against its expected result 182 | 4. Call Launch_Finish, etc 183 | 5. After receiving the packaged secrets from the Guest Owner (this step is optional), call Launch_Secret to pass the Guest Owner's secrets into the guest 184 | 6. The Guest Owner should now give the Hypervisor approval to run its Guest 185 | 186 | ## Command List 187 | The following commands are supported by the SEV-Tool. Please see the SEV-API for info on each specific command 188 | - Note: All input and output cert's mentioned below are SEV (special format) Certs. See SEV API for details 189 | 1. factory_reset 190 | - Input args: none 191 | - Files read in: none 192 | - Outputs: none 193 | - Platform/Guest Owner: Platform Owner 194 | - Note: in the current SEV API, this command was renamed to PLATFORM_RESET 195 | - Example 196 | ```sh 197 | $ sudo ./sevtool --factory_reset 198 | ``` 199 | 2. platform_status 200 | - Input args: none 201 | - Files read in: none 202 | - Outputs: The current platform status will be printed to the screen 203 | - Platform/Guest Owner: Platform Owner 204 | - Example 205 | ```sh 206 | $ sudo ./sevtool --platform_status 207 | ``` 208 | 3. pek_gen 209 | - Input args: none 210 | - Files read in: none 211 | - Outputs: none 212 | - Platform/Guest Owner: Platform Owner 213 | - Example 214 | ```sh 215 | $ sudo ./sevtool --pek_gen 216 | ``` 217 | 4. pek_csr 218 | - This command exports a CSR for the PEK of the platform. Signed CSR can only be re-imported successfully after the platform has been configured as self-owned. Changing ownership voids any existing CSR. As a result, this CSR export only works if the platform is self-owned to begin with. 219 | - Optional input args: --ofolder [folder_path] 220 | - This allows the user to specify the folder where the tool will export the certificate signing request 221 | - Files read in: none 222 | - Outputs: 223 | - If --[verbose] flag used: The pek_csr will be printed out to the screen as a hex dump and as a readable format 224 | - If --[ofolder] flag used: The pek_csr will be written as files to the specified folder as a hex dump and as a readable format. Files: pek_csr_out.cert and pek_csr_out_readable.cert 225 | - Platform/Guest Owner: Platform Owner 226 | - Example 227 | ```sh 228 | $ sudo ./sevtool --ofolder ./certs --pek_csr 229 | ``` 230 | 5. pdh_gen 231 | - Input args: none 232 | - Files read in: none 233 | - Outputs: none 234 | - Platform/Guest Owner: Platform Owner 235 | - Example 236 | ```sh 237 | $ sudo ./sevtool --pdh_gen 238 | ``` 239 | 6. pdh_cert_export 240 | - Optional input args: --ofolder [folder_path] 241 | - This allows the user to specify the folder where the tool will export the PDH cert and the Cert Chain (PEK, OCA, CEK) 242 | - Files read in: none 243 | - Outputs: 244 | - If --[verbose] flag used: The PDH cert and Cert Chain will be printed out to the screen as hex dumps and as readable formats 245 | - If --[ofolder] flag used: The PDH cert and Cert Chain will be written as files to the specified folder as hex dumps and as readable formats. Files: pdh_out.cert, pdh_readable_out.cert, cert_chain_out.cert, cert_chain_readable_out.cert 246 | - Platform/Guest Owner: Platform Owner 247 | - Example 248 | ```sh 249 | $ sudo ./sevtool --ofolder ./certs --pdh_cert_export 250 | ``` 251 | 7. pek_cert_import 252 | - This command imports a signed PEK CSR together with the corresponding OCA certificate. Import will not be successful if the platform is not self-owned at this stage. 253 | - Required input args: 254 | - The signed PEK CSR 255 | - The OCA certificate that signed the CSR (in AMD certificate format) 256 | - Files read in: [signed PEK CSR] [oca_cert_file] 257 | - Outputs: none 258 | - Platform/Guest Owner: Platform Owner 259 | - Example 260 | ```sh 261 | $ sudo ./sevtool --pek_cert_import pek_csr.signed.cert oca.cert 262 | ``` 263 | 8. get_id 264 | - Optional input args: --ofolder [folder_path] 265 | - This allows the user to specify the folder where the tool will export the IDs for Socket0 and Socket1 266 | - Files read in: none 267 | - Outputs: 268 | - If --[verbose] flag used: The IDs for Socket0 and Socket1 will be printed out to the screen 269 | - If --[ofolder] flag used: The IDs for Socket0 and Socket1 will be written as files to the specified folder. 270 | - Files: getid_s0_out.txt and getid_s1_out.txt 271 | - Platform/Guest Owner: Platform Owner 272 | - Example 273 | ```sh 274 | $ sudo ./sevtool --ofolder ./certs --get_id 275 | ``` 276 | 9. set_self_owned 277 | - Input args: none 278 | - Files read in: none 279 | - Outputs: none 280 | - Platform/Guest Owner: Platform Owner 281 | - Example 282 | ```sh 283 | $ sudo ./sevtool --ofolder ./certs --set_self_owned 284 | ``` 285 | 10. set_externally_owned 286 | - This function sets the platform as self-owned, exports a PEK CSR, signs it and re-imports it in one go. A Private key file (.pem) is a required argument. 287 | - Required input args: The private key of the OCA (.pem format) 288 | - Files read in: [oca_priv_key_file] 289 | - Outputs: none 290 | - Platform/Guest Owner: Platform Owner 291 | - Example 292 | ```sh 293 | $ sudo ./sevtool --set_externally_owned [oca_priv_key_file] 294 | $ sudo ./sevtool --set_externally_owned ../psp-sev-assets/oca_key_in.pem 295 | ``` 296 | 11. generate_cek_ask 297 | This command calls the get_id command and passes that ID into the AMD KDS server to retrieve the cek_ask. If the command returns an error while connecting to the KDS server, please try the command again. 298 | - Optional input args: --ofolder [folder_path] 299 | - This allows the user to specify the folder where the tool will export the cek_ark.cert to 300 | - Files read in: none 301 | - Outputs: 302 | - If --[ofolder] flag used: The cek_ask.cert file for your specific platform (processor in socket0) will be exported to the folder specified. Otherwise, it will be exported to the same directory as the SEV-Tool executable. File: cek_ask.cert 303 | - Platform/Guest Owner: Platform Owner 304 | - Example 305 | ```sh 306 | $ sudo ./sevtool --ofolder ./certs --generate_cek_ask 307 | ``` 308 | 12. get_ask_ark 309 | - Optional input args: --ofolder [folder_path] 310 | - This allows the user to specify the folder where the tool will export the ask_ark certificate to 311 | - Files read in: none 312 | - Outputs: 313 | - If --[ofolder] flag used: The ark_ark certificate will be exported to the folder specified. Otherwise, it will be exported to the same directory as the SEV-Tool executable. File: ask_ark.cert 314 | - Platform/Guest Owner: Guest Owner 315 | - Example 316 | ```sh 317 | $ sudo ./sevtool --ofolder ./certs --get_ask_ark 318 | ``` 319 | 13. export_cert_chain 320 | - This command exports all of the certs (PDH, PEK, OCA, CEK, ASK, ARK) and zips them up so that the Platform Owner can send them to the Guest Owner to allow the Guest Owner to validate the cert chain. The tool gets the CEK from the AMD KDS server and gets the ASK_ARK certificate from the SEV Developer website. 321 | - Optional input args: --ofolder [folder_path] 322 | - This allows the user to specify the folder where the tool will export all of the certificates to and the zip folder in 323 | - Files read in: none 324 | - Outputs: 325 | - If --[ofolder] flag used: The certificates will be exported to and zipped up in the folder specified. Otherwise, they will be exported to and zipped up in the same directory as the SEV-Tool executable. Files: pdh.cert, pek.cert, oca.cert, cek.cert, ask.cert, ark.cert, certs_export.zip 326 | - Platform/Guest Owner: Platform Owner 327 | - Example 328 | ```sh 329 | $ sudo ./sevtool --ofolder ./certs --export_cert_chain 330 | ``` 331 | 14. calc_measurement 332 | - The purpose of the calc_measurement command is for the user to be able to validate that they are calculating the HMAC/measurement correctly when they would be calling Launch_Measure during the normal API flow. The user can input all of the parameters used to calculate the HMAC and an output will be generated that the user can compare to their calculated measurement. 333 | - The digest parameter is the SHA256 (Naples) or SHA384 (Rome) output digest of the data passed into LaunchUpdateData and LaunchUpdateVMSA 334 | - Required input args: Note the format of the input parameters are ascii-encoded hex bytes. 335 | - \[Context]: 0x04 (Does not change) 336 | - [Api Major]: Can be obtained from PO or provided pek.cert (see example below) 337 | - [Api Minor]: Can be obtained from PO or provided pek.cert 338 | ```sh 339 | API=$(dd if=pek.cert ibs=1 skip=4 count=2 2>/dev/null | xxd -p) 340 | API_MAJOR=$(echo $API | cut -c1-2) 341 | API_MINOR=$(echo $API | cut -c3-4) 342 | ``` 343 | - [Build ID]: Must be provided by PO to GO in some way. Note that command `platform_status` returns value in decimal format, not hex. 344 | - [Policy]: Defined by guest. See [API](https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf) Chapter 3 Guest Policy. 345 | - [Digest]: Sha256 output digest over UEFI used during VM launch, e.g. OVMF_CODE.fd (for Naples, Sha384 for Rome) 346 | - [MNonce]: Provided by PO in some way. During VM launch, Launch_measure creates the nonce and appends it to the measure it calculated, see command description in the [API](https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf). Obtain MNonce by separating measure from nonce 347 | - [TIK]: Created during launch_blob creation and stored tmp_tk.bin. Retrieve by splitting into TEK and TIK (last 32 bytes) 348 | ```sh 349 | TIK=$(xxd -p tmp_tk.bin | tr -d '\n' | tail -c 32) 350 | ``` 351 | - Files read in: none 352 | - Optional input args: --ofolder [folder_path] 353 | - This allows the user to specify the folder where the tool will export the calculated measurement 354 | - Outputs: 355 | - The calculated measurement above matches the measure of the launch_measure SEV-command if the same/expected settings were used. 356 | - If --[verbose] flag used: The input data and calculated measurement will be printed out to the screen 357 | - If --[ofolder] flag used: The calculated measurement will be written to the specified folder as both binary and readable hex data. File: calc_measurement_out.bin, calc_measurement_out.txt 358 | - Platform/Guest Owner: Guest Owner 359 | - Example 360 | ```sh 361 | $ sudo ./sevtool --calc_measurement [Context] [Api Major] [Api Minor] [Build ID] [Policy] [Digest] [MNonce] [TIK] 362 | $ sudo ./sevtool --ofolder ./certs --calc_measurement 04 00 12 0f 00 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 4fbe0bedbad6c86ae8f68971d103e554 66320db73158a35a255d051758e95ed4 363 | You have entered 10 arguments 364 | Command: calc_measurement 365 | Input Arguments: 366 | Context: 04 367 | Api Major: 00 368 | Api Minor: 12 369 | Build ID: 0f 370 | Policy: 00 371 | Digest: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 372 | MNonce: 4fbe0bedbad6c86ae8f68971d103e554 373 | TIK: 66320db73158a35a255d051758e95ed4 374 | 375 | Output Measurement: 376 | 6faab2daae389bcd3405a05d6cafe33c0414f7bedd0bae19ba5f38b7fd1664ea 377 | 378 | Command Successful 379 | ``` 380 | - Note that, for security reasons, the TIK will not be shown when the user runs the tool 381 | 15. validate_cert_chain 382 | - This function imports the entire cert chain as separate cert files and validates it. 383 | - When calling this command, please unzip the certs into the folder you expect the tool to use. 384 | - The steps are as follows: 385 | - Imports the PDH, PEK, OCA, CEK, ASK, and ARK certs 386 | - Validates the ARK using the ARK (self-signed) 387 | - Validates the ASK using the ARK 388 | - Validates the CEK using the ASK 389 | - Validates the PEK using the CEK and the OCA 390 | - Validates the PDH using the PEK 391 | - Optional input args: --ofolder [folder_path] 392 | - This allows the user to specify the folder where the tool will import the certs from, otherwise it will use the same folder as the SEV-Tool executable 393 | - Files read in: ask.cert, ask.cert, cek.cert, oca.cert, pek.cert, pdh.cert 394 | - Outputs: none 395 | - Platform/Guest Owner: Guest Owner 396 | - Example 397 | ```sh 398 | $ sudo ./sevtool --ofolder ./certs --validate_cert_chain 399 | ``` 400 | 16. generate_launch_blob 401 | - This function imports the PDH certificate from the Platform and builds the Launch_Start session buffer (blob) and the Guest Owner Diffie-Hellman public key certificate. As part of the session buffer, a new public/private Diffie-Hellman keypair for the Guest Owner is generated, which is then used with the Platform's public DH key to calculate a shared secret, and then a master secret, which then is then used generate a new TEK and TIK. The session buffer (launch blob) and Guest Owner DH public key cert will be used as inputs to LaunchStart. 402 | - Required input args: Guest policy in hex format 403 | - Optional input args: --ofolder [folder_path] 404 | - This allows the user to specify the folder where the tool will export the blob file to 405 | - Files read in: pdh.cert 406 | - Outputs: 407 | - If --[ofolder] flag used: The blob file and Guest Owner DH public key certificate will be exported to the folder specified. The Guest Owner DH public and private keys are also exported during the process and are only to be used by the SEV-Tool. Otherwise, all files will be exported to the same directory as the SEV-Tool executable. Files: launch_blob.bin, godh.cert, (ignore these: godh_pubkey.pem, godh_privkey.pem). Note that the output blob file is a binary file; to import to qemu, the file needs to be manually converted to base64. 408 | - Platform/Guest Owner: Guest Owner 409 | - Example 410 | ```sh 411 | $ sudo ./sevtool --ofolder ./certs --generate_launch_blob [guest policy] 412 | $ sudo ./sevtool --ofolder ./certs --generate_launch_blob 39 413 | ``` 414 | 17. package_secret 415 | - This command reads in the pek.cert for API information, the file generated by generate_launch_blob (tmp_tk.bin) for the TEK, the calc_measurement_out.txt and the secret file (secret.txt) which is to be encrypted/wrapped by the TEK. It then outputs a file (packaged_secret.txt) which is then passed into Launch_Secret as part of the normal API flow 416 | - Required input args: --ofolder [folder_path] 417 | - This allows the user to specify the folder where the tool will look for the launch blob file and the secrets file, and where it will export the packaged secret file to 418 | - Files read in: secret.txt, launch_blob.bin, tmp_tk.bin, calc_measurement_out.bin 419 | - Outputs: 420 | - If --[ofolder] flag used: The blob file will be exported to the folder specified. Otherwise, it will be exported to the same directory as the SEV-Tool executable. File: packaged_secret.txt 421 | - Platform/Guest Owner: Guest Owner 422 | - Example 423 | ```sh 424 | $ sudo ./sevtool --ofolder ./certs --package_secret 425 | ``` 426 | 18. sign_pek_csr 427 | - This command reads the CSR and signs it with the provided OCA private key. Additionally, the oca.cert is created, which specifies the public key in AMD certificate format. 428 | - Required input args: 429 | - The CSR to be signed (pek_csr.cert) 430 | - The private key of the OCA in pem format [oca_priv_key].pem 431 | - Optional input args: --ofolder [folder_path] 432 | - This allows the user to specify the folder where the tool will export the signed CSR and OCA certificate to 433 | - Outputs: 434 | - oca.cert: The public certificate belonging to the private key, in AMD certificate format 435 | - pek_csr.signed.cert: The signed CSR containing the OCA signature but still missing the PEK signature. 436 | - Platform/Guest Owner: Platform Owner (Owner Certificate Authority) 437 | - Example 438 | ```sh 439 | $ sudo ./sevtool --sign_pek_csr [pek_csr.cert] [oca_priv_key] 440 | $ sudo ./sevtool --sign_pek_csr pek_csr.cert oca_priv.pem 441 | ``` 442 | 19. validate_attestation 443 | - This command imports the attestation report (attestation_report.bin) sent by the ATTESTATION command and the PEK certificate (pek.cert)(exported during generate_all_certs) and validates that the attestion report was signed by the PEK. 444 | - Optional input args: --ofolder [folder_path] 445 | - This allows the user to specify the folder where the tool will look for the attestation report and the pek cert file 446 | - Files read in: attestation_report.bin, pek.cert 447 | - Outputs: none 448 | - Platform/Guest Owner: Guest Owner 449 | - Example 450 | ```sh 451 | $ sudo ./sevtool --ofolder ./certs --validate_attestation 452 | ``` 453 | 20. validate_guest_report 454 | - This command imports the attestation report (guest_report.bin) generated from the Attestation guest message, sent through SNP_GUEST_REQUEST along with the current VCEK (vcek.pem)(exported during export_cert_chain_vcek) of the Platform and validates that the attestation report was signed by the VCEK. 455 | - Optional input args: --ofolder [folder_path] 456 | - This allows the user to specify the folder where the tool will look for the attestation report and the vcek cert file 457 | - Files read in: guest_report.bin, vcek.pem 458 | - Outputs: none 459 | - Platform/Guest Owner: Guest Owner 460 | - Example 461 | ```sh 462 | $ sudo ./sevtool --ofolder ./certs --validate_guest_report 463 | ``` 464 | 21. validate_cert_chain_vcek 465 | - This function imports the entire cert chain as separate pem cert files and validates it. 466 | - When calling this command, please unzip the certs into the folder you expect the tool to use. 467 | - The steps are as follows: 468 | - Imports the VCEK, ASK, and ARK .pem certs 469 | - Validates the ARK using the ARK (self-signed) 470 | - Validates the ASK using the ARK 471 | - Validates the VCEK using the ASK 472 | - Optional input args: --ofolder [folder_path] 473 | - This allows the user to specify the folder where the tool will import the certs from, otherwise it will use the same folder as the SEV-Tool executable 474 | - Files read in: vcek.pem, ask.pem, ark.pem 475 | - Outputs: none 476 | - Platform/Guest Owner: Guest Owner 477 | - Example 478 | ```sh 479 | $ sudo ./sevtool --ofolder ./certs --validate_cert_chain_vcek 480 | ``` 481 | 22. export_cert_chain_vcek 482 | - This command exports all of the certs (VCEK, ASK, ARK) as .pem files and zips them up so that the Platform Owner can send them to the Guest Owner to allow the Guest Owner to validate the vcek cert chain and the SNP guest message's Attestation report from SNP_GUEST_REQUEST. The tool gets the VCEK and ASK_ARK certificates from the AMD KDS server. 483 | - Optional input args: --ofolder [folder_path] 484 | - This allows the user to specify the folder where the tool will export all of the certificates to and the zip folder in 485 | - Files read in: none 486 | - Outputs: 487 | - If --[ofolder] flag used: The certificates will be exported to and zipped up in the folder specified. Otherwise, they will be exported to and zipped up in the same directory as the SEV-Tool executable. Files: vcek.der, vcek.pem, cert_chain.pem, ask.pem, ark.pem, certs_export_vcek.zip 488 | - Platform/Guest Owner: Platform Owner 489 | - Example 490 | ```sh 491 | $ sudo ./sevtool --ofolder ./certs --export_cert_chain_vcek 492 | ``` 493 | 494 | ## Running tests 495 | To run tests to check that each command is functioning correctly, run the test_all command and check that the entire thing returns success. 496 | 1. test_all 497 | - Required input args: --ofolder [folder_path] 498 | - Make a directory that the tests can use to store certs/data in during the test. Note that the tool will clear this directory before the tests are run. 499 | - Example 500 | ```sh 501 | $ sudo ./sevtool --ofolder ./tests --test_all 502 | ``` 503 | ## Issues, Feature Requests 504 | - For any issues with the tool itself, please create a ticket at https://github.com/AMDESE/sev-tool/issues 505 | - For any questions/concerns with the SEV API spec, please create a ticket at https://github.com/AMDESE/AMDSEV/issues 506 | --------------------------------------------------------------------------------