Browse Source

[Pal/Linux-SGX] Simple remote attestation framework

This is the minimalistic implementation of the remote attestation
framework. The framework conducts the following steps during
start-up to verify the authenticity of the SGX platform:

1. Connect to aesmd service to retrieve platform info (targetinfo)
   of the Quoting Enclave (QE) before enclave creation.
2. Prepare the SGX report inside enclave (during initialization):
   - Read SPID (service provider ID) from sgx.ra_client_spid in
     manifest.
   - Get an SGX report for local attestation to QE.
   - Generate a random 16-byte nonce for freshness.
   - Perform an OCALL for retrieving the quote.
3. Gather attestation data (QE report, QE quote, IAS report, signature,
   certificate chain) outside of enclave:
   - Connect to aesmd to retrieve the QE quote; aesmd also returns
     QE report.
   - Connect to Intel Attestation Service using curl. A client
     subscription key (specified via sgx.ra_client_key in manifest)
     is required to authenticate the HTTPS connection.
   - Get the IAS report, signature, and certificate chain from IAS.
     Print out the attestation result.
   - Return all this attestation data back to the enclave.
Chia-Che Tsai 5 years ago
parent
commit
6c4f21cba2
44 changed files with 4836 additions and 100 deletions
  1. 7 1
      Jenkinsfiles/Linux-SGX
  2. 7 1
      Jenkinsfiles/Linux-SGX-18.04
  3. 3 0
      Jenkinsfiles/ubuntu-16.04.dockerfile
  4. 4 1
      Jenkinsfiles/ubuntu-18.04.dockerfile
  5. 1 0
      Pal/lib/Makefile
  6. 11 19
      Pal/lib/crypto/adapters/mbedtls_adapter.c
  7. 130 0
      Pal/lib/crypto/adapters/mbedtls_encoding.c
  8. 386 0
      Pal/lib/crypto/mbedtls/asn1parse.c
  9. 293 0
      Pal/lib/crypto/mbedtls/base64.c
  10. 358 0
      Pal/lib/crypto/mbedtls/mbedtls/asn1.h
  11. 98 0
      Pal/lib/crypto/mbedtls/mbedtls/base64.h
  12. 5 2
      Pal/lib/crypto/mbedtls/mbedtls/config.h
  13. 580 0
      Pal/lib/crypto/mbedtls/mbedtls/oid.h
  14. 2 1
      Pal/lib/crypto/mbedtls/mbedtls/platform.h
  15. 2 3
      Pal/lib/crypto/mbedtls/md.c
  16. 575 0
      Pal/lib/crypto/mbedtls/md_wrap.c
  17. 707 0
      Pal/lib/crypto/mbedtls/oid.c
  18. 1 1
      Pal/lib/crypto/mbedtls/rsa.c
  19. 13 1
      Pal/lib/hex.h
  20. 39 3
      Pal/lib/pal_crypto.h
  21. 8 0
      Pal/regression/Attestation.c
  22. 10 0
      Pal/regression/Attestation.manifest.template
  23. 8 0
      Pal/regression/Makefile
  24. 20 0
      Pal/regression/test_pal.py
  25. 2 0
      Pal/src/host/Linux-SGX/.gitignore
  26. 24 3
      Pal/src/host/Linux-SGX/Makefile
  27. 24 6
      Pal/src/host/Linux-SGX/db_main.c
  28. 44 38
      Pal/src/host/Linux-SGX/enclave_framework.c
  29. 100 0
      Pal/src/host/Linux-SGX/enclave_ocalls.c
  30. 4 0
      Pal/src/host/Linux-SGX/enclave_ocalls.h
  31. 651 0
      Pal/src/host/Linux-SGX/enclave_platform.c
  32. 14 0
      Pal/src/host/Linux-SGX/ocall_types.h
  33. 1 0
      Pal/src/host/Linux-SGX/pal_linux.h
  34. 1 0
      Pal/src/host/Linux-SGX/pal_security.h
  35. 38 0
      Pal/src/host/Linux-SGX/quote/aesm.proto
  36. 17 6
      Pal/src/host/Linux-SGX/sgx_arch.h
  37. 75 0
      Pal/src/host/Linux-SGX/sgx_attest.h
  38. 8 0
      Pal/src/host/Linux-SGX/sgx_enclave.c
  39. 6 0
      Pal/src/host/Linux-SGX/sgx_internal.h
  40. 13 13
      Pal/src/host/Linux-SGX/sgx_main.c
  41. 479 0
      Pal/src/host/Linux-SGX/sgx_platform.c
  42. 22 0
      Pal/src/host/Linux-SGX/signer/pal-sgx-sign
  43. 4 0
      Pal/src/pal.h
  44. 41 1
      README.rst

+ 7 - 1
Jenkinsfiles/Linux-SGX

@@ -36,7 +36,13 @@ pipeline {
                         timeout(time: 15, unit: 'MINUTES') {
                             sh '''
                                 cd Pal/regression
-                                make SGX=1
+                                if [ "${ra_client_spid}" != "" ]; then \
+                                    make clean SGX=1; \
+                                    make SGX=1 RA_CLIENT_SPID=${ra_client_spid} \
+                                        RA_CLIENT_KEY=${ra_client_key}; \
+                                else \
+                                    make SGX=1; \
+                                fi
                                 make SGX_RUN=1
                                 make SGX_RUN=1 KEEP_LOG=1 regression
                                 '''

+ 7 - 1
Jenkinsfiles/Linux-SGX-18.04

@@ -36,7 +36,13 @@ pipeline {
                         timeout(time: 15, unit: 'MINUTES') {
                             sh '''
                                 cd Pal/regression
-                                make SGX=1
+                                if [ "${ra_client_spid}" != "" ]; then \
+                                    make clean SGX=1; \
+                                    make SGX=1 RA_CLIENT_SPID=${ra_client_spid} \
+                                        RA_CLIENT_KEY=${ra_client_key}; \
+                                else \
+                                    make SGX=1; \
+                                fi
                                 make SGX_RUN=1
                                 make SGX_RUN=1 KEEP_LOG=1 regression
                                 '''

+ 3 - 0
Jenkinsfiles/ubuntu-16.04.dockerfile

@@ -23,7 +23,10 @@ RUN apt-get update \
        python3-pytest \
        texinfo \
        wget \
+       curl \
        libomp-dev \
+       libprotobuf-c-dev \
+       protobuf-c-compiler \
 
 # Add the user UID:1001, GID:1001, home at /leeroy
     && groupadd -r leeroy -g 1001 \

+ 4 - 1
Jenkinsfiles/ubuntu-18.04.dockerfile

@@ -22,7 +22,10 @@ RUN apt-get update && apt-get install -y \
     python-protobuf \
     python3-pytest \
     texinfo \
-    wget
+    wget \
+    curl \
+    libprotobuf-c-dev \
+    protobuf-c-compiler
 
 # Add the user UID:1001, GID:1001, home at /leeroy
 RUN groupadd -r leeroy -g 1001 && useradd -u 1001 -r -g leeroy -m -d /leeroy -c "Leeroy Jenkins" leeroy && \

+ 1 - 0
Pal/lib/Makefile

@@ -46,6 +46,7 @@ ifeq ($(CRYPTO_PROVIDER),mbedtls)
 CFLAGS += -DCRYPTO_USE_MBEDTLS
 objs += crypto/adapters/mbedtls_adapter.o
 objs += crypto/adapters/mbedtls_dh.o
+objs += crypto/adapters/mbedtls_encoding.o
 endif
 ifeq ($(CRYPTO_PROVIDER),wolfssl)
 CFLAGS += -DCRYPTO_USE_WOLFSSL

+ 11 - 19
Pal/lib/crypto/adapters/mbedtls_adapter.c

@@ -219,31 +219,23 @@ int lib_RSAImportPublicKey(LIB_RSA_KEY *key, const uint8_t *e, uint64_t e_size,
     return 0;
 }
 
-int lib_RSAVerifySHA256(LIB_RSA_KEY *key, const uint8_t *signature,
-                        uint64_t signature_len, uint8_t *signed_data_out,
-                        uint64_t signed_data_out_len)
-{
-    size_t real_data_out_len;
+int lib_RSAVerifySHA256(LIB_RSA_KEY* key, const uint8_t* hash, uint64_t hash_len,
+                        const uint8_t* signature, uint64_t signature_len) {
 
     /* The mbedtls decrypt API assumes that you have a memory buffer that
      * is as large as the key size and take the length as a parameter. We
      * check, so that in the event the caller makes a mistake, you'll get
      * an error instead of reading off the end of the buffer. */
-    if (signature_len != key->len) {
+    if (signature_len != key->len)
         return -PAL_ERROR_INVAL;
-    }
-    int ret = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(key, NULL, NULL,
-                                                  MBEDTLS_RSA_PUBLIC,
-                                                  &real_data_out_len,
-                                                  signature,
-                                                  signed_data_out,
-                                                  signed_data_out_len);
-    if (ret == 0) {
-        if (real_data_out_len != SHA256_DIGEST_LEN) {
-            return -PAL_ERROR_INVAL;
-        }
-    }
-    return ret;
+
+    int ret = mbedtls_rsa_pkcs1_verify(key, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256,
+                                       hash_len, hash, signature);
+
+    if (ret < 0)
+        return -PAL_ERROR_DENIED;
+
+    return 0;
 }
 
 int lib_RSAFreeKey(LIB_RSA_KEY *key)

+ 130 - 0
Pal/lib/crypto/adapters/mbedtls_encoding.c

@@ -0,0 +1,130 @@
+/* Copyright (C) 2019, Texas A&M University.
+
+   This file is part of Graphene Library OS.
+
+   Graphene Library OS is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation, either version 3 of the
+   License, or (at your option) any later version.
+
+   Graphene Library OS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include "pal_crypto.h"
+#include "pal_error.h"
+#include "crypto/mbedtls/mbedtls/base64.h"
+#include "crypto/mbedtls/mbedtls/asn1.h"
+
+/*
+ * Encoding a byte string in Base64 format. If "dst" is NULL, this function returns the
+ * expected length after encoding.
+ *
+ * @src:  The raw data for encoding.
+ * @slen: The length of data
+ * @dst:  The buffer for storing the encoded data.
+ * @dlen: Returns the length after encoding.
+ */
+int lib_Base64Encode(const uint8_t* src, size_t slen, char* dst, size_t* dlen) {
+    int ret = mbedtls_base64_encode((unsigned char*)dst, *dlen, dlen,
+                                    (const unsigned char*)src, slen);
+    if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
+        return !dst ? 0 : -PAL_ERROR_OVERFLOW;
+    } else if (ret != 0) {
+        return -PAL_ERROR_INVAL;
+    } else {
+        return 0;
+    }
+}
+
+/*
+ * Decoding a byte string in Base64 format. If "dst" is NULL, this function returns the
+ * expected length after decoding.
+ *
+ * @src:  The Base64 string for decoding
+ * @slen: The length of data
+ * @dst:  The buffer for storing the decoded data.
+ * @dlen: Returns the length after decoding.
+ */
+int lib_Base64Decode(const char* src, size_t slen, uint8_t* dst, size_t* dlen) {
+    int ret = mbedtls_base64_decode((unsigned char*)dst, *dlen, dlen,
+                                    (const unsigned char*)src, slen);
+    if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
+        return !dst ? 0 : -PAL_ERROR_OVERFLOW;
+    } else if (ret != 0) {
+        return -PAL_ERROR_INVAL;
+    } else {
+        return 0;
+    }
+}
+
+/*
+ * Retrieve the next serialized object in the ASN1 format.
+ *
+ * @ptr:          Pass in the pointer for reading the ASN1 data. On success, will be updated
+ *                to the beginning of the next serialized object.
+ * @end:          The end of ASN1 data.
+ * @tag:          Returns the tag of the object.
+ * @is_construct: Returns a boolean to represent whether the object is a construct object.
+ * @buf:          Returns the data field of the object.
+ * @len:          Returns the length of the data field.
+ */
+int lib_ASN1GetSerial(uint8_t** ptr, const uint8_t* end, enum asn1_tag* tag, bool* is_construct,
+                      uint8_t** buf, size_t* len) {
+    if (end - (*ptr) < 1)
+        return -PAL_ERROR_ENDOFSTREAM;
+
+    uint8_t t = *(*ptr)++;
+    size_t l;
+    int ret = mbedtls_asn1_get_len((unsigned char**)ptr, (const unsigned char*)end, &l);
+    if (ret != 0)
+        return -PAL_ERROR_INVAL;
+
+    *tag = t & ~(MBEDTLS_ASN1_CONSTRUCTED|MBEDTLS_ASN1_CONTEXT_SPECIFIC);
+    *is_construct = t & MBEDTLS_ASN1_CONSTRUCTED;
+    *buf = *ptr;
+    *len = l;
+    *ptr += l;
+    return 0;
+}
+
+/*
+ * Retrieve the next ASN1 object which must be a large number (MBEDTLS_ASN1_INTEGER).
+ * Returns -PAL_ERROR_INVAL if the object is not a large number.
+ *
+ * @ptr:          Pass in the pointer for reading the ASN1 data. On sucess, will be updated
+ *                to the beginning of the next serialized object.
+ * @end:          The end of ASN1 data.
+ * @len:          Returns the length (number of bytes) of the large number.
+ */
+int lib_ASN1GetLargeNumberLength(uint8_t** ptr, const uint8_t* end, size_t* len) {
+    int ret = mbedtls_asn1_get_tag(ptr, end, len, MBEDTLS_ASN1_INTEGER);
+    if (ret < 0)
+        return -PAL_ERROR_INVAL;
+    return 0;
+}
+
+/*
+ * Retrieve the next ASN1 object which must be a bitstring. Returns -PAL_ERROR_INVAL if the
+ * object is not a bitstring.
+ *
+ * @ptr:          Pass in the pointer for reading the ASN1 data. On sucess, will be updated
+ *                to the beginning of the next serialized object.
+ * @end:          The end of ASN1 data.
+ * @str:          Returns the pointer to the bitstring.
+ * @len:          Returns the length of the bitstring.
+ */
+int lib_ASN1GetBitstring(uint8_t** ptr, const uint8_t* end, uint8_t** str, size_t* len) {
+    mbedtls_asn1_bitstring bs;
+    int ret = mbedtls_asn1_get_bitstring((unsigned char**)ptr, (const unsigned char*)end, &bs);
+    if (ret < 0)
+        return -PAL_ERROR_INVAL;
+    *str = (uint8_t*)bs.p;
+    *len = bs.len;
+    return 0;
+}

+ 386 - 0
Pal/lib/crypto/mbedtls/asn1parse.c

@@ -0,0 +1,386 @@
+/*
+ *  Generic ASN.1 parsing
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#if defined(MBEDTLS_ASN1_PARSE_C)
+
+#include "mbedtls/asn1.h"
+
+#if defined(MBEDTLS_BIGNUM_C)
+#include "mbedtls/bignum.h"
+#endif
+
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#include <stdlib.h>
+#define mbedtls_calloc    calloc
+#define mbedtls_free       free
+#endif
+
+/*
+ * ASN.1 DER decoding routines
+ */
+int mbedtls_asn1_get_len( unsigned char **p,
+                  const unsigned char *end,
+                  size_t *len )
+{
+    if( ( end - *p ) < 1 )
+        return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+    if( ( **p & 0x80 ) == 0 )
+        *len = *(*p)++;
+    else
+    {
+        switch( **p & 0x7F )
+        {
+        case 1:
+            if( ( end - *p ) < 2 )
+                return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+            *len = (*p)[1];
+            (*p) += 2;
+            break;
+
+        case 2:
+            if( ( end - *p ) < 3 )
+                return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+            *len = ( (size_t)(*p)[1] << 8 ) | (*p)[2];
+            (*p) += 3;
+            break;
+
+        case 3:
+            if( ( end - *p ) < 4 )
+                return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+            *len = ( (size_t)(*p)[1] << 16 ) |
+                   ( (size_t)(*p)[2] << 8  ) | (*p)[3];
+            (*p) += 4;
+            break;
+
+        case 4:
+            if( ( end - *p ) < 5 )
+                return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+            *len = ( (size_t)(*p)[1] << 24 ) | ( (size_t)(*p)[2] << 16 ) |
+                   ( (size_t)(*p)[3] << 8  ) |           (*p)[4];
+            (*p) += 5;
+            break;
+
+        default:
+            return( MBEDTLS_ERR_ASN1_INVALID_LENGTH );
+        }
+    }
+
+    if( *len > (size_t) ( end - *p ) )
+        return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+    return( 0 );
+}
+
+int mbedtls_asn1_get_tag( unsigned char **p,
+                  const unsigned char *end,
+                  size_t *len, int tag )
+{
+    if( ( end - *p ) < 1 )
+        return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+    if( **p != tag )
+        return( MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
+
+    (*p)++;
+
+    return( mbedtls_asn1_get_len( p, end, len ) );
+}
+
+int mbedtls_asn1_get_bool( unsigned char **p,
+                   const unsigned char *end,
+                   int *val )
+{
+    int ret;
+    size_t len;
+
+    if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_BOOLEAN ) ) != 0 )
+        return( ret );
+
+    if( len != 1 )
+        return( MBEDTLS_ERR_ASN1_INVALID_LENGTH );
+
+    *val = ( **p != 0 ) ? 1 : 0;
+    (*p)++;
+
+    return( 0 );
+}
+
+int mbedtls_asn1_get_int( unsigned char **p,
+                  const unsigned char *end,
+                  int *val )
+{
+    int ret;
+    size_t len;
+
+    if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_INTEGER ) ) != 0 )
+        return( ret );
+
+    if( len == 0 || len > sizeof( int ) || ( **p & 0x80 ) != 0 )
+        return( MBEDTLS_ERR_ASN1_INVALID_LENGTH );
+
+    *val = 0;
+
+    while( len-- > 0 )
+    {
+        *val = ( *val << 8 ) | **p;
+        (*p)++;
+    }
+
+    return( 0 );
+}
+
+#if defined(MBEDTLS_BIGNUM_C)
+int mbedtls_asn1_get_mpi( unsigned char **p,
+                  const unsigned char *end,
+                  mbedtls_mpi *X )
+{
+    int ret;
+    size_t len;
+
+    if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_INTEGER ) ) != 0 )
+        return( ret );
+
+    ret = mbedtls_mpi_read_binary( X, *p, len );
+
+    *p += len;
+
+    return( ret );
+}
+#endif /* MBEDTLS_BIGNUM_C */
+
+int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end,
+                        mbedtls_asn1_bitstring *bs)
+{
+    int ret;
+
+    /* Certificate type is a single byte bitstring */
+    if( ( ret = mbedtls_asn1_get_tag( p, end, &bs->len, MBEDTLS_ASN1_BIT_STRING ) ) != 0 )
+        return( ret );
+
+    /* Check length, subtract one for actual bit string length */
+    if( bs->len < 1 )
+        return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+    bs->len -= 1;
+
+    /* Get number of unused bits, ensure unused bits <= 7 */
+    bs->unused_bits = **p;
+    if( bs->unused_bits > 7 )
+        return( MBEDTLS_ERR_ASN1_INVALID_LENGTH );
+    (*p)++;
+
+    /* Get actual bitstring */
+    bs->p = *p;
+    *p += bs->len;
+
+    if( *p != end )
+        return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+
+    return( 0 );
+}
+
+/*
+ * Get a bit string without unused bits
+ */
+int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end,
+                             size_t *len )
+{
+    int ret;
+
+    if( ( ret = mbedtls_asn1_get_tag( p, end, len, MBEDTLS_ASN1_BIT_STRING ) ) != 0 )
+        return( ret );
+
+    if( (*len)-- < 2 || *(*p)++ != 0 )
+        return( MBEDTLS_ERR_ASN1_INVALID_DATA );
+
+    return( 0 );
+}
+
+
+
+/*
+ *  Parses and splits an ASN.1 "SEQUENCE OF <tag>"
+ */
+int mbedtls_asn1_get_sequence_of( unsigned char **p,
+                          const unsigned char *end,
+                          mbedtls_asn1_sequence *cur,
+                          int tag)
+{
+    int ret;
+    size_t len;
+    mbedtls_asn1_buf *buf;
+
+    /* Get main sequence tag */
+    if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
+            MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
+        return( ret );
+
+    if( *p + len != end )
+        return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+
+    while( *p < end )
+    {
+        buf = &(cur->buf);
+        buf->tag = **p;
+
+        if( ( ret = mbedtls_asn1_get_tag( p, end, &buf->len, tag ) ) != 0 )
+            return( ret );
+
+        buf->p = *p;
+        *p += buf->len;
+
+        /* Allocate and assign next pointer */
+        if( *p < end )
+        {
+            cur->next = (mbedtls_asn1_sequence*)mbedtls_calloc( 1,
+                                            sizeof( mbedtls_asn1_sequence ) );
+
+            if( cur->next == NULL )
+                return( MBEDTLS_ERR_ASN1_ALLOC_FAILED );
+
+            cur = cur->next;
+        }
+    }
+
+    /* Set final sequence entry's next pointer to NULL */
+    cur->next = NULL;
+
+    if( *p != end )
+        return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+
+    return( 0 );
+}
+
+int mbedtls_asn1_get_alg( unsigned char **p,
+                  const unsigned char *end,
+                  mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params )
+{
+    int ret;
+    size_t len;
+
+    if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
+            MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
+        return( ret );
+
+    if( ( end - *p ) < 1 )
+        return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
+    alg->tag = **p;
+    end = *p + len;
+
+    if( ( ret = mbedtls_asn1_get_tag( p, end, &alg->len, MBEDTLS_ASN1_OID ) ) != 0 )
+        return( ret );
+
+    alg->p = *p;
+    *p += alg->len;
+
+    if( *p == end )
+    {
+        mbedtls_platform_zeroize( params, sizeof(mbedtls_asn1_buf) );
+        return( 0 );
+    }
+
+    params->tag = **p;
+    (*p)++;
+
+    if( ( ret = mbedtls_asn1_get_len( p, end, &params->len ) ) != 0 )
+        return( ret );
+
+    params->p = *p;
+    *p += params->len;
+
+    if( *p != end )
+        return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+
+    return( 0 );
+}
+
+int mbedtls_asn1_get_alg_null( unsigned char **p,
+                       const unsigned char *end,
+                       mbedtls_asn1_buf *alg )
+{
+    int ret;
+    mbedtls_asn1_buf params;
+
+    memset( &params, 0, sizeof(mbedtls_asn1_buf) );
+
+    if( ( ret = mbedtls_asn1_get_alg( p, end, alg, &params ) ) != 0 )
+        return( ret );
+
+    if( ( params.tag != MBEDTLS_ASN1_NULL && params.tag != 0 ) || params.len != 0 )
+        return( MBEDTLS_ERR_ASN1_INVALID_DATA );
+
+    return( 0 );
+}
+
+void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *cur )
+{
+    if( cur == NULL )
+        return;
+
+    mbedtls_free( cur->oid.p );
+    mbedtls_free( cur->val.p );
+
+    mbedtls_platform_zeroize( cur, sizeof( mbedtls_asn1_named_data ) );
+}
+
+void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head )
+{
+    mbedtls_asn1_named_data *cur;
+
+    while( ( cur = *head ) != NULL )
+    {
+        *head = cur->next;
+        mbedtls_asn1_free_named_data( cur );
+        mbedtls_free( cur );
+    }
+}
+
+mbedtls_asn1_named_data *mbedtls_asn1_find_named_data( mbedtls_asn1_named_data *list,
+                                       const char *oid, size_t len )
+{
+    while( list != NULL )
+    {
+        if( list->oid.len == len &&
+            memcmp( list->oid.p, oid, len ) == 0 )
+        {
+            break;
+        }
+
+        list = list->next;
+    }
+
+    return( list );
+}
+
+#endif /* MBEDTLS_ASN1_PARSE_C */

+ 293 - 0
Pal/lib/crypto/mbedtls/base64.c

@@ -0,0 +1,293 @@
+/*
+ *  RFC 1521 base64 encoding/decoding
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#if defined(MBEDTLS_BASE64_C)
+
+#include "mbedtls/base64.h"
+
+#include <stdint.h>
+
+#if defined(MBEDTLS_SELF_TEST)
+#include <string.h>
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#include <stdio.h>
+#define mbedtls_printf printf
+#endif /* MBEDTLS_PLATFORM_C */
+#endif /* MBEDTLS_SELF_TEST */
+
+static const unsigned char base64_enc_map[64] =
+{
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+    'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+    'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', '+', '/'
+};
+
+static const unsigned char base64_dec_map[128] =
+{
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127, 127,  62, 127, 127, 127,  63,  52,  53,
+     54,  55,  56,  57,  58,  59,  60,  61, 127, 127,
+    127,  64, 127, 127, 127,   0,   1,   2,   3,   4,
+      5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
+     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
+     25, 127, 127, 127, 127, 127, 127,  26,  27,  28,
+     29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
+     39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
+     49,  50,  51, 127, 127, 127, 127, 127
+};
+
+#define BASE64_SIZE_T_MAX   ( (size_t) -1 ) /* SIZE_T_MAX is not standard */
+
+/*
+ * Encode a buffer into base64 format
+ */
+int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
+                   const unsigned char *src, size_t slen )
+{
+    size_t i, n;
+    int C1, C2, C3;
+    unsigned char *p;
+
+    if( slen == 0 )
+    {
+        *olen = 0;
+        return( 0 );
+    }
+
+    n = slen / 3 + ( slen % 3 != 0 );
+
+    if( n > ( BASE64_SIZE_T_MAX - 1 ) / 4 )
+    {
+        *olen = BASE64_SIZE_T_MAX;
+        return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
+    }
+
+    n *= 4;
+
+    if( ( dlen < n + 1 ) || ( NULL == dst ) )
+    {
+        *olen = n + 1;
+        return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
+    }
+
+    n = ( slen / 3 ) * 3;
+
+    for( i = 0, p = dst; i < n; i += 3 )
+    {
+        C1 = *src++;
+        C2 = *src++;
+        C3 = *src++;
+
+        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+        *p++ = base64_enc_map[(((C1 &  3) << 4) + (C2 >> 4)) & 0x3F];
+        *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
+        *p++ = base64_enc_map[C3 & 0x3F];
+    }
+
+    if( i < slen )
+    {
+        C1 = *src++;
+        C2 = ( ( i + 1 ) < slen ) ? *src++ : 0;
+
+        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+        *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+
+        if( ( i + 1 ) < slen )
+             *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
+        else *p++ = '=';
+
+        *p++ = '=';
+    }
+
+    *olen = p - dst;
+    *p = 0;
+
+    return( 0 );
+}
+
+/*
+ * Decode a base64-formatted buffer
+ */
+int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
+                   const unsigned char *src, size_t slen )
+{
+    size_t i, n;
+    uint32_t j, x;
+    unsigned char *p;
+
+    /* First pass: check for validity and get output length */
+    for( i = n = j = 0; i < slen; i++ )
+    {
+        /* Skip spaces before checking for EOL */
+        x = 0;
+        while( i < slen && src[i] == ' ' )
+        {
+            ++i;
+            ++x;
+        }
+
+        /* Spaces at end of buffer are OK */
+        if( i == slen )
+            break;
+
+        if( ( slen - i ) >= 2 &&
+            src[i] == '\r' && src[i + 1] == '\n' )
+            continue;
+
+        if( src[i] == '\n' )
+            continue;
+
+        /* Space inside a line is an error */
+        if( x != 0 )
+            return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+
+        if( src[i] == '=' && ++j > 2 )
+            return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+
+        if( src[i] > 127 || base64_dec_map[src[i]] == 127 )
+            return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+
+        if( base64_dec_map[src[i]] < 64 && j != 0 )
+            return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+
+        n++;
+    }
+
+    if( n == 0 )
+    {
+        *olen = 0;
+        return( 0 );
+    }
+
+    /* The following expression is to calculate the following formula without
+     * risk of integer overflow in n:
+     *     n = ( ( n * 6 ) + 7 ) >> 3;
+     */
+    n = ( 6 * ( n >> 3 ) ) + ( ( 6 * ( n & 0x7 ) + 7 ) >> 3 );
+    n -= j;
+
+    if( dst == NULL || dlen < n )
+    {
+        *olen = n;
+        return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
+    }
+
+   for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ )
+   {
+        if( *src == '\r' || *src == '\n' || *src == ' ' )
+            continue;
+
+        j -= ( base64_dec_map[*src] == 64 );
+        x  = ( x << 6 ) | ( base64_dec_map[*src] & 0x3F );
+
+        if( ++n == 4 )
+        {
+            n = 0;
+            if( j > 0 ) *p++ = (unsigned char)( x >> 16 );
+            if( j > 1 ) *p++ = (unsigned char)( x >>  8 );
+            if( j > 2 ) *p++ = (unsigned char)( x       );
+        }
+    }
+
+    *olen = p - dst;
+
+    return( 0 );
+}
+
+#if defined(MBEDTLS_SELF_TEST)
+
+static const unsigned char base64_test_dec[64] =
+{
+    0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
+    0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
+    0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
+    0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
+    0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
+    0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
+    0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
+    0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
+};
+
+static const unsigned char base64_test_enc[] =
+    "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
+    "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
+
+/*
+ * Checkup routine
+ */
+int mbedtls_base64_self_test( int verbose )
+{
+    size_t len;
+    const unsigned char *src;
+    unsigned char buffer[128];
+
+    if( verbose != 0 )
+        mbedtls_printf( "  Base64 encoding test: " );
+
+    src = base64_test_dec;
+
+    if( mbedtls_base64_encode( buffer, sizeof( buffer ), &len, src, 64 ) != 0 ||
+         memcmp( base64_test_enc, buffer, 88 ) != 0 )
+    {
+        if( verbose != 0 )
+            mbedtls_printf( "failed\n" );
+
+        return( 1 );
+    }
+
+    if( verbose != 0 )
+        mbedtls_printf( "passed\n  Base64 decoding test: " );
+
+    src = base64_test_enc;
+
+    if( mbedtls_base64_decode( buffer, sizeof( buffer ), &len, src, 88 ) != 0 ||
+         memcmp( base64_test_dec, buffer, 64 ) != 0 )
+    {
+        if( verbose != 0 )
+            mbedtls_printf( "failed\n" );
+
+        return( 1 );
+    }
+
+    if( verbose != 0 )
+        mbedtls_printf( "passed\n\n" );
+
+    return( 0 );
+}
+
+#endif /* MBEDTLS_SELF_TEST */
+
+#endif /* MBEDTLS_BASE64_C */

+ 358 - 0
Pal/lib/crypto/mbedtls/mbedtls/asn1.h

@@ -0,0 +1,358 @@
+/**
+ * \file asn1.h
+ *
+ * \brief Generic ASN.1 parsing
+ */
+/*
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+#ifndef MBEDTLS_ASN1_H
+#define MBEDTLS_ASN1_H
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#include <stddef.h>
+
+#if defined(MBEDTLS_BIGNUM_C)
+#include "bignum.h"
+#endif
+
+/**
+ * \addtogroup asn1_module
+ * \{
+ */
+
+/**
+ * \name ASN1 Error codes
+ * These error codes are OR'ed to X509 error codes for
+ * higher error granularity.
+ * ASN1 is a standard to specify data structures.
+ * \{
+ */
+#define MBEDTLS_ERR_ASN1_OUT_OF_DATA                      -0x0060  /**< Out of data when parsing an ASN1 data structure. */
+#define MBEDTLS_ERR_ASN1_UNEXPECTED_TAG                   -0x0062  /**< ASN1 tag was of an unexpected value. */
+#define MBEDTLS_ERR_ASN1_INVALID_LENGTH                   -0x0064  /**< Error when trying to determine the length or invalid length. */
+#define MBEDTLS_ERR_ASN1_LENGTH_MISMATCH                  -0x0066  /**< Actual length differs from expected length. */
+#define MBEDTLS_ERR_ASN1_INVALID_DATA                     -0x0068  /**< Data is invalid. (not used) */
+#define MBEDTLS_ERR_ASN1_ALLOC_FAILED                     -0x006A  /**< Memory allocation failed */
+#define MBEDTLS_ERR_ASN1_BUF_TOO_SMALL                    -0x006C  /**< Buffer too small when writing ASN.1 data structure. */
+
+/* \} name */
+
+/**
+ * \name DER constants
+ * These constants comply with the DER encoded ASN.1 type tags.
+ * DER encoding uses hexadecimal representation.
+ * An example DER sequence is:\n
+ * - 0x02 -- tag indicating INTEGER
+ * - 0x01 -- length in octets
+ * - 0x05 -- value
+ * Such sequences are typically read into \c ::mbedtls_x509_buf.
+ * \{
+ */
+#define MBEDTLS_ASN1_BOOLEAN                 0x01
+#define MBEDTLS_ASN1_INTEGER                 0x02
+#define MBEDTLS_ASN1_BIT_STRING              0x03
+#define MBEDTLS_ASN1_OCTET_STRING            0x04
+#define MBEDTLS_ASN1_NULL                    0x05
+#define MBEDTLS_ASN1_OID                     0x06
+#define MBEDTLS_ASN1_UTF8_STRING             0x0C
+#define MBEDTLS_ASN1_SEQUENCE                0x10
+#define MBEDTLS_ASN1_SET                     0x11
+#define MBEDTLS_ASN1_PRINTABLE_STRING        0x13
+#define MBEDTLS_ASN1_T61_STRING              0x14
+#define MBEDTLS_ASN1_IA5_STRING              0x16
+#define MBEDTLS_ASN1_UTC_TIME                0x17
+#define MBEDTLS_ASN1_GENERALIZED_TIME        0x18
+#define MBEDTLS_ASN1_UNIVERSAL_STRING        0x1C
+#define MBEDTLS_ASN1_BMP_STRING              0x1E
+#define MBEDTLS_ASN1_PRIMITIVE               0x00
+#define MBEDTLS_ASN1_CONSTRUCTED             0x20
+#define MBEDTLS_ASN1_CONTEXT_SPECIFIC        0x80
+
+/*
+ * Bit masks for each of the components of an ASN.1 tag as specified in
+ * ITU X.690 (08/2015), section 8.1 "General rules for encoding",
+ * paragraph 8.1.2.2:
+ *
+ * Bit  8     7   6   5          1
+ *     +-------+-----+------------+
+ *     | Class | P/C | Tag number |
+ *     +-------+-----+------------+
+ */
+#define MBEDTLS_ASN1_TAG_CLASS_MASK          0xC0
+#define MBEDTLS_ASN1_TAG_PC_MASK             0x20
+#define MBEDTLS_ASN1_TAG_VALUE_MASK          0x1F
+
+/* \} name */
+/* \} addtogroup asn1_module */
+
+/** Returns the size of the binary string, without the trailing \\0 */
+#define MBEDTLS_OID_SIZE(x) (sizeof(x) - 1)
+
+/**
+ * Compares an mbedtls_asn1_buf structure to a reference OID.
+ *
+ * Only works for 'defined' oid_str values (MBEDTLS_OID_HMAC_SHA1), you cannot use a
+ * 'unsigned char *oid' here!
+ */
+#define MBEDTLS_OID_CMP(oid_str, oid_buf)                                   \
+        ( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf)->len ) ||                \
+          memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) != 0 )
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \name Functions to parse ASN.1 data structures
+ * \{
+ */
+
+/**
+ * Type-length-value structure that allows for ASN1 using DER.
+ */
+typedef struct mbedtls_asn1_buf
+{
+    int tag;                /**< ASN1 type, e.g. MBEDTLS_ASN1_UTF8_STRING. */
+    size_t len;             /**< ASN1 length, in octets. */
+    unsigned char *p;       /**< ASN1 data, e.g. in ASCII. */
+}
+mbedtls_asn1_buf;
+
+/**
+ * Container for ASN1 bit strings.
+ */
+typedef struct mbedtls_asn1_bitstring
+{
+    size_t len;                 /**< ASN1 length, in octets. */
+    unsigned char unused_bits;  /**< Number of unused bits at the end of the string */
+    unsigned char *p;           /**< Raw ASN1 data for the bit string */
+}
+mbedtls_asn1_bitstring;
+
+/**
+ * Container for a sequence of ASN.1 items
+ */
+typedef struct mbedtls_asn1_sequence
+{
+    mbedtls_asn1_buf buf;                   /**< Buffer containing the given ASN.1 item. */
+    struct mbedtls_asn1_sequence *next;    /**< The next entry in the sequence. */
+}
+mbedtls_asn1_sequence;
+
+/**
+ * Container for a sequence or list of 'named' ASN.1 data items
+ */
+typedef struct mbedtls_asn1_named_data
+{
+    mbedtls_asn1_buf oid;                   /**< The object identifier. */
+    mbedtls_asn1_buf val;                   /**< The named value. */
+    struct mbedtls_asn1_named_data *next;  /**< The next entry in the sequence. */
+    unsigned char next_merged;      /**< Merge next item into the current one? */
+}
+mbedtls_asn1_named_data;
+
+/**
+ * \brief       Get the length of an ASN.1 element.
+ *              Updates the pointer to immediately behind the length.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param len   The variable that will receive the value
+ *
+ * \return      0 if successful, MBEDTLS_ERR_ASN1_OUT_OF_DATA on reaching
+ *              end of data, MBEDTLS_ERR_ASN1_INVALID_LENGTH if length is
+ *              unparseable.
+ */
+int mbedtls_asn1_get_len( unsigned char **p,
+                  const unsigned char *end,
+                  size_t *len );
+
+/**
+ * \brief       Get the tag and length of the tag. Check for the requested tag.
+ *              Updates the pointer to immediately behind the tag and length.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param len   The variable that will receive the length
+ * \param tag   The expected tag
+ *
+ * \return      0 if successful, MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if tag did
+ *              not match requested tag, or another specific ASN.1 error code.
+ */
+int mbedtls_asn1_get_tag( unsigned char **p,
+                  const unsigned char *end,
+                  size_t *len, int tag );
+
+/**
+ * \brief       Retrieve a boolean ASN.1 tag and its value.
+ *              Updates the pointer to immediately behind the full tag.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param val   The variable that will receive the value
+ *
+ * \return      0 if successful or a specific ASN.1 error code.
+ */
+int mbedtls_asn1_get_bool( unsigned char **p,
+                   const unsigned char *end,
+                   int *val );
+
+/**
+ * \brief       Retrieve an integer ASN.1 tag and its value.
+ *              Updates the pointer to immediately behind the full tag.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param val   The variable that will receive the value
+ *
+ * \return      0 if successful or a specific ASN.1 error code.
+ */
+int mbedtls_asn1_get_int( unsigned char **p,
+                  const unsigned char *end,
+                  int *val );
+
+/**
+ * \brief       Retrieve a bitstring ASN.1 tag and its value.
+ *              Updates the pointer to immediately behind the full tag.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param bs    The variable that will receive the value
+ *
+ * \return      0 if successful or a specific ASN.1 error code.
+ */
+int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end,
+                        mbedtls_asn1_bitstring *bs);
+
+/**
+ * \brief       Retrieve a bitstring ASN.1 tag without unused bits and its
+ *              value.
+ *              Updates the pointer to the beginning of the bit/octet string.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param len   Length of the actual bit/octect string in bytes
+ *
+ * \return      0 if successful or a specific ASN.1 error code.
+ */
+int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end,
+                             size_t *len );
+
+/**
+ * \brief       Parses and splits an ASN.1 "SEQUENCE OF <tag>"
+ *              Updated the pointer to immediately behind the full sequence tag.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param cur   First variable in the chain to fill
+ * \param tag   Type of sequence
+ *
+ * \return      0 if successful or a specific ASN.1 error code.
+ */
+int mbedtls_asn1_get_sequence_of( unsigned char **p,
+                          const unsigned char *end,
+                          mbedtls_asn1_sequence *cur,
+                          int tag);
+
+#if defined(MBEDTLS_BIGNUM_C)
+/**
+ * \brief       Retrieve a MPI value from an integer ASN.1 tag.
+ *              Updates the pointer to immediately behind the full tag.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param X     The MPI that will receive the value
+ *
+ * \return      0 if successful or a specific ASN.1 or MPI error code.
+ */
+int mbedtls_asn1_get_mpi( unsigned char **p,
+                  const unsigned char *end,
+                  mbedtls_mpi *X );
+#endif /* MBEDTLS_BIGNUM_C */
+
+/**
+ * \brief       Retrieve an AlgorithmIdentifier ASN.1 sequence.
+ *              Updates the pointer to immediately behind the full
+ *              AlgorithmIdentifier.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param alg   The buffer to receive the OID
+ * \param params The buffer to receive the params (if any)
+ *
+ * \return      0 if successful or a specific ASN.1 or MPI error code.
+ */
+int mbedtls_asn1_get_alg( unsigned char **p,
+                  const unsigned char *end,
+                  mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params );
+
+/**
+ * \brief       Retrieve an AlgorithmIdentifier ASN.1 sequence with NULL or no
+ *              params.
+ *              Updates the pointer to immediately behind the full
+ *              AlgorithmIdentifier.
+ *
+ * \param p     The position in the ASN.1 data
+ * \param end   End of data
+ * \param alg   The buffer to receive the OID
+ *
+ * \return      0 if successful or a specific ASN.1 or MPI error code.
+ */
+int mbedtls_asn1_get_alg_null( unsigned char **p,
+                       const unsigned char *end,
+                       mbedtls_asn1_buf *alg );
+
+/**
+ * \brief       Find a specific named_data entry in a sequence or list based on
+ *              the OID.
+ *
+ * \param list  The list to seek through
+ * \param oid   The OID to look for
+ * \param len   Size of the OID
+ *
+ * \return      NULL if not found, or a pointer to the existing entry.
+ */
+mbedtls_asn1_named_data *mbedtls_asn1_find_named_data( mbedtls_asn1_named_data *list,
+                                       const char *oid, size_t len );
+
+/**
+ * \brief       Free a mbedtls_asn1_named_data entry
+ *
+ * \param entry The named data entry to free
+ */
+void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *entry );
+
+/**
+ * \brief       Free all entries in a mbedtls_asn1_named_data list
+ *              Head will be set to NULL
+ *
+ * \param head  Pointer to the head of the list of named data entries to free
+ */
+void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* asn1.h */

+ 98 - 0
Pal/lib/crypto/mbedtls/mbedtls/base64.h

@@ -0,0 +1,98 @@
+/**
+ * \file base64.h
+ *
+ * \brief RFC 1521 base64 encoding/decoding
+ */
+/*
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+#ifndef MBEDTLS_BASE64_H
+#define MBEDTLS_BASE64_H
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#include <stddef.h>
+
+#define MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL               -0x002A  /**< Output buffer too small. */
+#define MBEDTLS_ERR_BASE64_INVALID_CHARACTER              -0x002C  /**< Invalid character in input. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief          Encode a buffer into base64 format
+ *
+ * \param dst      destination buffer
+ * \param dlen     size of the destination buffer
+ * \param olen     number of bytes written
+ * \param src      source buffer
+ * \param slen     amount of data to be encoded
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
+ *                 *olen is always updated to reflect the amount
+ *                 of data that has (or would have) been written.
+ *                 If that length cannot be represented, then no data is
+ *                 written to the buffer and *olen is set to the maximum
+ *                 length representable as a size_t.
+ *
+ * \note           Call this function with dlen = 0 to obtain the
+ *                 required buffer size in *olen
+ */
+int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
+                   const unsigned char *src, size_t slen );
+
+/**
+ * \brief          Decode a base64-formatted buffer
+ *
+ * \param dst      destination buffer (can be NULL for checking size)
+ * \param dlen     size of the destination buffer
+ * \param olen     number of bytes written
+ * \param src      source buffer
+ * \param slen     amount of data to be decoded
+ *
+ * \return         0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
+ *                 MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
+ *                 not correct. *olen is always updated to reflect the amount
+ *                 of data that has (or would have) been written.
+ *
+ * \note           Call this function with *dst = NULL or dlen = 0 to obtain
+ *                 the required buffer size in *olen
+ */
+int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
+                   const unsigned char *src, size_t slen );
+
+#if defined(MBEDTLS_SELF_TEST)
+/**
+ * \brief          Checkup routine
+ *
+ * \return         0 if successful, or 1 if the test failed
+ */
+int mbedtls_base64_self_test( int verbose );
+
+#endif /* MBEDTLS_SELF_TEST */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* base64.h */

+ 5 - 2
Pal/lib/crypto/mbedtls/mbedtls/config.h

@@ -28,10 +28,13 @@
 #define MBEDTLS_HAVE_ASM
 #define MBEDTLS_HAVE_X86_64
 #define MBEDTLS_MD_C
-#define MBEDTLS_PKCS1_V15_BASIC
+#define MBEDTLS_PKCS1
+#define MBEDTLS_PKCS1_V15
+#define MBEDTLS_OID_C
 #define MBEDTLS_PLATFORM_C
 #define MBEDTLS_RSA_C
 #define MBEDTLS_SHA256_C
-#define MBEDTLS_PLATFORM_C
+#define MBEDTLS_BASE64_C
+#define MBEDTLS_ASN1_PARSE_C
 
 #endif

+ 580 - 0
Pal/lib/crypto/mbedtls/mbedtls/oid.h

@@ -0,0 +1,580 @@
+/**
+ * \file oid.h
+ *
+ * \brief Object Identifier (OID) database
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+#ifndef MBEDTLS_OID_H
+#define MBEDTLS_OID_H
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#include "asn1.h"
+
+typedef enum {
+    MBEDTLS_PK_NONE=0,
+    MBEDTLS_PK_RSA,
+    MBEDTLS_PK_ECKEY,
+    MBEDTLS_PK_ECKEY_DH,
+    MBEDTLS_PK_ECDSA,
+    MBEDTLS_PK_RSA_ALT,
+    MBEDTLS_PK_RSASSA_PSS,
+    MBEDTLS_PK_OPAQUE,
+} mbedtls_pk_type_t;
+
+#include <stddef.h>
+
+#if defined(MBEDTLS_CIPHER_C)
+#include "cipher.h"
+#endif
+
+#if defined(MBEDTLS_MD_C)
+#include "md.h"
+#endif
+
+#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
+#include "x509.h"
+#endif
+
+#define MBEDTLS_ERR_OID_NOT_FOUND                         -0x002E  /**< OID is not found. */
+#define MBEDTLS_ERR_OID_BUF_TOO_SMALL                     -0x000B  /**< output buffer is too small */
+
+/*
+ * Top level OID tuples
+ */
+#define MBEDTLS_OID_ISO_MEMBER_BODIES           "\x2a"          /* {iso(1) member-body(2)} */
+#define MBEDTLS_OID_ISO_IDENTIFIED_ORG          "\x2b"          /* {iso(1) identified-organization(3)} */
+#define MBEDTLS_OID_ISO_CCITT_DS                "\x55"          /* {joint-iso-ccitt(2) ds(5)} */
+#define MBEDTLS_OID_ISO_ITU_COUNTRY             "\x60"          /* {joint-iso-itu-t(2) country(16)} */
+
+/*
+ * ISO Member bodies OID parts
+ */
+#define MBEDTLS_OID_COUNTRY_US                  "\x86\x48"      /* {us(840)} */
+#define MBEDTLS_OID_ORG_RSA_DATA_SECURITY       "\x86\xf7\x0d"  /* {rsadsi(113549)} */
+#define MBEDTLS_OID_RSA_COMPANY                 MBEDTLS_OID_ISO_MEMBER_BODIES MBEDTLS_OID_COUNTRY_US \
+                                        MBEDTLS_OID_ORG_RSA_DATA_SECURITY /* {iso(1) member-body(2) us(840) rsadsi(113549)} */
+#define MBEDTLS_OID_ORG_ANSI_X9_62              "\xce\x3d" /* ansi-X9-62(10045) */
+#define MBEDTLS_OID_ANSI_X9_62                  MBEDTLS_OID_ISO_MEMBER_BODIES MBEDTLS_OID_COUNTRY_US \
+                                        MBEDTLS_OID_ORG_ANSI_X9_62
+
+/*
+ * ISO Identified organization OID parts
+ */
+#define MBEDTLS_OID_ORG_DOD                     "\x06"          /* {dod(6)} */
+#define MBEDTLS_OID_ORG_OIW                     "\x0e"
+#define MBEDTLS_OID_OIW_SECSIG                  MBEDTLS_OID_ORG_OIW "\x03"
+#define MBEDTLS_OID_OIW_SECSIG_ALG              MBEDTLS_OID_OIW_SECSIG "\x02"
+#define MBEDTLS_OID_OIW_SECSIG_SHA1             MBEDTLS_OID_OIW_SECSIG_ALG "\x1a"
+#define MBEDTLS_OID_ORG_CERTICOM                "\x81\x04"  /* certicom(132) */
+#define MBEDTLS_OID_CERTICOM                    MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_CERTICOM
+#define MBEDTLS_OID_ORG_TELETRUST               "\x24" /* teletrust(36) */
+#define MBEDTLS_OID_TELETRUST                   MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_TELETRUST
+
+/*
+ * ISO ITU OID parts
+ */
+#define MBEDTLS_OID_ORGANIZATION                "\x01"          /* {organization(1)} */
+#define MBEDTLS_OID_ISO_ITU_US_ORG              MBEDTLS_OID_ISO_ITU_COUNTRY MBEDTLS_OID_COUNTRY_US MBEDTLS_OID_ORGANIZATION /* {joint-iso-itu-t(2) country(16) us(840) organization(1)} */
+
+#define MBEDTLS_OID_ORG_GOV                     "\x65"          /* {gov(101)} */
+#define MBEDTLS_OID_GOV                         MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ORG_GOV /* {joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101)} */
+
+#define MBEDTLS_OID_ORG_NETSCAPE                "\x86\xF8\x42"  /* {netscape(113730)} */
+#define MBEDTLS_OID_NETSCAPE                    MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ORG_NETSCAPE /* Netscape OID {joint-iso-itu-t(2) country(16) us(840) organization(1) netscape(113730)} */
+
+/* ISO arc for standard certificate and CRL extensions */
+#define MBEDTLS_OID_ID_CE                       MBEDTLS_OID_ISO_CCITT_DS "\x1D" /**< id-ce OBJECT IDENTIFIER  ::=  {joint-iso-ccitt(2) ds(5) 29} */
+
+/**
+ * Private Internet Extensions
+ * { iso(1) identified-organization(3) dod(6) internet(1)
+ *                      security(5) mechanisms(5) pkix(7) }
+ */
+#define MBEDTLS_OID_PKIX                        MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_DOD "\x01\x05\x05\x07"
+
+/*
+ * Arc for standard naming attributes
+ */
+#define MBEDTLS_OID_AT                          MBEDTLS_OID_ISO_CCITT_DS "\x04" /**< id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} */
+#define MBEDTLS_OID_AT_CN                       MBEDTLS_OID_AT "\x03" /**< id-at-commonName AttributeType:= {id-at 3} */
+#define MBEDTLS_OID_AT_SUR_NAME                 MBEDTLS_OID_AT "\x04" /**< id-at-surName AttributeType:= {id-at 4} */
+#define MBEDTLS_OID_AT_SERIAL_NUMBER            MBEDTLS_OID_AT "\x05" /**< id-at-serialNumber AttributeType:= {id-at 5} */
+#define MBEDTLS_OID_AT_COUNTRY                  MBEDTLS_OID_AT "\x06" /**< id-at-countryName AttributeType:= {id-at 6} */
+#define MBEDTLS_OID_AT_LOCALITY                 MBEDTLS_OID_AT "\x07" /**< id-at-locality AttributeType:= {id-at 7} */
+#define MBEDTLS_OID_AT_STATE                    MBEDTLS_OID_AT "\x08" /**< id-at-state AttributeType:= {id-at 8} */
+#define MBEDTLS_OID_AT_ORGANIZATION             MBEDTLS_OID_AT "\x0A" /**< id-at-organizationName AttributeType:= {id-at 10} */
+#define MBEDTLS_OID_AT_ORG_UNIT                 MBEDTLS_OID_AT "\x0B" /**< id-at-organizationalUnitName AttributeType:= {id-at 11} */
+#define MBEDTLS_OID_AT_TITLE                    MBEDTLS_OID_AT "\x0C" /**< id-at-title AttributeType:= {id-at 12} */
+#define MBEDTLS_OID_AT_POSTAL_ADDRESS           MBEDTLS_OID_AT "\x10" /**< id-at-postalAddress AttributeType:= {id-at 16} */
+#define MBEDTLS_OID_AT_POSTAL_CODE              MBEDTLS_OID_AT "\x11" /**< id-at-postalCode AttributeType:= {id-at 17} */
+#define MBEDTLS_OID_AT_GIVEN_NAME               MBEDTLS_OID_AT "\x2A" /**< id-at-givenName AttributeType:= {id-at 42} */
+#define MBEDTLS_OID_AT_INITIALS                 MBEDTLS_OID_AT "\x2B" /**< id-at-initials AttributeType:= {id-at 43} */
+#define MBEDTLS_OID_AT_GENERATION_QUALIFIER     MBEDTLS_OID_AT "\x2C" /**< id-at-generationQualifier AttributeType:= {id-at 44} */
+#define MBEDTLS_OID_AT_UNIQUE_IDENTIFIER        MBEDTLS_OID_AT "\x2D" /**< id-at-uniqueIdentifier AttributType:= {id-at 45} */
+#define MBEDTLS_OID_AT_DN_QUALIFIER             MBEDTLS_OID_AT "\x2E" /**< id-at-dnQualifier AttributeType:= {id-at 46} */
+#define MBEDTLS_OID_AT_PSEUDONYM                MBEDTLS_OID_AT "\x41" /**< id-at-pseudonym AttributeType:= {id-at 65} */
+
+#define MBEDTLS_OID_DOMAIN_COMPONENT            "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19" /** id-domainComponent AttributeType:= {itu-t(0) data(9) pss(2342) ucl(19200300) pilot(100) pilotAttributeType(1) domainComponent(25)} */
+
+/*
+ * OIDs for standard certificate extensions
+ */
+#define MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER    MBEDTLS_OID_ID_CE "\x23" /**< id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 35 } */
+#define MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER      MBEDTLS_OID_ID_CE "\x0E" /**< id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 14 } */
+#define MBEDTLS_OID_KEY_USAGE                   MBEDTLS_OID_ID_CE "\x0F" /**< id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 } */
+#define MBEDTLS_OID_CERTIFICATE_POLICIES        MBEDTLS_OID_ID_CE "\x20" /**< id-ce-certificatePolicies OBJECT IDENTIFIER ::=  { id-ce 32 } */
+#define MBEDTLS_OID_POLICY_MAPPINGS             MBEDTLS_OID_ID_CE "\x21" /**< id-ce-policyMappings OBJECT IDENTIFIER ::=  { id-ce 33 } */
+#define MBEDTLS_OID_SUBJECT_ALT_NAME            MBEDTLS_OID_ID_CE "\x11" /**< id-ce-subjectAltName OBJECT IDENTIFIER ::=  { id-ce 17 } */
+#define MBEDTLS_OID_ISSUER_ALT_NAME             MBEDTLS_OID_ID_CE "\x12" /**< id-ce-issuerAltName OBJECT IDENTIFIER ::=  { id-ce 18 } */
+#define MBEDTLS_OID_SUBJECT_DIRECTORY_ATTRS     MBEDTLS_OID_ID_CE "\x09" /**< id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::=  { id-ce 9 } */
+#define MBEDTLS_OID_BASIC_CONSTRAINTS           MBEDTLS_OID_ID_CE "\x13" /**< id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 } */
+#define MBEDTLS_OID_NAME_CONSTRAINTS            MBEDTLS_OID_ID_CE "\x1E" /**< id-ce-nameConstraints OBJECT IDENTIFIER ::=  { id-ce 30 } */
+#define MBEDTLS_OID_POLICY_CONSTRAINTS          MBEDTLS_OID_ID_CE "\x24" /**< id-ce-policyConstraints OBJECT IDENTIFIER ::=  { id-ce 36 } */
+#define MBEDTLS_OID_EXTENDED_KEY_USAGE          MBEDTLS_OID_ID_CE "\x25" /**< id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } */
+#define MBEDTLS_OID_CRL_DISTRIBUTION_POINTS     MBEDTLS_OID_ID_CE "\x1F" /**< id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::=  { id-ce 31 } */
+#define MBEDTLS_OID_INIHIBIT_ANYPOLICY          MBEDTLS_OID_ID_CE "\x36" /**< id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::=  { id-ce 54 } */
+#define MBEDTLS_OID_FRESHEST_CRL                MBEDTLS_OID_ID_CE "\x2E" /**< id-ce-freshestCRL OBJECT IDENTIFIER ::=  { id-ce 46 } */
+
+/*
+ * Netscape certificate extensions
+ */
+#define MBEDTLS_OID_NS_CERT                 MBEDTLS_OID_NETSCAPE "\x01"
+#define MBEDTLS_OID_NS_CERT_TYPE            MBEDTLS_OID_NS_CERT  "\x01"
+#define MBEDTLS_OID_NS_BASE_URL             MBEDTLS_OID_NS_CERT  "\x02"
+#define MBEDTLS_OID_NS_REVOCATION_URL       MBEDTLS_OID_NS_CERT  "\x03"
+#define MBEDTLS_OID_NS_CA_REVOCATION_URL    MBEDTLS_OID_NS_CERT  "\x04"
+#define MBEDTLS_OID_NS_RENEWAL_URL          MBEDTLS_OID_NS_CERT  "\x07"
+#define MBEDTLS_OID_NS_CA_POLICY_URL        MBEDTLS_OID_NS_CERT  "\x08"
+#define MBEDTLS_OID_NS_SSL_SERVER_NAME      MBEDTLS_OID_NS_CERT  "\x0C"
+#define MBEDTLS_OID_NS_COMMENT              MBEDTLS_OID_NS_CERT  "\x0D"
+#define MBEDTLS_OID_NS_DATA_TYPE            MBEDTLS_OID_NETSCAPE "\x02"
+#define MBEDTLS_OID_NS_CERT_SEQUENCE        MBEDTLS_OID_NS_DATA_TYPE "\x05"
+
+/*
+ * OIDs for CRL extensions
+ */
+#define MBEDTLS_OID_PRIVATE_KEY_USAGE_PERIOD    MBEDTLS_OID_ID_CE "\x10"
+#define MBEDTLS_OID_CRL_NUMBER                  MBEDTLS_OID_ID_CE "\x14" /**< id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } */
+
+/*
+ * X.509 v3 Extended key usage OIDs
+ */
+#define MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE      MBEDTLS_OID_EXTENDED_KEY_USAGE "\x00" /**< anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } */
+
+#define MBEDTLS_OID_KP                          MBEDTLS_OID_PKIX "\x03" /**< id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } */
+#define MBEDTLS_OID_SERVER_AUTH                 MBEDTLS_OID_KP "\x01" /**< id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } */
+#define MBEDTLS_OID_CLIENT_AUTH                 MBEDTLS_OID_KP "\x02" /**< id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } */
+#define MBEDTLS_OID_CODE_SIGNING                MBEDTLS_OID_KP "\x03" /**< id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } */
+#define MBEDTLS_OID_EMAIL_PROTECTION            MBEDTLS_OID_KP "\x04" /**< id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } */
+#define MBEDTLS_OID_TIME_STAMPING               MBEDTLS_OID_KP "\x08" /**< id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } */
+#define MBEDTLS_OID_OCSP_SIGNING                MBEDTLS_OID_KP "\x09" /**< id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } */
+
+/*
+ * PKCS definition OIDs
+ */
+
+#define MBEDTLS_OID_PKCS                MBEDTLS_OID_RSA_COMPANY "\x01" /**< pkcs OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) 1 } */
+#define MBEDTLS_OID_PKCS1               MBEDTLS_OID_PKCS "\x01" /**< pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } */
+#define MBEDTLS_OID_PKCS5               MBEDTLS_OID_PKCS "\x05" /**< pkcs-5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } */
+#define MBEDTLS_OID_PKCS9               MBEDTLS_OID_PKCS "\x09" /**< pkcs-9 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } */
+#define MBEDTLS_OID_PKCS12              MBEDTLS_OID_PKCS "\x0c" /**< pkcs-12 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } */
+
+/*
+ * PKCS#1 OIDs
+ */
+#define MBEDTLS_OID_PKCS1_RSA           MBEDTLS_OID_PKCS1 "\x01" /**< rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } */
+#define MBEDTLS_OID_PKCS1_MD2           MBEDTLS_OID_PKCS1 "\x02" /**< md2WithRSAEncryption ::= { pkcs-1 2 } */
+#define MBEDTLS_OID_PKCS1_MD4           MBEDTLS_OID_PKCS1 "\x03" /**< md4WithRSAEncryption ::= { pkcs-1 3 } */
+#define MBEDTLS_OID_PKCS1_MD5           MBEDTLS_OID_PKCS1 "\x04" /**< md5WithRSAEncryption ::= { pkcs-1 4 } */
+#define MBEDTLS_OID_PKCS1_SHA1          MBEDTLS_OID_PKCS1 "\x05" /**< sha1WithRSAEncryption ::= { pkcs-1 5 } */
+#define MBEDTLS_OID_PKCS1_SHA224        MBEDTLS_OID_PKCS1 "\x0e" /**< sha224WithRSAEncryption ::= { pkcs-1 14 } */
+#define MBEDTLS_OID_PKCS1_SHA256        MBEDTLS_OID_PKCS1 "\x0b" /**< sha256WithRSAEncryption ::= { pkcs-1 11 } */
+#define MBEDTLS_OID_PKCS1_SHA384        MBEDTLS_OID_PKCS1 "\x0c" /**< sha384WithRSAEncryption ::= { pkcs-1 12 } */
+#define MBEDTLS_OID_PKCS1_SHA512        MBEDTLS_OID_PKCS1 "\x0d" /**< sha512WithRSAEncryption ::= { pkcs-1 13 } */
+
+#define MBEDTLS_OID_RSA_SHA_OBS         "\x2B\x0E\x03\x02\x1D"
+
+#define MBEDTLS_OID_PKCS9_EMAIL         MBEDTLS_OID_PKCS9 "\x01" /**< emailAddress AttributeType ::= { pkcs-9 1 } */
+
+/* RFC 4055 */
+#define MBEDTLS_OID_RSASSA_PSS          MBEDTLS_OID_PKCS1 "\x0a" /**< id-RSASSA-PSS ::= { pkcs-1 10 } */
+#define MBEDTLS_OID_MGF1                MBEDTLS_OID_PKCS1 "\x08" /**< id-mgf1 ::= { pkcs-1 8 } */
+
+/*
+ * Digest algorithms
+ */
+#define MBEDTLS_OID_DIGEST_ALG_MD2              MBEDTLS_OID_RSA_COMPANY "\x02\x02" /**< id-mbedtls_md2 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 2 } */
+#define MBEDTLS_OID_DIGEST_ALG_MD4              MBEDTLS_OID_RSA_COMPANY "\x02\x04" /**< id-mbedtls_md4 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 4 } */
+#define MBEDTLS_OID_DIGEST_ALG_MD5              MBEDTLS_OID_RSA_COMPANY "\x02\x05" /**< id-mbedtls_md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 } */
+#define MBEDTLS_OID_DIGEST_ALG_SHA1             MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_OIW_SECSIG_SHA1 /**< id-mbedtls_sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } */
+#define MBEDTLS_OID_DIGEST_ALG_SHA224           MBEDTLS_OID_GOV "\x03\x04\x02\x04" /**< id-sha224 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 4 } */
+#define MBEDTLS_OID_DIGEST_ALG_SHA256           MBEDTLS_OID_GOV "\x03\x04\x02\x01" /**< id-mbedtls_sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 } */
+
+#define MBEDTLS_OID_DIGEST_ALG_SHA384           MBEDTLS_OID_GOV "\x03\x04\x02\x02" /**< id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2 } */
+
+#define MBEDTLS_OID_DIGEST_ALG_SHA512           MBEDTLS_OID_GOV "\x03\x04\x02\x03" /**< id-mbedtls_sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 } */
+
+#define MBEDTLS_OID_HMAC_SHA1                   MBEDTLS_OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */
+
+/*
+ * Encryption algorithms
+ */
+#define MBEDTLS_OID_DES_CBC                     MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_OIW_SECSIG_ALG "\x07" /**< desCBC OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 7 } */
+#define MBEDTLS_OID_DES_EDE3_CBC                MBEDTLS_OID_RSA_COMPANY "\x03\x07" /**< des-ede3-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) -- us(840) rsadsi(113549) encryptionAlgorithm(3) 7 } */
+
+/*
+ * PKCS#5 OIDs
+ */
+#define MBEDTLS_OID_PKCS5_PBKDF2                MBEDTLS_OID_PKCS5 "\x0c" /**< id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} */
+#define MBEDTLS_OID_PKCS5_PBES2                 MBEDTLS_OID_PKCS5 "\x0d" /**< id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} */
+#define MBEDTLS_OID_PKCS5_PBMAC1                MBEDTLS_OID_PKCS5 "\x0e" /**< id-PBMAC1 OBJECT IDENTIFIER ::= {pkcs-5 14} */
+
+/*
+ * PKCS#5 PBES1 algorithms
+ */
+#define MBEDTLS_OID_PKCS5_PBE_MD2_DES_CBC       MBEDTLS_OID_PKCS5 "\x01" /**< pbeWithMD2AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 1} */
+#define MBEDTLS_OID_PKCS5_PBE_MD2_RC2_CBC       MBEDTLS_OID_PKCS5 "\x04" /**< pbeWithMD2AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 4} */
+#define MBEDTLS_OID_PKCS5_PBE_MD5_DES_CBC       MBEDTLS_OID_PKCS5 "\x03" /**< pbeWithMD5AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 3} */
+#define MBEDTLS_OID_PKCS5_PBE_MD5_RC2_CBC       MBEDTLS_OID_PKCS5 "\x06" /**< pbeWithMD5AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 6} */
+#define MBEDTLS_OID_PKCS5_PBE_SHA1_DES_CBC      MBEDTLS_OID_PKCS5 "\x0a" /**< pbeWithSHA1AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 10} */
+#define MBEDTLS_OID_PKCS5_PBE_SHA1_RC2_CBC      MBEDTLS_OID_PKCS5 "\x0b" /**< pbeWithSHA1AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 11} */
+
+/*
+ * PKCS#8 OIDs
+ */
+#define MBEDTLS_OID_PKCS9_CSR_EXT_REQ           MBEDTLS_OID_PKCS9 "\x0e" /**< extensionRequest OBJECT IDENTIFIER ::= {pkcs-9 14} */
+
+/*
+ * PKCS#12 PBE OIDs
+ */
+#define MBEDTLS_OID_PKCS12_PBE                      MBEDTLS_OID_PKCS12 "\x01" /**< pkcs-12PbeIds OBJECT IDENTIFIER ::= {pkcs-12 1} */
+
+#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC4_128         MBEDTLS_OID_PKCS12_PBE "\x01" /**< pbeWithSHAAnd128BitRC4 OBJECT IDENTIFIER ::= {pkcs-12PbeIds 1} */
+#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC4_40          MBEDTLS_OID_PKCS12_PBE "\x02" /**< pbeWithSHAAnd40BitRC4 OBJECT IDENTIFIER ::= {pkcs-12PbeIds 2} */
+#define MBEDTLS_OID_PKCS12_PBE_SHA1_DES3_EDE_CBC    MBEDTLS_OID_PKCS12_PBE "\x03" /**< pbeWithSHAAnd3-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 3} */
+#define MBEDTLS_OID_PKCS12_PBE_SHA1_DES2_EDE_CBC    MBEDTLS_OID_PKCS12_PBE "\x04" /**< pbeWithSHAAnd2-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 4} */
+#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC2_128_CBC     MBEDTLS_OID_PKCS12_PBE "\x05" /**< pbeWithSHAAnd128BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 5} */
+#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC2_40_CBC      MBEDTLS_OID_PKCS12_PBE "\x06" /**< pbeWithSHAAnd40BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 6} */
+
+/*
+ * EC key algorithms from RFC 5480
+ */
+
+/* id-ecPublicKey OBJECT IDENTIFIER ::= {
+ *       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } */
+#define MBEDTLS_OID_EC_ALG_UNRESTRICTED         MBEDTLS_OID_ANSI_X9_62 "\x02\01"
+
+/*   id-ecDH OBJECT IDENTIFIER ::= {
+ *     iso(1) identified-organization(3) certicom(132)
+ *     schemes(1) ecdh(12) } */
+#define MBEDTLS_OID_EC_ALG_ECDH                 MBEDTLS_OID_CERTICOM "\x01\x0c"
+
+/*
+ * ECParameters namedCurve identifiers, from RFC 5480, RFC 5639, and SEC2
+ */
+
+/* secp192r1 OBJECT IDENTIFIER ::= {
+ *   iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 1 } */
+#define MBEDTLS_OID_EC_GRP_SECP192R1        MBEDTLS_OID_ANSI_X9_62 "\x03\x01\x01"
+
+/* secp224r1 OBJECT IDENTIFIER ::= {
+ *   iso(1) identified-organization(3) certicom(132) curve(0) 33 } */
+#define MBEDTLS_OID_EC_GRP_SECP224R1        MBEDTLS_OID_CERTICOM "\x00\x21"
+
+/* secp256r1 OBJECT IDENTIFIER ::= {
+ *   iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 7 } */
+#define MBEDTLS_OID_EC_GRP_SECP256R1        MBEDTLS_OID_ANSI_X9_62 "\x03\x01\x07"
+
+/* secp384r1 OBJECT IDENTIFIER ::= {
+ *   iso(1) identified-organization(3) certicom(132) curve(0) 34 } */
+#define MBEDTLS_OID_EC_GRP_SECP384R1        MBEDTLS_OID_CERTICOM "\x00\x22"
+
+/* secp521r1 OBJECT IDENTIFIER ::= {
+ *   iso(1) identified-organization(3) certicom(132) curve(0) 35 } */
+#define MBEDTLS_OID_EC_GRP_SECP521R1        MBEDTLS_OID_CERTICOM "\x00\x23"
+
+/* secp192k1 OBJECT IDENTIFIER ::= {
+ *   iso(1) identified-organization(3) certicom(132) curve(0) 31 } */
+#define MBEDTLS_OID_EC_GRP_SECP192K1        MBEDTLS_OID_CERTICOM "\x00\x1f"
+
+/* secp224k1 OBJECT IDENTIFIER ::= {
+ *   iso(1) identified-organization(3) certicom(132) curve(0) 32 } */
+#define MBEDTLS_OID_EC_GRP_SECP224K1        MBEDTLS_OID_CERTICOM "\x00\x20"
+
+/* secp256k1 OBJECT IDENTIFIER ::= {
+ *   iso(1) identified-organization(3) certicom(132) curve(0) 10 } */
+#define MBEDTLS_OID_EC_GRP_SECP256K1        MBEDTLS_OID_CERTICOM "\x00\x0a"
+
+/* RFC 5639 4.1
+ * ecStdCurvesAndGeneration OBJECT IDENTIFIER::= {iso(1)
+ * identified-organization(3) teletrust(36) algorithm(3) signature-
+ * algorithm(3) ecSign(2) 8}
+ * ellipticCurve OBJECT IDENTIFIER ::= {ecStdCurvesAndGeneration 1}
+ * versionOne OBJECT IDENTIFIER ::= {ellipticCurve 1} */
+#define MBEDTLS_OID_EC_BRAINPOOL_V1         MBEDTLS_OID_TELETRUST "\x03\x03\x02\x08\x01\x01"
+
+/* brainpoolP256r1 OBJECT IDENTIFIER ::= {versionOne 7} */
+#define MBEDTLS_OID_EC_GRP_BP256R1          MBEDTLS_OID_EC_BRAINPOOL_V1 "\x07"
+
+/* brainpoolP384r1 OBJECT IDENTIFIER ::= {versionOne 11} */
+#define MBEDTLS_OID_EC_GRP_BP384R1          MBEDTLS_OID_EC_BRAINPOOL_V1 "\x0B"
+
+/* brainpoolP512r1 OBJECT IDENTIFIER ::= {versionOne 13} */
+#define MBEDTLS_OID_EC_GRP_BP512R1          MBEDTLS_OID_EC_BRAINPOOL_V1 "\x0D"
+
+/*
+ * SEC1 C.1
+ *
+ * prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 }
+ * id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1)}
+ */
+#define MBEDTLS_OID_ANSI_X9_62_FIELD_TYPE   MBEDTLS_OID_ANSI_X9_62 "\x01"
+#define MBEDTLS_OID_ANSI_X9_62_PRIME_FIELD  MBEDTLS_OID_ANSI_X9_62_FIELD_TYPE "\x01"
+
+/*
+ * ECDSA signature identifiers, from RFC 5480
+ */
+#define MBEDTLS_OID_ANSI_X9_62_SIG          MBEDTLS_OID_ANSI_X9_62 "\x04" /* signatures(4) */
+#define MBEDTLS_OID_ANSI_X9_62_SIG_SHA2     MBEDTLS_OID_ANSI_X9_62_SIG "\x03" /* ecdsa-with-SHA2(3) */
+
+/* ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
+ *   iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) 1 } */
+#define MBEDTLS_OID_ECDSA_SHA1              MBEDTLS_OID_ANSI_X9_62_SIG "\x01"
+
+/* ecdsa-with-SHA224 OBJECT IDENTIFIER ::= {
+ *   iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+ *   ecdsa-with-SHA2(3) 1 } */
+#define MBEDTLS_OID_ECDSA_SHA224            MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x01"
+
+/* ecdsa-with-SHA256 OBJECT IDENTIFIER ::= {
+ *   iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+ *   ecdsa-with-SHA2(3) 2 } */
+#define MBEDTLS_OID_ECDSA_SHA256            MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x02"
+
+/* ecdsa-with-SHA384 OBJECT IDENTIFIER ::= {
+ *   iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+ *   ecdsa-with-SHA2(3) 3 } */
+#define MBEDTLS_OID_ECDSA_SHA384            MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x03"
+
+/* ecdsa-with-SHA512 OBJECT IDENTIFIER ::= {
+ *   iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+ *   ecdsa-with-SHA2(3) 4 } */
+#define MBEDTLS_OID_ECDSA_SHA512            MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x04"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Base OID descriptor structure
+ */
+typedef struct {
+    const char *asn1;               /*!< OID ASN.1 representation       */
+    size_t asn1_len;                /*!< length of asn1                 */
+    const char *name;               /*!< official name (e.g. from RFC)  */
+    const char *description;        /*!< human friendly description     */
+} mbedtls_oid_descriptor_t;
+
+/**
+ * \brief           Translate an ASN.1 OID into its numeric representation
+ *                  (e.g. "\x2A\x86\x48\x86\xF7\x0D" into "1.2.840.113549")
+ *
+ * \param buf       buffer to put representation in
+ * \param size      size of the buffer
+ * \param oid       OID to translate
+ *
+ * \return          Length of the string written (excluding final NULL) or
+ *                  MBEDTLS_ERR_OID_BUF_TOO_SMALL in case of error
+ */
+int mbedtls_oid_get_numeric_string( char *buf, size_t size, const mbedtls_asn1_buf *oid );
+
+#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
+/**
+ * \brief          Translate an X.509 extension OID into local values
+ *
+ * \param oid      OID to use
+ * \param ext_type place to store the extension type
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_x509_ext_type( const mbedtls_asn1_buf *oid, int *ext_type );
+#endif
+
+/**
+ * \brief          Translate an X.509 attribute type OID into the short name
+ *                 (e.g. the OID for an X520 Common Name into "CN")
+ *
+ * \param oid      OID to use
+ * \param short_name    place to store the string pointer
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_attr_short_name( const mbedtls_asn1_buf *oid, const char **short_name );
+
+/**
+ * \brief          Translate PublicKeyAlgorithm OID into pk_type
+ *
+ * \param oid      OID to use
+ * \param pk_alg   place to store public key algorithm
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_pk_alg( const mbedtls_asn1_buf *oid, mbedtls_pk_type_t *pk_alg );
+
+/**
+ * \brief          Translate pk_type into PublicKeyAlgorithm OID
+ *
+ * \param pk_alg   Public key type to look for
+ * \param oid      place to store ASN.1 OID string pointer
+ * \param olen     length of the OID
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_oid_by_pk_alg( mbedtls_pk_type_t pk_alg,
+                           const char **oid, size_t *olen );
+
+#if defined(MBEDTLS_ECP_C)
+/**
+ * \brief          Translate NamedCurve OID into an EC group identifier
+ *
+ * \param oid      OID to use
+ * \param grp_id   place to store group id
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_ec_grp( const mbedtls_asn1_buf *oid, mbedtls_ecp_group_id *grp_id );
+
+/**
+ * \brief          Translate EC group identifier into NamedCurve OID
+ *
+ * \param grp_id   EC group identifier
+ * \param oid      place to store ASN.1 OID string pointer
+ * \param olen     length of the OID
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_oid_by_ec_grp( mbedtls_ecp_group_id grp_id,
+                           const char **oid, size_t *olen );
+#endif /* MBEDTLS_ECP_C */
+
+#if defined(MBEDTLS_MD_C)
+/**
+ * \brief          Translate SignatureAlgorithm OID into md_type and pk_type
+ *
+ * \param oid      OID to use
+ * \param md_alg   place to store message digest algorithm
+ * \param pk_alg   place to store public key algorithm
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_sig_alg( const mbedtls_asn1_buf *oid,
+                     mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg );
+
+/**
+ * \brief          Translate SignatureAlgorithm OID into description
+ *
+ * \param oid      OID to use
+ * \param desc     place to store string pointer
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_sig_alg_desc( const mbedtls_asn1_buf *oid, const char **desc );
+
+/**
+ * \brief          Translate md_type and pk_type into SignatureAlgorithm OID
+ *
+ * \param md_alg   message digest algorithm
+ * \param pk_alg   public key algorithm
+ * \param oid      place to store ASN.1 OID string pointer
+ * \param olen     length of the OID
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_oid_by_sig_alg( mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg,
+                            const char **oid, size_t *olen );
+
+/**
+ * \brief          Translate hash algorithm OID into md_type
+ *
+ * \param oid      OID to use
+ * \param md_alg   place to store message digest algorithm
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_md_alg( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg );
+#endif /* MBEDTLS_MD_C */
+
+/**
+ * \brief          Translate Extended Key Usage OID into description
+ *
+ * \param oid      OID to use
+ * \param desc     place to store string pointer
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_extended_key_usage( const mbedtls_asn1_buf *oid, const char **desc );
+
+/**
+ * \brief          Translate md_type into hash algorithm OID
+ *
+ * \param md_alg   message digest algorithm
+ * \param oid      place to store ASN.1 OID string pointer
+ * \param olen     length of the OID
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_oid_by_md( mbedtls_md_type_t md_alg, const char **oid, size_t *olen );
+
+#if defined(MBEDTLS_CIPHER_C)
+/**
+ * \brief          Translate encryption algorithm OID into cipher_type
+ *
+ * \param oid           OID to use
+ * \param cipher_alg    place to store cipher algorithm
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_cipher_alg( const mbedtls_asn1_buf *oid, mbedtls_cipher_type_t *cipher_alg );
+#endif /* MBEDTLS_CIPHER_C */
+
+#if defined(MBEDTLS_PKCS12_C)
+/**
+ * \brief          Translate PKCS#12 PBE algorithm OID into md_type and
+ *                 cipher_type
+ *
+ * \param oid           OID to use
+ * \param md_alg        place to store message digest algorithm
+ * \param cipher_alg    place to store cipher algorithm
+ *
+ * \return         0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_pkcs12_pbe_alg( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg,
+                            mbedtls_cipher_type_t *cipher_alg );
+#endif /* MBEDTLS_PKCS12_C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* oid.h */

+ 2 - 1
Pal/lib/crypto/mbedtls/mbedtls/platform.h

@@ -27,5 +27,6 @@ void free(void *);
 
 #define mbedtls_calloc calloc
 #define mbedtls_free free
-
+#define mbedtls_snprintf snprintf
+#define mbedtls_platform_zeroize(buf, len) do { memset((buf), 0, (len)); } while (0)
 #endif

+ 2 - 3
Pal/lib/crypto/mbedtls/md.c

@@ -37,13 +37,10 @@
 #if defined(MBEDTLS_PLATFORM_C)
 #include "mbedtls/platform.h"
 #else
-#include <stdlib.h>
 #define mbedtls_calloc    calloc
 #define mbedtls_free       free
 #endif
 
-#include <string.h>
-
 #if defined(MBEDTLS_FS_IO)
 #include <stdio.h>
 #endif
@@ -96,6 +93,7 @@ const int *mbedtls_md_list( void )
     return( supported_digests );
 }
 
+#if 0
 const mbedtls_md_info_t *mbedtls_md_info_from_string( const char *md_name )
 {
     if( NULL == md_name )
@@ -136,6 +134,7 @@ const mbedtls_md_info_t *mbedtls_md_info_from_string( const char *md_name )
 #endif
     return( NULL );
 }
+#endif
 
 const mbedtls_md_info_t *mbedtls_md_info_from_type( mbedtls_md_type_t md_type )
 {

+ 575 - 0
Pal/lib/crypto/mbedtls/md_wrap.c

@@ -0,0 +1,575 @@
+/**
+ * \file md_wrap.c
+ *
+ * \brief Generic message digest wrapper for mbed TLS
+ *
+ * \author Adriaan de Jong <dejong@fox-it.com>
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#if defined(MBEDTLS_MD_C)
+
+#include "mbedtls/md_internal.h"
+
+#if defined(MBEDTLS_MD2_C)
+#include "mbedtls/md2.h"
+#endif
+
+#if defined(MBEDTLS_MD4_C)
+#include "mbedtls/md4.h"
+#endif
+
+#if defined(MBEDTLS_MD5_C)
+#include "mbedtls/md5.h"
+#endif
+
+#if defined(MBEDTLS_RIPEMD160_C)
+#include "mbedtls/ripemd160.h"
+#endif
+
+#if defined(MBEDTLS_SHA1_C)
+#include "mbedtls/sha1.h"
+#endif
+
+#if defined(MBEDTLS_SHA256_C)
+#include "mbedtls/sha256.h"
+#endif
+
+#if defined(MBEDTLS_SHA512_C)
+#include "mbedtls/sha512.h"
+#endif
+
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#include <stdlib.h>
+#define mbedtls_calloc    calloc
+#define mbedtls_free       free
+#endif
+
+#if defined(MBEDTLS_MD2_C)
+
+static void md2_starts_wrap( void *ctx )
+{
+    mbedtls_md2_starts( (mbedtls_md2_context *) ctx );
+}
+
+static void md2_update_wrap( void *ctx, const unsigned char *input,
+                             size_t ilen )
+{
+    mbedtls_md2_update( (mbedtls_md2_context *) ctx, input, ilen );
+}
+
+static void md2_finish_wrap( void *ctx, unsigned char *output )
+{
+    mbedtls_md2_finish( (mbedtls_md2_context *) ctx, output );
+}
+
+static void *md2_ctx_alloc( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( mbedtls_md2_context ) );
+
+    if( ctx != NULL )
+        mbedtls_md2_init( (mbedtls_md2_context *) ctx );
+
+    return( ctx );
+}
+
+static void md2_ctx_free( void *ctx )
+{
+    mbedtls_md2_free( (mbedtls_md2_context *) ctx );
+    mbedtls_free( ctx );
+}
+
+static void md2_clone_wrap( void *dst, const void *src )
+{
+    mbedtls_md2_clone( (mbedtls_md2_context *) dst,
+                 (const mbedtls_md2_context *) src );
+}
+
+static void md2_process_wrap( void *ctx, const unsigned char *data )
+{
+    ((void) data);
+
+    mbedtls_md2_process( (mbedtls_md2_context *) ctx );
+}
+
+const mbedtls_md_info_t mbedtls_md2_info = {
+    MBEDTLS_MD_MD2,
+    "MD2",
+    16,
+    16,
+    md2_starts_wrap,
+    md2_update_wrap,
+    md2_finish_wrap,
+    mbedtls_md2,
+    md2_ctx_alloc,
+    md2_ctx_free,
+    md2_clone_wrap,
+    md2_process_wrap,
+};
+
+#endif /* MBEDTLS_MD2_C */
+
+#if defined(MBEDTLS_MD4_C)
+
+static void md4_starts_wrap( void *ctx )
+{
+    mbedtls_md4_starts( (mbedtls_md4_context *) ctx );
+}
+
+static void md4_update_wrap( void *ctx, const unsigned char *input,
+                             size_t ilen )
+{
+    mbedtls_md4_update( (mbedtls_md4_context *) ctx, input, ilen );
+}
+
+static void md4_finish_wrap( void *ctx, unsigned char *output )
+{
+    mbedtls_md4_finish( (mbedtls_md4_context *) ctx, output );
+}
+
+static void *md4_ctx_alloc( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( mbedtls_md4_context ) );
+
+    if( ctx != NULL )
+        mbedtls_md4_init( (mbedtls_md4_context *) ctx );
+
+    return( ctx );
+}
+
+static void md4_ctx_free( void *ctx )
+{
+    mbedtls_md4_free( (mbedtls_md4_context *) ctx );
+    mbedtls_free( ctx );
+}
+
+static void md4_clone_wrap( void *dst, const void *src )
+{
+    mbedtls_md4_clone( (mbedtls_md4_context *) dst,
+                 (const mbedtls_md4_context *) src );
+}
+
+static void md4_process_wrap( void *ctx, const unsigned char *data )
+{
+    mbedtls_md4_process( (mbedtls_md4_context *) ctx, data );
+}
+
+const mbedtls_md_info_t mbedtls_md4_info = {
+    MBEDTLS_MD_MD4,
+    "MD4",
+    16,
+    64,
+    md4_starts_wrap,
+    md4_update_wrap,
+    md4_finish_wrap,
+    mbedtls_md4,
+    md4_ctx_alloc,
+    md4_ctx_free,
+    md4_clone_wrap,
+    md4_process_wrap,
+};
+
+#endif /* MBEDTLS_MD4_C */
+
+#if defined(MBEDTLS_MD5_C)
+
+static void md5_starts_wrap( void *ctx )
+{
+    mbedtls_md5_starts( (mbedtls_md5_context *) ctx );
+}
+
+static void md5_update_wrap( void *ctx, const unsigned char *input,
+                             size_t ilen )
+{
+    mbedtls_md5_update( (mbedtls_md5_context *) ctx, input, ilen );
+}
+
+static void md5_finish_wrap( void *ctx, unsigned char *output )
+{
+    mbedtls_md5_finish( (mbedtls_md5_context *) ctx, output );
+}
+
+static void *md5_ctx_alloc( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( mbedtls_md5_context ) );
+
+    if( ctx != NULL )
+        mbedtls_md5_init( (mbedtls_md5_context *) ctx );
+
+    return( ctx );
+}
+
+static void md5_ctx_free( void *ctx )
+{
+    mbedtls_md5_free( (mbedtls_md5_context *) ctx );
+    mbedtls_free( ctx );
+}
+
+static void md5_clone_wrap( void *dst, const void *src )
+{
+    mbedtls_md5_clone( (mbedtls_md5_context *) dst,
+                 (const mbedtls_md5_context *) src );
+}
+
+static void md5_process_wrap( void *ctx, const unsigned char *data )
+{
+    mbedtls_md5_process( (mbedtls_md5_context *) ctx, data );
+}
+
+const mbedtls_md_info_t mbedtls_md5_info = {
+    MBEDTLS_MD_MD5,
+    "MD5",
+    16,
+    64,
+    md5_starts_wrap,
+    md5_update_wrap,
+    md5_finish_wrap,
+    mbedtls_md5,
+    md5_ctx_alloc,
+    md5_ctx_free,
+    md5_clone_wrap,
+    md5_process_wrap,
+};
+
+#endif /* MBEDTLS_MD5_C */
+
+#if defined(MBEDTLS_RIPEMD160_C)
+
+static void ripemd160_starts_wrap( void *ctx )
+{
+    mbedtls_ripemd160_starts( (mbedtls_ripemd160_context *) ctx );
+}
+
+static void ripemd160_update_wrap( void *ctx, const unsigned char *input,
+                                   size_t ilen )
+{
+    mbedtls_ripemd160_update( (mbedtls_ripemd160_context *) ctx, input, ilen );
+}
+
+static void ripemd160_finish_wrap( void *ctx, unsigned char *output )
+{
+    mbedtls_ripemd160_finish( (mbedtls_ripemd160_context *) ctx, output );
+}
+
+static void *ripemd160_ctx_alloc( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( mbedtls_ripemd160_context ) );
+
+    if( ctx != NULL )
+        mbedtls_ripemd160_init( (mbedtls_ripemd160_context *) ctx );
+
+    return( ctx );
+}
+
+static void ripemd160_ctx_free( void *ctx )
+{
+    mbedtls_ripemd160_free( (mbedtls_ripemd160_context *) ctx );
+    mbedtls_free( ctx );
+}
+
+static void ripemd160_clone_wrap( void *dst, const void *src )
+{
+    mbedtls_ripemd160_clone( (mbedtls_ripemd160_context *) dst,
+                       (const mbedtls_ripemd160_context *) src );
+}
+
+static void ripemd160_process_wrap( void *ctx, const unsigned char *data )
+{
+    mbedtls_ripemd160_process( (mbedtls_ripemd160_context *) ctx, data );
+}
+
+const mbedtls_md_info_t mbedtls_ripemd160_info = {
+    MBEDTLS_MD_RIPEMD160,
+    "RIPEMD160",
+    20,
+    64,
+    ripemd160_starts_wrap,
+    ripemd160_update_wrap,
+    ripemd160_finish_wrap,
+    mbedtls_ripemd160,
+    ripemd160_ctx_alloc,
+    ripemd160_ctx_free,
+    ripemd160_clone_wrap,
+    ripemd160_process_wrap,
+};
+
+#endif /* MBEDTLS_RIPEMD160_C */
+
+#if defined(MBEDTLS_SHA1_C)
+
+static void sha1_starts_wrap( void *ctx )
+{
+    mbedtls_sha1_starts( (mbedtls_sha1_context *) ctx );
+}
+
+static void sha1_update_wrap( void *ctx, const unsigned char *input,
+                              size_t ilen )
+{
+    mbedtls_sha1_update( (mbedtls_sha1_context *) ctx, input, ilen );
+}
+
+static void sha1_finish_wrap( void *ctx, unsigned char *output )
+{
+    mbedtls_sha1_finish( (mbedtls_sha1_context *) ctx, output );
+}
+
+static void *sha1_ctx_alloc( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( mbedtls_sha1_context ) );
+
+    if( ctx != NULL )
+        mbedtls_sha1_init( (mbedtls_sha1_context *) ctx );
+
+    return( ctx );
+}
+
+static void sha1_clone_wrap( void *dst, const void *src )
+{
+    mbedtls_sha1_clone( (mbedtls_sha1_context *) dst,
+                  (const mbedtls_sha1_context *) src );
+}
+
+static void sha1_ctx_free( void *ctx )
+{
+    mbedtls_sha1_free( (mbedtls_sha1_context *) ctx );
+    mbedtls_free( ctx );
+}
+
+static void sha1_process_wrap( void *ctx, const unsigned char *data )
+{
+    mbedtls_sha1_process( (mbedtls_sha1_context *) ctx, data );
+}
+
+const mbedtls_md_info_t mbedtls_sha1_info = {
+    MBEDTLS_MD_SHA1,
+    "SHA1",
+    20,
+    64,
+    sha1_starts_wrap,
+    sha1_update_wrap,
+    sha1_finish_wrap,
+    mbedtls_sha1,
+    sha1_ctx_alloc,
+    sha1_ctx_free,
+    sha1_clone_wrap,
+    sha1_process_wrap,
+};
+
+#endif /* MBEDTLS_SHA1_C */
+
+/*
+ * Wrappers for generic message digests
+ */
+#if defined(MBEDTLS_SHA256_C)
+
+static void sha224_starts_wrap( void *ctx )
+{
+    mbedtls_sha256_starts( (mbedtls_sha256_context *) ctx, 1 );
+}
+
+static void sha224_update_wrap( void *ctx, const unsigned char *input,
+                                size_t ilen )
+{
+    mbedtls_sha256_update( (mbedtls_sha256_context *) ctx, input, ilen );
+}
+
+static void sha224_finish_wrap( void *ctx, unsigned char *output )
+{
+    mbedtls_sha256_finish( (mbedtls_sha256_context *) ctx, output );
+}
+
+static void sha224_wrap( const unsigned char *input, size_t ilen,
+                    unsigned char *output )
+{
+    mbedtls_sha256( input, ilen, output, 1 );
+}
+
+static void *sha224_ctx_alloc( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( mbedtls_sha256_context ) );
+
+    if( ctx != NULL )
+        mbedtls_sha256_init( (mbedtls_sha256_context *) ctx );
+
+    return( ctx );
+}
+
+static void sha224_ctx_free( void *ctx )
+{
+    mbedtls_sha256_free( (mbedtls_sha256_context *) ctx );
+    mbedtls_free( ctx );
+}
+
+static void sha224_clone_wrap( void *dst, const void *src )
+{
+    mbedtls_sha256_clone( (mbedtls_sha256_context *) dst,
+                    (const mbedtls_sha256_context *) src );
+}
+
+static void sha224_process_wrap( void *ctx, const unsigned char *data )
+{
+    mbedtls_sha256_process( (mbedtls_sha256_context *) ctx, data );
+}
+
+const mbedtls_md_info_t mbedtls_sha224_info = {
+    MBEDTLS_MD_SHA224,
+    "SHA224",
+    28,
+    64,
+    sha224_starts_wrap,
+    sha224_update_wrap,
+    sha224_finish_wrap,
+    sha224_wrap,
+    sha224_ctx_alloc,
+    sha224_ctx_free,
+    sha224_clone_wrap,
+    sha224_process_wrap,
+};
+
+static void sha256_starts_wrap( void *ctx )
+{
+    mbedtls_sha256_starts( (mbedtls_sha256_context *) ctx, 0 );
+}
+
+static void sha256_wrap( const unsigned char *input, size_t ilen,
+                    unsigned char *output )
+{
+    mbedtls_sha256( input, ilen, output, 0 );
+}
+
+const mbedtls_md_info_t mbedtls_sha256_info = {
+    MBEDTLS_MD_SHA256,
+    "SHA256",
+    32,
+    64,
+    sha256_starts_wrap,
+    sha224_update_wrap,
+    sha224_finish_wrap,
+    sha256_wrap,
+    sha224_ctx_alloc,
+    sha224_ctx_free,
+    sha224_clone_wrap,
+    sha224_process_wrap,
+};
+
+#endif /* MBEDTLS_SHA256_C */
+
+#if defined(MBEDTLS_SHA512_C)
+
+static void sha384_starts_wrap( void *ctx )
+{
+    mbedtls_sha512_starts( (mbedtls_sha512_context *) ctx, 1 );
+}
+
+static void sha384_update_wrap( void *ctx, const unsigned char *input,
+                                size_t ilen )
+{
+    mbedtls_sha512_update( (mbedtls_sha512_context *) ctx, input, ilen );
+}
+
+static void sha384_finish_wrap( void *ctx, unsigned char *output )
+{
+    mbedtls_sha512_finish( (mbedtls_sha512_context *) ctx, output );
+}
+
+static void sha384_wrap( const unsigned char *input, size_t ilen,
+                    unsigned char *output )
+{
+    mbedtls_sha512( input, ilen, output, 1 );
+}
+
+static void *sha384_ctx_alloc( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( mbedtls_sha512_context ) );
+
+    if( ctx != NULL )
+        mbedtls_sha512_init( (mbedtls_sha512_context *) ctx );
+
+    return( ctx );
+}
+
+static void sha384_ctx_free( void *ctx )
+{
+    mbedtls_sha512_free( (mbedtls_sha512_context *) ctx );
+    mbedtls_free( ctx );
+}
+
+static void sha384_clone_wrap( void *dst, const void *src )
+{
+    mbedtls_sha512_clone( (mbedtls_sha512_context *) dst,
+                    (const mbedtls_sha512_context *) src );
+}
+
+static void sha384_process_wrap( void *ctx, const unsigned char *data )
+{
+    mbedtls_sha512_process( (mbedtls_sha512_context *) ctx, data );
+}
+
+const mbedtls_md_info_t mbedtls_sha384_info = {
+    MBEDTLS_MD_SHA384,
+    "SHA384",
+    48,
+    128,
+    sha384_starts_wrap,
+    sha384_update_wrap,
+    sha384_finish_wrap,
+    sha384_wrap,
+    sha384_ctx_alloc,
+    sha384_ctx_free,
+    sha384_clone_wrap,
+    sha384_process_wrap,
+};
+
+static void sha512_starts_wrap( void *ctx )
+{
+    mbedtls_sha512_starts( (mbedtls_sha512_context *) ctx, 0 );
+}
+
+static void sha512_wrap( const unsigned char *input, size_t ilen,
+                    unsigned char *output )
+{
+    mbedtls_sha512( input, ilen, output, 0 );
+}
+
+const mbedtls_md_info_t mbedtls_sha512_info = {
+    MBEDTLS_MD_SHA512,
+    "SHA512",
+    64,
+    128,
+    sha512_starts_wrap,
+    sha384_update_wrap,
+    sha384_finish_wrap,
+    sha512_wrap,
+    sha384_ctx_alloc,
+    sha384_ctx_free,
+    sha384_clone_wrap,
+    sha384_process_wrap,
+};
+
+#endif /* MBEDTLS_SHA512_C */
+
+#endif /* MBEDTLS_MD_C */

+ 707 - 0
Pal/lib/crypto/mbedtls/oid.c

@@ -0,0 +1,707 @@
+/**
+ * \file oid.c
+ *
+ * \brief Object Identifier (OID) database
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#if defined(MBEDTLS_OID_C)
+
+#include "mbedtls/oid.h"
+#include "mbedtls/rsa.h"
+
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#define mbedtls_snprintf snprintf
+#endif
+
+#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
+#include "mbedtls/x509.h"
+#endif
+
+/*
+ * Macro to automatically add the size of #define'd OIDs
+ */
+#define ADD_LEN(s)      s, MBEDTLS_OID_SIZE(s)
+
+/*
+ * Macro to generate an internal function for oid_XXX_from_asn1() (used by
+ * the other functions)
+ */
+#define FN_OID_TYPED_FROM_ASN1( TYPE_T, NAME, LIST )                        \
+static const TYPE_T * oid_ ## NAME ## _from_asn1( const mbedtls_asn1_buf *oid )     \
+{                                                                           \
+    const TYPE_T *p = LIST;                                                 \
+    const mbedtls_oid_descriptor_t *cur = (const mbedtls_oid_descriptor_t *) p;             \
+    if( p == NULL || oid == NULL ) return( NULL );                          \
+    while( cur->asn1 != NULL ) {                                            \
+        if( cur->asn1_len == oid->len &&                                    \
+            memcmp( cur->asn1, oid->p, oid->len ) == 0 ) {                  \
+            return( p );                                                    \
+        }                                                                   \
+        p++;                                                                \
+        cur = (const mbedtls_oid_descriptor_t *) p;                                 \
+    }                                                                       \
+    return( NULL );                                                         \
+}
+
+/*
+ * Macro to generate a function for retrieving a single attribute from the
+ * descriptor of an mbedtls_oid_descriptor_t wrapper.
+ */
+#define FN_OID_GET_DESCRIPTOR_ATTR1(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1) \
+int FN_NAME( const mbedtls_asn1_buf *oid, ATTR1_TYPE * ATTR1 )                  \
+{                                                                       \
+    const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1( oid );        \
+    if( data == NULL ) return( MBEDTLS_ERR_OID_NOT_FOUND );            \
+    *ATTR1 = data->descriptor.ATTR1;                                    \
+    return( 0 );                                                        \
+}
+
+/*
+ * Macro to generate a function for retrieving a single attribute from an
+ * mbedtls_oid_descriptor_t wrapper.
+ */
+#define FN_OID_GET_ATTR1(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1) \
+int FN_NAME( const mbedtls_asn1_buf *oid, ATTR1_TYPE * ATTR1 )                  \
+{                                                                       \
+    const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1( oid );        \
+    if( data == NULL ) return( MBEDTLS_ERR_OID_NOT_FOUND );            \
+    *ATTR1 = data->ATTR1;                                               \
+    return( 0 );                                                        \
+}
+
+/*
+ * Macro to generate a function for retrieving two attributes from an
+ * mbedtls_oid_descriptor_t wrapper.
+ */
+#define FN_OID_GET_ATTR2(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1,     \
+                         ATTR2_TYPE, ATTR2)                                 \
+int FN_NAME( const mbedtls_asn1_buf *oid, ATTR1_TYPE * ATTR1, ATTR2_TYPE * ATTR2 )  \
+{                                                                           \
+    const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1( oid );            \
+    if( data == NULL ) return( MBEDTLS_ERR_OID_NOT_FOUND );                \
+    *ATTR1 = data->ATTR1;                                                   \
+    *ATTR2 = data->ATTR2;                                                   \
+    return( 0 );                                                            \
+}
+
+/*
+ * Macro to generate a function for retrieving the OID based on a single
+ * attribute from a mbedtls_oid_descriptor_t wrapper.
+ */
+#define FN_OID_GET_OID_BY_ATTR1(FN_NAME, TYPE_T, LIST, ATTR1_TYPE, ATTR1)   \
+int FN_NAME( ATTR1_TYPE ATTR1, const char **oid, size_t *olen )             \
+{                                                                           \
+    const TYPE_T *cur = LIST;                                               \
+    while( cur->descriptor.asn1 != NULL ) {                                 \
+        if( cur->ATTR1 == ATTR1 ) {                                         \
+            *oid = cur->descriptor.asn1;                                    \
+            *olen = cur->descriptor.asn1_len;                               \
+            return( 0 );                                                    \
+        }                                                                   \
+        cur++;                                                              \
+    }                                                                       \
+    return( MBEDTLS_ERR_OID_NOT_FOUND );                                   \
+}
+
+/*
+ * Macro to generate a function for retrieving the OID based on two
+ * attributes from a mbedtls_oid_descriptor_t wrapper.
+ */
+#define FN_OID_GET_OID_BY_ATTR2(FN_NAME, TYPE_T, LIST, ATTR1_TYPE, ATTR1,   \
+                                ATTR2_TYPE, ATTR2)                          \
+int FN_NAME( ATTR1_TYPE ATTR1, ATTR2_TYPE ATTR2, const char **oid ,         \
+             size_t *olen )                                                 \
+{                                                                           \
+    const TYPE_T *cur = LIST;                                               \
+    while( cur->descriptor.asn1 != NULL ) {                                 \
+        if( cur->ATTR1 == ATTR1 && cur->ATTR2 == ATTR2 ) {                  \
+            *oid = cur->descriptor.asn1;                                    \
+            *olen = cur->descriptor.asn1_len;                               \
+            return( 0 );                                                    \
+        }                                                                   \
+        cur++;                                                              \
+    }                                                                       \
+    return( MBEDTLS_ERR_OID_NOT_FOUND );                                   \
+}
+
+#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
+/*
+ * For X520 attribute types
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    const char          *short_name;
+} oid_x520_attr_t;
+
+static const oid_x520_attr_t oid_x520_attr_type[] =
+{
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_CN ),          "id-at-commonName",               "Common Name" },
+        "CN",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_COUNTRY ),     "id-at-countryName",              "Country" },
+        "C",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_LOCALITY ),    "id-at-locality",                 "Locality" },
+        "L",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_STATE ),       "id-at-state",                    "State" },
+        "ST",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_ORGANIZATION ),"id-at-organizationName",         "Organization" },
+        "O",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_ORG_UNIT ),    "id-at-organizationalUnitName",   "Org Unit" },
+        "OU",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS9_EMAIL ),    "emailAddress",                   "E-mail address" },
+        "emailAddress",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_SERIAL_NUMBER ),"id-at-serialNumber",            "Serial number" },
+        "serialNumber",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_POSTAL_ADDRESS ),"id-at-postalAddress",          "Postal address" },
+        "postalAddress",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_POSTAL_CODE ), "id-at-postalCode",               "Postal code" },
+        "postalCode",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_SUR_NAME ),    "id-at-surName",                  "Surname" },
+        "SN",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_GIVEN_NAME ),  "id-at-givenName",                "Given name" },
+        "GN",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_INITIALS ),    "id-at-initials",                 "Initials" },
+        "initials",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_GENERATION_QUALIFIER ), "id-at-generationQualifier", "Generation qualifier" },
+        "generationQualifier",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_TITLE ),       "id-at-title",                    "Title" },
+        "title",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_DN_QUALIFIER ),"id-at-dnQualifier",              "Distinguished Name qualifier" },
+        "dnQualifier",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_PSEUDONYM ),   "id-at-pseudonym",                "Pseudonym" },
+        "pseudonym",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_DOMAIN_COMPONENT ), "id-domainComponent",           "Domain component" },
+        "DC",
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_AT_UNIQUE_IDENTIFIER ), "id-at-uniqueIdentifier",    "Unique Identifier" },
+        "uniqueIdentifier",
+    },
+    {
+        { NULL, 0, NULL, NULL },
+        NULL,
+    }
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_x520_attr_t, x520_attr, oid_x520_attr_type)
+FN_OID_GET_ATTR1(mbedtls_oid_get_attr_short_name, oid_x520_attr_t, x520_attr, const char *, short_name)
+
+/*
+ * For X509 extensions
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    int                 ext_type;
+} oid_x509_ext_t;
+
+static const oid_x509_ext_t oid_x509_ext[] =
+{
+    {
+        { ADD_LEN( MBEDTLS_OID_BASIC_CONSTRAINTS ),    "id-ce-basicConstraints",   "Basic Constraints" },
+        MBEDTLS_X509_EXT_BASIC_CONSTRAINTS,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_KEY_USAGE ),            "id-ce-keyUsage",           "Key Usage" },
+        MBEDTLS_X509_EXT_KEY_USAGE,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_EXTENDED_KEY_USAGE ),   "id-ce-extKeyUsage",        "Extended Key Usage" },
+        MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_SUBJECT_ALT_NAME ),     "id-ce-subjectAltName",     "Subject Alt Name" },
+        MBEDTLS_X509_EXT_SUBJECT_ALT_NAME,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_NS_CERT_TYPE ),         "id-netscape-certtype",     "Netscape Certificate Type" },
+        MBEDTLS_X509_EXT_NS_CERT_TYPE,
+    },
+    {
+        { NULL, 0, NULL, NULL },
+        0,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_x509_ext_t, x509_ext, oid_x509_ext)
+FN_OID_GET_ATTR1(mbedtls_oid_get_x509_ext_type, oid_x509_ext_t, x509_ext, int, ext_type)
+
+static const mbedtls_oid_descriptor_t oid_ext_key_usage[] =
+{
+    { ADD_LEN( MBEDTLS_OID_SERVER_AUTH ),      "id-kp-serverAuth",      "TLS Web Server Authentication" },
+    { ADD_LEN( MBEDTLS_OID_CLIENT_AUTH ),      "id-kp-clientAuth",      "TLS Web Client Authentication" },
+    { ADD_LEN( MBEDTLS_OID_CODE_SIGNING ),     "id-kp-codeSigning",     "Code Signing" },
+    { ADD_LEN( MBEDTLS_OID_EMAIL_PROTECTION ), "id-kp-emailProtection", "E-mail Protection" },
+    { ADD_LEN( MBEDTLS_OID_TIME_STAMPING ),    "id-kp-timeStamping",    "Time Stamping" },
+    { ADD_LEN( MBEDTLS_OID_OCSP_SIGNING ),     "id-kp-OCSPSigning",     "OCSP Signing" },
+    { NULL, 0, NULL, NULL },
+};
+
+FN_OID_TYPED_FROM_ASN1(mbedtls_oid_descriptor_t, ext_key_usage, oid_ext_key_usage)
+FN_OID_GET_ATTR1(mbedtls_oid_get_extended_key_usage, mbedtls_oid_descriptor_t, ext_key_usage, const char *, description)
+#endif /* MBEDTLS_X509_USE_C || MBEDTLS_X509_CREATE_C */
+
+#if defined(MBEDTLS_MD_C)
+/*
+ * For SignatureAlgorithmIdentifier
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    mbedtls_md_type_t           md_alg;
+    mbedtls_pk_type_t           pk_alg;
+} oid_sig_alg_t;
+
+static const oid_sig_alg_t oid_sig_alg[] =
+{
+#if defined(MBEDTLS_RSA_C)
+#if defined(MBEDTLS_MD2_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_MD2 ),        "md2WithRSAEncryption",     "RSA with MD2" },
+        MBEDTLS_MD_MD2,      MBEDTLS_PK_RSA,
+    },
+#endif /* MBEDTLS_MD2_C */
+#if defined(MBEDTLS_MD4_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_MD4 ),        "md4WithRSAEncryption",     "RSA with MD4" },
+        MBEDTLS_MD_MD4,      MBEDTLS_PK_RSA,
+    },
+#endif /* MBEDTLS_MD4_C */
+#if defined(MBEDTLS_MD5_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_MD5 ),        "md5WithRSAEncryption",     "RSA with MD5" },
+        MBEDTLS_MD_MD5,      MBEDTLS_PK_RSA,
+    },
+#endif /* MBEDTLS_MD5_C */
+#if defined(MBEDTLS_SHA1_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_SHA1 ),       "sha-1WithRSAEncryption",   "RSA with SHA1" },
+        MBEDTLS_MD_SHA1,     MBEDTLS_PK_RSA,
+    },
+#endif /* MBEDTLS_SHA1_C */
+#if defined(MBEDTLS_SHA256_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_SHA224 ),     "sha224WithRSAEncryption",  "RSA with SHA-224" },
+        MBEDTLS_MD_SHA224,   MBEDTLS_PK_RSA,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_SHA256 ),     "sha256WithRSAEncryption",  "RSA with SHA-256" },
+        MBEDTLS_MD_SHA256,   MBEDTLS_PK_RSA,
+    },
+#endif /* MBEDTLS_SHA256_C */
+#if defined(MBEDTLS_SHA512_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_SHA384 ),     "sha384WithRSAEncryption",  "RSA with SHA-384" },
+        MBEDTLS_MD_SHA384,   MBEDTLS_PK_RSA,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_SHA512 ),     "sha512WithRSAEncryption",  "RSA with SHA-512" },
+        MBEDTLS_MD_SHA512,   MBEDTLS_PK_RSA,
+    },
+#endif /* MBEDTLS_SHA512_C */
+#if defined(MBEDTLS_SHA1_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_RSA_SHA_OBS ),      "sha-1WithRSAEncryption",   "RSA with SHA1" },
+        MBEDTLS_MD_SHA1,     MBEDTLS_PK_RSA,
+    },
+#endif /* MBEDTLS_SHA1_C */
+#endif /* MBEDTLS_RSA_C */
+#if defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_SHA1_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_ECDSA_SHA1 ),       "ecdsa-with-SHA1",      "ECDSA with SHA1" },
+        MBEDTLS_MD_SHA1,     MBEDTLS_PK_ECDSA,
+    },
+#endif /* MBEDTLS_SHA1_C */
+#if defined(MBEDTLS_SHA256_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_ECDSA_SHA224 ),     "ecdsa-with-SHA224",    "ECDSA with SHA224" },
+        MBEDTLS_MD_SHA224,   MBEDTLS_PK_ECDSA,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_ECDSA_SHA256 ),     "ecdsa-with-SHA256",    "ECDSA with SHA256" },
+        MBEDTLS_MD_SHA256,   MBEDTLS_PK_ECDSA,
+    },
+#endif /* MBEDTLS_SHA256_C */
+#if defined(MBEDTLS_SHA512_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_ECDSA_SHA384 ),     "ecdsa-with-SHA384",    "ECDSA with SHA384" },
+        MBEDTLS_MD_SHA384,   MBEDTLS_PK_ECDSA,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_ECDSA_SHA512 ),     "ecdsa-with-SHA512",    "ECDSA with SHA512" },
+        MBEDTLS_MD_SHA512,   MBEDTLS_PK_ECDSA,
+    },
+#endif /* MBEDTLS_SHA512_C */
+#endif /* MBEDTLS_ECDSA_C */
+#if defined(MBEDTLS_RSA_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_RSASSA_PSS ),        "RSASSA-PSS",           "RSASSA-PSS" },
+        MBEDTLS_MD_NONE,     MBEDTLS_PK_RSASSA_PSS,
+    },
+#endif /* MBEDTLS_RSA_C */
+    {
+        { NULL, 0, NULL, NULL },
+        MBEDTLS_MD_NONE, MBEDTLS_PK_NONE,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_sig_alg_t, sig_alg, oid_sig_alg)
+FN_OID_GET_DESCRIPTOR_ATTR1(mbedtls_oid_get_sig_alg_desc, oid_sig_alg_t, sig_alg, const char *, description)
+FN_OID_GET_ATTR2(mbedtls_oid_get_sig_alg, oid_sig_alg_t, sig_alg, mbedtls_md_type_t, md_alg, mbedtls_pk_type_t, pk_alg)
+FN_OID_GET_OID_BY_ATTR2(mbedtls_oid_get_oid_by_sig_alg, oid_sig_alg_t, oid_sig_alg, mbedtls_pk_type_t, pk_alg, mbedtls_md_type_t, md_alg)
+#endif /* MBEDTLS_MD_C */
+
+/*
+ * For PublicKeyInfo (PKCS1, RFC 5480)
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    mbedtls_pk_type_t           pk_alg;
+} oid_pk_alg_t;
+
+static const oid_pk_alg_t oid_pk_alg[] =
+{
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS1_RSA ),      "rsaEncryption",   "RSA" },
+        MBEDTLS_PK_RSA,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_ALG_UNRESTRICTED ),  "id-ecPublicKey",   "Generic EC key" },
+        MBEDTLS_PK_ECKEY,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_ALG_ECDH ),          "id-ecDH",          "EC key for ECDH" },
+        MBEDTLS_PK_ECKEY_DH,
+    },
+    {
+        { NULL, 0, NULL, NULL },
+        MBEDTLS_PK_NONE,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_pk_alg_t, pk_alg, oid_pk_alg)
+FN_OID_GET_ATTR1(mbedtls_oid_get_pk_alg, oid_pk_alg_t, pk_alg, mbedtls_pk_type_t, pk_alg)
+FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_pk_alg, oid_pk_alg_t, oid_pk_alg, mbedtls_pk_type_t, pk_alg)
+
+#if defined(MBEDTLS_ECP_C)
+/*
+ * For namedCurve (RFC 5480)
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    mbedtls_ecp_group_id        grp_id;
+} oid_ecp_grp_t;
+
+static const oid_ecp_grp_t oid_ecp_grp[] =
+{
+#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP192R1 ), "secp192r1",    "secp192r1" },
+        MBEDTLS_ECP_DP_SECP192R1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP224R1 ), "secp224r1",    "secp224r1" },
+        MBEDTLS_ECP_DP_SECP224R1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP256R1 ), "secp256r1",    "secp256r1" },
+        MBEDTLS_ECP_DP_SECP256R1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP384R1 ), "secp384r1",    "secp384r1" },
+        MBEDTLS_ECP_DP_SECP384R1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP521R1 ), "secp521r1",    "secp521r1" },
+        MBEDTLS_ECP_DP_SECP521R1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP192K1 ), "secp192k1",    "secp192k1" },
+        MBEDTLS_ECP_DP_SECP192K1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP224K1 ), "secp224k1",    "secp224k1" },
+        MBEDTLS_ECP_DP_SECP224K1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_SECP256K1 ), "secp256k1",    "secp256k1" },
+        MBEDTLS_ECP_DP_SECP256K1,
+    },
+#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_BP256R1 ),   "brainpoolP256r1","brainpool256r1" },
+        MBEDTLS_ECP_DP_BP256R1,
+    },
+#endif /* MBEDTLS_ECP_DP_BP256R1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_BP384R1 ),   "brainpoolP384r1","brainpool384r1" },
+        MBEDTLS_ECP_DP_BP384R1,
+    },
+#endif /* MBEDTLS_ECP_DP_BP384R1_ENABLED */
+#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED)
+    {
+        { ADD_LEN( MBEDTLS_OID_EC_GRP_BP512R1 ),   "brainpoolP512r1","brainpool512r1" },
+        MBEDTLS_ECP_DP_BP512R1,
+    },
+#endif /* MBEDTLS_ECP_DP_BP512R1_ENABLED */
+    {
+        { NULL, 0, NULL, NULL },
+        MBEDTLS_ECP_DP_NONE,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_ecp_grp_t, grp_id, oid_ecp_grp)
+FN_OID_GET_ATTR1(mbedtls_oid_get_ec_grp, oid_ecp_grp_t, grp_id, mbedtls_ecp_group_id, grp_id)
+FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_ec_grp, oid_ecp_grp_t, oid_ecp_grp, mbedtls_ecp_group_id, grp_id)
+#endif /* MBEDTLS_ECP_C */
+
+#if defined(MBEDTLS_CIPHER_C)
+/*
+ * For PKCS#5 PBES2 encryption algorithm
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    mbedtls_cipher_type_t       cipher_alg;
+} oid_cipher_alg_t;
+
+static const oid_cipher_alg_t oid_cipher_alg[] =
+{
+    {
+        { ADD_LEN( MBEDTLS_OID_DES_CBC ),              "desCBC",       "DES-CBC" },
+        MBEDTLS_CIPHER_DES_CBC,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_DES_EDE3_CBC ),         "des-ede3-cbc", "DES-EDE3-CBC" },
+        MBEDTLS_CIPHER_DES_EDE3_CBC,
+    },
+    {
+        { NULL, 0, NULL, NULL },
+        MBEDTLS_CIPHER_NONE,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_cipher_alg_t, cipher_alg, oid_cipher_alg)
+FN_OID_GET_ATTR1(mbedtls_oid_get_cipher_alg, oid_cipher_alg_t, cipher_alg, mbedtls_cipher_type_t, cipher_alg)
+#endif /* MBEDTLS_CIPHER_C */
+
+#if defined(MBEDTLS_MD_C)
+/*
+ * For digestAlgorithm
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    mbedtls_md_type_t           md_alg;
+} oid_md_alg_t;
+
+static const oid_md_alg_t oid_md_alg[] =
+{
+#if defined(MBEDTLS_MD2_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_MD2 ),       "id-md2",       "MD2" },
+        MBEDTLS_MD_MD2,
+    },
+#endif /* MBEDTLS_MD2_C */
+#if defined(MBEDTLS_MD4_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_MD4 ),       "id-md4",       "MD4" },
+        MBEDTLS_MD_MD4,
+    },
+#endif /* MBEDTLS_MD4_C */
+#if defined(MBEDTLS_MD5_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_MD5 ),       "id-md5",       "MD5" },
+        MBEDTLS_MD_MD5,
+    },
+#endif /* MBEDTLS_MD5_C */
+#if defined(MBEDTLS_SHA1_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_SHA1 ),      "id-sha1",      "SHA-1" },
+        MBEDTLS_MD_SHA1,
+    },
+#endif /* MBEDTLS_SHA1_C */
+#if defined(MBEDTLS_SHA256_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_SHA224 ),    "id-sha224",    "SHA-224" },
+        MBEDTLS_MD_SHA224,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_SHA256 ),    "id-sha256",    "SHA-256" },
+        MBEDTLS_MD_SHA256,
+    },
+#endif /* MBEDTLS_SHA256_C */
+#if defined(MBEDTLS_SHA512_C)
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_SHA384 ),    "id-sha384",    "SHA-384" },
+        MBEDTLS_MD_SHA384,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_DIGEST_ALG_SHA512 ),    "id-sha512",    "SHA-512" },
+        MBEDTLS_MD_SHA512,
+    },
+#endif /* MBEDTLS_SHA512_C */
+    {
+        { NULL, 0, NULL, NULL },
+        MBEDTLS_MD_NONE,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_md_alg_t, md_alg, oid_md_alg)
+FN_OID_GET_ATTR1(mbedtls_oid_get_md_alg, oid_md_alg_t, md_alg, mbedtls_md_type_t, md_alg)
+FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_md, oid_md_alg_t, oid_md_alg, mbedtls_md_type_t, md_alg)
+#endif /* MBEDTLS_MD_C */
+
+#if defined(MBEDTLS_PKCS12_C)
+/*
+ * For PKCS#12 PBEs
+ */
+typedef struct {
+    mbedtls_oid_descriptor_t    descriptor;
+    mbedtls_md_type_t           md_alg;
+    mbedtls_cipher_type_t       cipher_alg;
+} oid_pkcs12_pbe_alg_t;
+
+static const oid_pkcs12_pbe_alg_t oid_pkcs12_pbe_alg[] =
+{
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS12_PBE_SHA1_DES3_EDE_CBC ), "pbeWithSHAAnd3-KeyTripleDES-CBC", "PBE with SHA1 and 3-Key 3DES" },
+        MBEDTLS_MD_SHA1,      MBEDTLS_CIPHER_DES_EDE3_CBC,
+    },
+    {
+        { ADD_LEN( MBEDTLS_OID_PKCS12_PBE_SHA1_DES2_EDE_CBC ), "pbeWithSHAAnd2-KeyTripleDES-CBC", "PBE with SHA1 and 2-Key 3DES" },
+        MBEDTLS_MD_SHA1,      MBEDTLS_CIPHER_DES_EDE_CBC,
+    },
+    {
+        { NULL, 0, NULL, NULL },
+        MBEDTLS_MD_NONE, MBEDTLS_CIPHER_NONE,
+    },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_pkcs12_pbe_alg_t, pkcs12_pbe_alg, oid_pkcs12_pbe_alg)
+FN_OID_GET_ATTR2(mbedtls_oid_get_pkcs12_pbe_alg, oid_pkcs12_pbe_alg_t, pkcs12_pbe_alg, mbedtls_md_type_t, md_alg, mbedtls_cipher_type_t, cipher_alg)
+#endif /* MBEDTLS_PKCS12_C */
+
+#define OID_SAFE_SNPRINTF                               \
+    do {                                                \
+        if( ret < 0 || (size_t) ret >= n )              \
+            return( MBEDTLS_ERR_OID_BUF_TOO_SMALL );    \
+                                                        \
+        n -= (size_t) ret;                              \
+        p += (size_t) ret;                              \
+    } while( 0 )
+
+/* Return the x.y.z.... style numeric string for the given OID */
+int mbedtls_oid_get_numeric_string( char *buf, size_t size,
+                            const mbedtls_asn1_buf *oid )
+{
+    int ret;
+    size_t i, n;
+    unsigned int value;
+    char *p;
+
+    p = buf;
+    n = size;
+
+    /* First byte contains first two dots */
+    if( oid->len > 0 )
+    {
+        ret = mbedtls_snprintf( p, n, "%d.%d", oid->p[0] / 40, oid->p[0] % 40 );
+        OID_SAFE_SNPRINTF;
+    }
+
+    value = 0;
+    for( i = 1; i < oid->len; i++ )
+    {
+        /* Prevent overflow in value. */
+        if( ( ( value << 7 ) >> 7 ) != value )
+            return( MBEDTLS_ERR_OID_BUF_TOO_SMALL );
+
+        value <<= 7;
+        value += oid->p[i] & 0x7F;
+
+        if( !( oid->p[i] & 0x80 ) )
+        {
+            /* Last byte */
+            ret = mbedtls_snprintf( p, n, ".%d", value );
+            OID_SAFE_SNPRINTF;
+            value = 0;
+        }
+    }
+
+    return( (int) ( size - n ) );
+}
+
+#endif /* MBEDTLS_OID_C */

+ 1 - 1
Pal/lib/crypto/mbedtls/rsa.c

@@ -453,7 +453,7 @@ cleanup:
     return( 0 );
 }
 
-#if defined(MBEDTLS_PKCS1_V21)
+#if defined(MBEDTLS_PKCS1_V21) || defined(MBEDTLS_PKCS1_V15)
 /**
  * Generate and apply the MGF1 operation (from PKCS#1 v2.1) to a buffer.
  *

+ 13 - 1
Pal/lib/hex.h

@@ -20,6 +20,7 @@
 
 #include <assert.h>
 #include <stddef.h>
+#include <stdint.h>
 
 /* This function is a helper for debug printing.
  * It accepts a pointer to a numerical value, and
@@ -50,8 +51,19 @@ char * __bytes2hexstr(void * hex, size_t size, char *str, size_t len)
 #define IS_INDEXABLE(arg) (sizeof((arg)[0]))
 #define IS_ARRAY(arg) (IS_INDEXABLE(arg) > 0 && (((void *) &(arg)) == ((void *) (arg))))
 
+static inline __attribute__((always_inline))
+int8_t hex2dec(char c) {
+    if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+    else if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+    else if (c >= '0' && c <= '9')
+        return c - '0';
+    else
+        return -1;
+}
 
-/*
+    /*
  * BYTES2HEXSTR converts an array into a hexadecimal string and fills into a
  * given buffer. The buffer size is given as an extra argument.
  */

+ 39 - 3
Pal/lib/pal_crypto.h

@@ -24,6 +24,10 @@
 #ifndef PAL_CRYPTO_H
 #define PAL_CRYPTO_H
 
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
 #define SHA256_DIGEST_LEN 32
 
 #ifdef CRYPTO_USE_WOLFSSL
@@ -121,11 +125,43 @@ int lib_RSAImportPublicKey(LIB_RSA_KEY *key, const uint8_t *e, uint64_t e_size,
 // padding, with SHA256 as the hash mechanism. These signatures are generated
 // by the Graphene filesystem build (so outside of a running Graphene
 // application), but are verified within the Graphene application.
-int lib_RSAVerifySHA256(LIB_RSA_KEY *key, const uint8_t *signature,
-                        uint64_t signature_len, uint8_t *signed_data_out,
-                        uint64_t signed_data_out_len);
+int lib_RSAVerifySHA256(LIB_RSA_KEY* key, const uint8_t* hash, uint64_t hash_len,
+                        const uint8_t* signature, uint64_t signature_len);
 
 // Frees memory allocated in lib_RSAInitKey.
 int lib_RSAFreeKey(LIB_RSA_KEY *key);
 
+// Encode and decode Base64 messages.
+// These two functions can be used to query encode and decode sizes if dst is given NULL
+int lib_Base64Encode(const uint8_t* src, size_t slen, char* dst, size_t* dlen);
+int lib_Base64Decode(const char *src, size_t slen, uint8_t* dst, size_t* dlen);
+
+#ifdef CRYPTO_USE_MBEDTLS
+#include "crypto/mbedtls/mbedtls/asn1.h"
+enum asn1_tag {
+    ASN1_BOOLEAN                = MBEDTLS_ASN1_BOOLEAN,
+    ASN1_INTEGET                = MBEDTLS_ASN1_INTEGER,
+    ASN1_BIT_STRING             = MBEDTLS_ASN1_BIT_STRING,
+    ASN1_OCTET_STRING           = MBEDTLS_ASN1_OCTET_STRING,
+    ASN1_NULL                   = MBEDTLS_ASN1_NULL,
+    ASN1_OID                    = MBEDTLS_ASN1_OID,
+    ASN1_UTF8_STRING            = MBEDTLS_ASN1_UTF8_STRING,
+    ASN1_SEQUENCE               = MBEDTLS_ASN1_SEQUENCE,
+    ASN1_SET                    = MBEDTLS_ASN1_SET,
+    ASN1_PRINTABLE_STRING       = MBEDTLS_ASN1_PRINTABLE_STRING,
+    ASN1_T61_STRING             = MBEDTLS_ASN1_T61_STRING,
+    ASN1_IA5_STRING             = MBEDTLS_ASN1_IA5_STRING,
+    ASN1_UTC_TIME               = MBEDTLS_ASN1_UTC_TIME,
+    ASN1_GENERALIZED_TIME       = MBEDTLS_ASN1_GENERALIZED_TIME,
+    ASN1_UNIVERSAL_STRING       = MBEDTLS_ASN1_UNIVERSAL_STRING,
+    ASN1_BMP_STRING             = MBEDTLS_ASN1_BMP_STRING,
+};
+#endif /* CRYPTO_USE_MBEDTLS */
+
+int lib_ASN1GetSerial(uint8_t** ptr, const uint8_t* end, enum asn1_tag* tag, bool* is_construct,
+                      uint8_t** buf, size_t* len);
+
+int lib_ASN1GetBitstring(uint8_t** ptr, const uint8_t* end, uint8_t** str, size_t* len);
+int lib_ASN1GetLargeNumberLength(uint8_t** ptr, const uint8_t* end, size_t* len);
+
 #endif

+ 8 - 0
Pal/regression/Attestation.c

@@ -0,0 +1,8 @@
+#include "pal.h"
+#include "pal_debug.h"
+
+int main(int argc, char** argv, char** envp) {
+    pal_printf("Attestation status: %s\n",      pal_control.attestation_status);
+    pal_printf("Attestation timestamp: %s\n",   pal_control.attestation_timestamp);
+    return 0;
+}

+ 10 - 0
Pal/regression/Attestation.manifest.template

@@ -0,0 +1,10 @@
+# the executable to run
+loader.exec = file:Attestation
+
+# debug type: inline|none
+loader.debug_type = none
+
+sgx.ra_client_spid = $(RA_CLIENT_SPID)
+sgx.ra_client_key  = $(RA_CLIENT_KEY)
+sgx.ra_client_linkable = $(RA_CLIENT_LINKABLE)
+sgx.ra_accept_group_out_of_date = 1

+ 8 - 0
Pal/regression/Makefile

@@ -35,9 +35,17 @@ ifeq ($(WERROR),1)
 CFLAGS += -Werror
 endif
 
+# To enable tests for remote attestation, please provide RA_CLIENT_SPID and RA_CLIENT_KEY
+# in the environment variables.
+# To obtain a SPID, register in the Intel API portal:
+# https://api.portal.trustedservices.intel.com/EPID-attestation
+
 manifest_rules = \
 	-e 's:\$$(PAL):$(abspath ../../Runtime/pal_loader):g' \
 	-e 's:\$$(PWD):$(shell pwd)/:g' \
+	-e 's:\$$(RA_CLIENT_SPID):$(RA_CLIENT_SPID):g' \
+	-e 's:\$$(RA_CLIENT_KEY):$(RA_CLIENT_KEY):g' \
+	-e 's:\$$(RA_CLIENT_LINKABLE):$(if $(RA_CLIENT_LINKABLE),$(RA_CLIENT_LINKABLE),0):g' \
 	$(extra_rules)
 
 manifest: manifest.template

+ 20 - 0
Pal/regression/test_pal.py

@@ -10,6 +10,7 @@ import shutil
 import string
 import subprocess
 import unittest
+from datetime import datetime, timedelta
 
 from regression import (
     HAS_SGX,
@@ -613,3 +614,22 @@ class TC_40_AVXDisable(RegressionTestCase):
         # Disable AVX bit in XFRM
         stdout, stderr = self.run_binary(['AvxDisable'])
         self.assertIn('Illegal instruction executed in enclave', stderr)
+
+@unittest.skipUnless(HAS_SGX, 'need SGX')
+class TC_50_Attestation(RegressionTestCase):
+    def test_000_remote_attestation(self):
+        stdout, stderr = self.run_binary(["Attestation"])
+
+        for line in stderr.split("\n"):
+            # Check the attestation status
+            if line.startswith("Attestation status:"):
+                status = line[19:].strip()
+                self.assertIn(status, ["OK", "GROUP_OUT_OF_DATE"])
+
+            # Check the timestamp
+            if line.startswith("Attestation timestamp:"):
+                timestamp = datetime.strptime(line[22:].strip(), "%Y-%m-%dT%H:%M:%S.%f")
+                # The timestamp may be in another time zone, but should be
+                # within 24 hours of the current time.
+                self.assertTrue(datetime.now() - timedelta(hours=24) <= timestamp and \
+                                datetime.now() + timedelta(hours=24) >= timestamp);

+ 2 - 0
Pal/src/host/Linux-SGX/.gitignore

@@ -2,6 +2,8 @@
 /generated-offsets.s
 /generated_offsets.py
 /pal-sgx
+/quote/aesm.pb-c.c
+/quote/aesm.pb-c.h
 
 *.pem
 *.pub

+ 24 - 3
Pal/src/host/Linux-SGX/Makefile

@@ -1,6 +1,9 @@
 include ../../../../Makefile.configs
 include Makefile.am
 
+ias_cert_url ?= https://software.intel.com/sites/default/files/managed/7b/de/RK_PUB.zip
+ias_cert_zip = quote/$(notdir $(ias_cert_url))
+
 CFLAGS	+= -I. -Iinclude -I../.. -I../../../include -I../../../lib -Isgx-driver
 ASFLAGS += -I. -I../.. -I../../../include
 
@@ -12,9 +15,10 @@ ASFLAGS += $(defs)
 enclave-objs = $(addprefix db_,files devices pipes sockets streams memory \
 		 threading mutex events process object main rtld \
 		 exception misc ipc spinlock) \
-	       $(addprefix enclave_,ocalls ecalls framework pages untrusted)
+	       $(addprefix enclave_,ocalls ecalls framework platform pages untrusted)
 enclave-asm-objs = enclave_entry
-urts-objs = $(addprefix sgx_,enclave framework main rtld thread process exception graphene)
+urts-objs = $(addprefix sgx_,enclave framework platform main rtld thread process exception graphene) \
+	    quote/aesm.pb-c
 urts-asm-objs = sgx_entry
 graphene_lib = .lib/graphene-lib.a
 
@@ -40,6 +44,9 @@ $(addsuffix .s,$(enclave-objs)): CFLAGS += -DIN_ENCLAVE
 $(addsuffix .o,$(enclave-asm-objs)): ASFLAGS += -DIN_ENCLAVE
 $(addsuffix .s,$(enclave-asm-objs)): ASFLAGS += -DIN_ENCLAVE
 
+$(addsuffix .o,$(urts-objs)): quote/aesm.pb-c.h
+$(addsuffix .o,$(enclave-objs)): quote/generated-cacert.h
+
 %.o: %.c
 	$(call cmd,cc_o_c)
 
@@ -57,7 +64,19 @@ $(addsuffix .s,$(enclave-asm-objs)): ASFLAGS += -DIN_ENCLAVE
 
 pal-sgx: $(addsuffix .o,$(urts-objs) $(urts-asm-objs)) $(graphene_lib)
 	@echo [ host/Linux-SGX/$@ ]
-	@$(CC) $(CFLAGS) -Wl,-z,relro,-z,now -pie $^ -lc -pthread -o $@
+	@$(CC) $(CFLAGS) -Wl,-z,relro,-z,now -pie $^ -lc -pthread -lprotobuf-c -o $@
+
+quote/aesm.pb-c.o: quote/aesm.pb-c.c quote/aesm.pb-c.h
+quote/aesm.pb-c.c quote/aesm.pb-c.h: quote/aesm.proto
+	@echo [ host/Linux-SGX/quote/aesm.pb-c.c ]
+	@echo [ host/Linux-SGX/quote/aesm.pb-c.h ]
+	@protoc-c --c_out=. $<
+
+quote/generated-cacert.h: $(ias_cert_zip)
+	@echo "#define IAS_CA_CERT \"$(shell unzip -p $< | tr -d '[\r\n]')\"" > $@
+
+$(ias_cert_zip):
+	@wget $(ias_cert_url) -O $@
 
 debugger/sgx_gdb.so: debugger/sgx_gdb.c debugger/sgx_gdb.h sgx_arch.h
 	@echo [ host/Linux-SGX/$@ ]
@@ -75,6 +94,8 @@ include ../../../../Makefile.rules
 
 CLEAN_FILES += $(notdir $(pal_static) $(pal_lib) $(pal_loader))
 CLEAN_FILES += debugger/sgx_gdb.o
+CLEAN_FILES += quote/aesm.pb-c.c quote/aesm.pb-c.h
+CLEAN_FILES += $(ias_cert_zip) quote/generated-cacert.h
 
 .PHONY: clean
 clean:

+ 24 - 6
Pal/src/host/Linux-SGX/db_main.c

@@ -80,10 +80,10 @@ PAL_NUM _DkGetHostId (void)
 void setup_pal_map (struct link_map * map);
 static struct link_map pal_map;
 
-void init_untrusted_slab_mgr ();
-int init_enclave (void);
-int init_enclave_key (void);
-int init_child_process (PAL_HANDLE * parent_handle);
+void init_untrusted_slab_mgr(void);
+int init_enclave(void);
+int init_enclave_key(void);
+int init_child_process(PAL_HANDLE* parent_handle);
 
 /*
  * Creates a dummy file handle with the given name.
@@ -262,6 +262,7 @@ void pal_linux_main(char * uptr_args, uint64_t args_size,
 
     COPY_ARRAY(pal_sec.proc_fds, sec_info.proc_fds);
     COPY_ARRAY(pal_sec.pipe_prefix, sec_info.pipe_prefix);
+    pal_sec.aesm_targetinfo = sec_info.aesm_targetinfo;
     pal_sec.mcast_port = sec_info.mcast_port;
     pal_sec.mcast_srv = sec_info.mcast_srv;
     pal_sec.mcast_cli = sec_info.mcast_cli;
@@ -310,6 +311,11 @@ void pal_linux_main(char * uptr_args, uint64_t args_size,
     /* now we can add a link map for PAL itself */
     setup_pal_map(&pal_map);
 
+    /* Set the alignment early */
+    pal_state.alloc_align = pagesz;
+    pal_state.alloc_shift = pagesz - 1;
+    pal_state.alloc_mask  = ~pagesz;
+
     /* initialize enclave properties */
     rv = init_enclave();
     if (rv) {
@@ -385,8 +391,20 @@ void pal_linux_main(char * uptr_args, uint64_t args_size,
     __pal_control.manifest_preload.start = (PAL_PTR) manifest_addr;
     __pal_control.manifest_preload.end = (PAL_PTR) manifest_addr + manifest_size;
 
-    init_trusted_files();
-    init_trusted_children();
+    if ((rv = init_trusted_platform()) < 0) {
+        SGX_DBG(DBG_E, "Failed to verify the platform using remote attestation: %d\n", rv);
+        ocall_exit(rv, true);
+    }
+
+    if ((rv = init_trusted_files()) < 0) {
+        SGX_DBG(DBG_E, "Failed to load the checksums of trusted files: %d\n", rv);
+        ocall_exit(rv, true);
+    }
+
+    if ((rv = init_trusted_children()) < 0) {
+        SGX_DBG(DBG_E, "Failed to load the measurement of trusted child enclaves: %d\n", rv);
+        ocall_exit(rv, true);
+    }
 
 #if PRINT_ENCLAVE_STAT == 1
     printf("                >>>>>>>> "

+ 44 - 38
Pal/src/host/Linux-SGX/enclave_framework.c

@@ -88,6 +88,19 @@ uint64_t sgx_copy_to_enclave(const void* ptr, uint64_t maxsize, const void* uptr
     return usize;
 }
 
+static void print_report(sgx_arch_report_t* r) {
+    SGX_DBG(DBG_S, "  cpusvn:     %08lx %08lx\n", r->body.cpusvn[0], r->body.cpusvn[1]);
+    SGX_DBG(DBG_S, "  mrenclave:  %s\n",        ALLOCA_BYTES2HEXSTR(r->body.mrenclave));
+    SGX_DBG(DBG_S, "  mrsigner:   %s\n",        ALLOCA_BYTES2HEXSTR(r->body.mrsigner));
+    SGX_DBG(DBG_S, "  attr.flags: %016lx\n",    r->body.attributes.flags);
+    SGX_DBG(DBG_S, "  attr.xfrm:  %016lx\n",    r->body.attributes.xfrm);
+    SGX_DBG(DBG_S, "  isvprodid:  %02x\n",      r->body.isvprodid);
+    SGX_DBG(DBG_S, "  isvsvn:     %02x\n",      r->body.isvsvn);
+    SGX_DBG(DBG_S, "  reportdata: %s\n",        ALLOCA_BYTES2HEXSTR(r->body.report_data));
+    SGX_DBG(DBG_S, "  keyid:      %s\n",        ALLOCA_BYTES2HEXSTR(r->keyid));
+    SGX_DBG(DBG_S, "  mac:        %s\n",        ALLOCA_BYTES2HEXSTR(r->mac));
+}
+
 static sgx_arch_key128_t enclave_key;
 
 #define KEYBUF_SIZE ((sizeof(sgx_arch_key128_t) * 2) + 1)
@@ -110,30 +123,19 @@ static int sgx_get_report(sgx_arch_targetinfo_t* target_info, sgx_sign_data_t* d
         return -PAL_ERROR_DENIED;
     }
 
-    SGX_DBG(DBG_S, "Generated report:\n");
-    SGX_DBG(DBG_S, "    cpusvn:           %08lx %08lx\n", report->cpusvn[0],
-                                                report->cpusvn[1]);
-    SGX_DBG(DBG_S, "    mrenclave:        %s\n",        ALLOCA_BYTES2HEXSTR(report->mrenclave));
-    SGX_DBG(DBG_S, "    mrsigner:         %s\n",        ALLOCA_BYTES2HEXSTR(report->mrsigner));
-    SGX_DBG(DBG_S, "    attributes.flags: %016lx\n",    report->attributes.flags);
-    SGX_DBG(DBG_S, "    sttributes.xfrm:  %016lx\n",    report->attributes.xfrm);
-
-    SGX_DBG(DBG_S, "    isvprodid:        %02x\n",      report->isvprodid);
-    SGX_DBG(DBG_S, "    isvsvn:           %02x\n",      report->isvsvn);
-    SGX_DBG(DBG_S, "    keyid:            %s\n",        ALLOCA_BYTES2HEXSTR(report->keyid));
-    SGX_DBG(DBG_S, "    mac:              %s\n",        ALLOCA_BYTES2HEXSTR(report->mac));
-
+    print_report(report);
     return 0;
 }
 
+
 int sgx_verify_report (sgx_arch_report_t * report)
 {
-    sgx_arch_keyrequest_t keyrequest;
+    sgx_arch_keyrequest_t keyrequest __sgx_mem_aligned;
     memset(&keyrequest, 0, sizeof(sgx_arch_keyrequest_t));
     keyrequest.keyname = REPORT_KEY;
     memcpy(keyrequest.keyid, report->keyid, sizeof(keyrequest.keyid));
 
-    sgx_arch_key128_t report_key;
+    sgx_arch_key128_t report_key __attribute__((aligned(sizeof(sgx_arch_key128_t))));
     memset(&report_key, 0, sizeof(report_key));
 
     int ret = sgx_getkey(&keyrequest, &report_key);
@@ -147,15 +149,19 @@ int sgx_verify_report (sgx_arch_report_t * report)
     sgx_arch_mac_t check_mac;
     memset(&check_mac, 0, sizeof(check_mac));
 
+    // Generating the MAC with AES-CMAC using the report key. Only hash the part of the report
+    // BEFORE the keyid field (hence the offsetof(...) trick). ENCLU[EREPORT] does not include
+    // the MAC and the keyid fields when generating the MAC.
     lib_AESCMAC((uint8_t*)&report_key, sizeof(report_key),
                 (uint8_t*)report, offsetof(sgx_arch_report_t, keyid),
                 (uint8_t*)&check_mac, sizeof(check_mac));
 
+    // Clear the report key for security
     memset(&report_key, 0, sizeof(report_key));
 
     SGX_DBG(DBG_S, "Verify report:\n");
-    SGX_DBG(DBG_S, "    expected:         %s\n", ALLOCA_BYTES2HEXSTR(report->mac));
-    SGX_DBG(DBG_S, "    mac:              %s\n", ALLOCA_BYTES2HEXSTR(check_mac));
+    print_report(report);
+    SGX_DBG(DBG_S, "  verify:     %s\n", ALLOCA_BYTES2HEXSTR(check_mac));
 
     if (memcmp(&check_mac, &report->mac, sizeof(check_mac))) {
         SGX_DBG(DBG_E, "Report verification failed\n");
@@ -167,8 +173,8 @@ int sgx_verify_report (sgx_arch_report_t * report)
 
 int init_enclave_key (void)
 {
-    sgx_arch_keyrequest_t keyrequest;
-    memset(&keyrequest, 0, sizeof(keyrequest));
+    sgx_arch_keyrequest_t keyrequest __sgx_mem_aligned;
+    memset(&keyrequest, 0, sizeof(sgx_arch_keyrequest_t));
     keyrequest.keyname = SEAL_KEY;
 
     int ret = sgx_getkey(&keyrequest, &enclave_key);
@@ -950,17 +956,17 @@ int init_enclave (void)
 
     // Since this report is only read by ourselves we can
     // leave targetinfo zeroed.
-    sgx_arch_targetinfo_t targetinfo = {0};
+    sgx_arch_targetinfo_t targetinfo __sgx_mem_aligned = {0};
     struct pal_enclave_state reportdata = {0};
-    sgx_arch_report_t report;
+    sgx_arch_report_t report __sgx_mem_aligned;
 
     int ret = sgx_report(&targetinfo, &reportdata, &report);
     if (ret)
         return -PAL_ERROR_INVAL;
 
-    memcpy(pal_sec.mrenclave, report.mrenclave, sizeof(pal_sec.mrenclave));
-    memcpy(pal_sec.mrsigner, report.mrsigner, sizeof(pal_sec.mrsigner));
-    pal_sec.enclave_attributes = report.attributes;
+    memcpy(pal_sec.mrenclave, report.body.mrenclave, sizeof(pal_sec.mrenclave));
+    memcpy(pal_sec.mrsigner,  report.body.mrsigner,  sizeof(pal_sec.mrsigner));
+    pal_sec.enclave_attributes = report.body.attributes;
 
 #if 0
     /*
@@ -1094,8 +1100,8 @@ out_no_final:
  */
 int _DkStreamReportRequest(PAL_HANDLE stream, sgx_sign_data_t* data,
                            check_mrenclave_t check_mrenclave) {
-    sgx_arch_targetinfo_t target_info;
-    sgx_arch_report_t report;
+    sgx_arch_targetinfo_t target_info __sgx_mem_aligned;
+    sgx_arch_report_t report __sgx_mem_aligned;
     uint64_t bytes;
     int64_t ret;
 
@@ -1133,7 +1139,7 @@ int _DkStreamReportRequest(PAL_HANDLE stream, sgx_sign_data_t* data,
     }
 
     SGX_DBG(DBG_S, "Received local report (mrenclave = %s)\n",
-            ALLOCA_BYTES2HEXSTR(report.mrenclave));
+            ALLOCA_BYTES2HEXSTR(report.body.mrenclave));
 
     /* Verify report[B -> A] */
     ret = sgx_verify_report(&report);
@@ -1142,8 +1148,8 @@ int _DkStreamReportRequest(PAL_HANDLE stream, sgx_sign_data_t* data,
         goto out;
     }
 
-    struct pal_enclave_state* remote_state = (void*)&report.report_data;
-    ret = check_mrenclave(stream, &report.mrenclave, remote_state);
+    struct pal_enclave_state* remote_state = (void*)&report.body.report_data;
+    ret = check_mrenclave(stream, &report.body.mrenclave, remote_state);
     if (ret < 0) {
         SGX_DBG(DBG_E, "Failed to check local report: %ld\n", ret);
         goto out;
@@ -1151,7 +1157,7 @@ int _DkStreamReportRequest(PAL_HANDLE stream, sgx_sign_data_t* data,
 
     if (ret == 1) {
         SGX_DBG(DBG_E, "Not an allowed enclave (mrenclave = %s). Maybe missing 'sgx.trusted_children' in the manifest file?\n",
-                ALLOCA_BYTES2HEXSTR(report.mrenclave));
+                ALLOCA_BYTES2HEXSTR(report.body.mrenclave));
         ret = -PAL_ERROR_DENIED;
         goto out;
     }
@@ -1159,8 +1165,8 @@ int _DkStreamReportRequest(PAL_HANDLE stream, sgx_sign_data_t* data,
     SGX_DBG(DBG_S, "Local attestation succeeded!\n");
 
     /* A -> B: report[A -> B] */
-    memcpy(&target_info.mrenclave , &report.mrenclave,  sizeof(sgx_arch_hash_t));
-    memcpy(&target_info.attributes, &report.attributes, sizeof(sgx_arch_attributes_t));
+    memcpy(&target_info.mrenclave , &report.body.mrenclave,  sizeof(sgx_arch_hash_t));
+    memcpy(&target_info.attributes, &report.body.attributes, sizeof(sgx_arch_attributes_t));
 
     ret = sgx_get_report(&target_info, data, &report);
     if (ret < 0) {
@@ -1197,8 +1203,8 @@ out:
  */
 int _DkStreamReportRespond(PAL_HANDLE stream, sgx_sign_data_t* data,
                            check_mrenclave_t check_mrenclave) {
-    sgx_arch_targetinfo_t target_info;
-    sgx_arch_report_t report;
+    sgx_arch_targetinfo_t target_info __sgx_mem_aligned;
+    sgx_arch_report_t report __sgx_mem_aligned;
     uint64_t bytes;
     int64_t ret;
     memset(&target_info, 0, sizeof(target_info));
@@ -1252,7 +1258,7 @@ int _DkStreamReportRespond(PAL_HANDLE stream, sgx_sign_data_t* data,
     }
 
     SGX_DBG(DBG_S, "Received local report (mrenclave = %s)\n",
-            ALLOCA_BYTES2HEXSTR(report.mrenclave));
+            ALLOCA_BYTES2HEXSTR(report.body.mrenclave));
 
     /* Verify report[A -> B] */
     ret = sgx_verify_report(&report);
@@ -1261,8 +1267,8 @@ int _DkStreamReportRespond(PAL_HANDLE stream, sgx_sign_data_t* data,
         goto out;
     }
 
-    struct pal_enclave_state* remote_state = (void*)&report.report_data;
-    ret = check_mrenclave(stream, &report.mrenclave, remote_state);
+    struct pal_enclave_state* remote_state = (void*)&report.body.report_data;
+    ret = check_mrenclave(stream, &report.body.mrenclave, remote_state);
     if (ret < 0) {
         SGX_DBG(DBG_E, "Failed to check mrenclave: %ld\n", ret);
         goto out;
@@ -1270,7 +1276,7 @@ int _DkStreamReportRespond(PAL_HANDLE stream, sgx_sign_data_t* data,
 
     if (ret == 1) {
         SGX_DBG(DBG_E, "Not an allowed enclave (mrenclave = %s). Maybe missing 'sgx.trusted_children' in the manifest file?\n",
-                ALLOCA_BYTES2HEXSTR(report.mrenclave));
+                ALLOCA_BYTES2HEXSTR(report.body.mrenclave));
         ret = -PAL_ERROR_DENIED;
         goto out;
     }

+ 100 - 0
Pal/src/host/Linux-SGX/enclave_ocalls.c

@@ -1102,3 +1102,103 @@ int ocall_load_debug(const char * command)
     sgx_reset_ustack();
     return retval;
 }
+
+/*
+ * ocall_get_attestation() triggers remote attestation in untrusted PAL (see sgx_platform.c:
+ * retrieve_verified_quote()). If the OCall returns successfully, the function returns
+ * attestation data required for platform verification (i.e., sgx_attestation_t). Except the
+ * QE report, most data fields of the attestation need to be copied into the enclave.
+ *
+ * @spid:        The client SPID registered with the IAS.
+ * @subkey:      SPID subscription key.
+ * @linkable:    Whether the SPID is linkable.
+ * @report:      Local attestation report for the quoting enclave.
+ * @nonce:       Randomly-generated nonce for freshness.
+ * @attestation: Returns the attestation data (QE report, quote, IAS report, signature,
+ *               and certificate chain).
+ */
+int ocall_get_attestation (const sgx_spid_t* spid, const char* subkey, bool linkable,
+                           const sgx_arch_report_t* report, const sgx_quote_nonce_t* nonce,
+                           sgx_attestation_t* attestation) {
+
+    ms_ocall_get_attestation_t * ms;
+    int retval = -EPERM;
+
+    ms = sgx_alloc_on_ustack(sizeof(*ms));
+    if (!ms)
+        goto reset;
+
+    memcpy(&ms->ms_spid,   spid,   sizeof(sgx_spid_t));
+    ms->ms_subkey = sgx_copy_to_ustack(subkey, strlen(subkey) + 1);
+    memcpy(&ms->ms_report, report, sizeof(sgx_arch_report_t));
+    memcpy(&ms->ms_nonce,  nonce,  sizeof(sgx_quote_nonce_t));
+    ms->ms_linkable = linkable;
+
+    retval = sgx_ocall(OCALL_GET_ATTESTATION, ms);
+
+    if (retval >= 0) {
+        // First, try to copy the whole ms->ms_attestation inside
+        if (!sgx_copy_to_enclave(attestation, sizeof(sgx_attestation_t), &ms->ms_attestation,
+                                 sizeof(sgx_attestation_t))) {
+            retval = -EACCES;
+            goto reset;
+        }
+
+        // For calling ocall_unmap_untrusted, need to reset the untrusted stack
+        sgx_reset_ustack();
+
+        // Copy each field inside and free the untrusted buffers
+        if (attestation->quote) {
+            size_t len = attestation->quote_len;
+            sgx_quote_t* quote = malloc(len);
+            if (!sgx_copy_to_enclave(quote, len, attestation->quote, len))
+                retval = -EACCES;
+            ocall_unmap_untrusted(attestation->quote, ALLOC_ALIGNUP(len));
+            attestation->quote = quote;
+        }
+
+        if (attestation->ias_report) {
+            size_t len = attestation->ias_report_len;
+            char* ias_report = malloc(len + 1);
+            if (!sgx_copy_to_enclave(ias_report, len, attestation->ias_report, len))
+                retval = -EACCES;
+            ocall_unmap_untrusted(attestation->ias_report, ALLOC_ALIGNUP(len));
+            ias_report[len] = 0; // Ensure null-ending
+            attestation->ias_report = ias_report;
+        }
+
+        if (attestation->ias_sig) {
+            size_t len = attestation->ias_sig_len;
+            uint8_t* ias_sig = malloc(len);
+            if (!sgx_copy_to_enclave(ias_sig, len, attestation->ias_sig, len))
+                retval = -EACCES;
+            ocall_unmap_untrusted(attestation->ias_sig, ALLOC_ALIGNUP(len));
+            attestation->ias_sig = ias_sig;
+        }
+
+        if (attestation->ias_certs) {
+            size_t len = attestation->ias_certs_len;
+            char* ias_certs = malloc(len + 1);
+            if (!sgx_copy_to_enclave(ias_certs, len, attestation->ias_certs, len))
+                retval = -EACCES;
+            ocall_unmap_untrusted(attestation->ias_certs, ALLOC_ALIGNUP(len));
+            ias_certs[len] = 0; // Ensure null-ending
+            attestation->ias_certs = ias_certs;
+        }
+
+        // At this point, no field should point to outside the enclave
+        if (retval < 0) {
+            if (attestation->quote)      free(attestation->quote);
+            if (attestation->ias_report) free(attestation->ias_report);
+            if (attestation->ias_sig)    free(attestation->ias_sig);
+            if (attestation->ias_certs)  free(attestation->ias_certs);
+        }
+
+        goto out;
+    }
+
+reset:
+    sgx_reset_ustack();
+out:
+    return retval;
+}

+ 4 - 0
Pal/src/host/Linux-SGX/enclave_ocalls.h

@@ -98,3 +98,7 @@ int ocall_rename (const char * oldpath, const char * newpath);
 int ocall_delete (const char * pathname);
 
 int ocall_load_debug (const char * command);
+
+int ocall_get_attestation(const sgx_spid_t* spid, const char* subkey, bool linkable,
+                          const sgx_arch_report_t* report, const sgx_quote_nonce_t* nonce,
+                          sgx_attestation_t* attestation);

+ 651 - 0
Pal/src/host/Linux-SGX/enclave_platform.c

@@ -0,0 +1,651 @@
+/* Copyright (C) 2019, Texas A&M University.
+
+   This file is part of Graphene Library OS.
+
+   Graphene Library OS is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation, either version 3 of the
+   License, or (at your option) any later version.
+
+   Graphene Library OS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pal_linux.h>
+#include <pal_linux_error.h>
+#include <pal_internal.h>
+#include <pal_debug.h>
+#include <pal_security.h>
+#include <pal_crypto.h>
+#include <api.h>
+
+#include "quote/generated-cacert.h"
+
+/*
+ * Graphene's simple remote attestation feature:
+ *
+ * This feature is for verifying the SGX hardware platforms for executing applications
+ * in Graphene, to a remote trusted entity. The whole remote attestation process requires
+ * interaction with the Intel Attestation Service (IAS) and Intel PSW enclave for
+ * generating the remote and local quotes. Once the platform is fully verified, Graphene
+ * will continue to initialize the library OS and the application; otherwise the execution
+ * should terminate.
+ *
+ * Graphene's remote attestation process is meant to be transparent to the application;
+ * that is, no change is required to the source code or binary of the application. The
+ * remote attestation feature is enabled if "sgx.ra_client_spid" and "sgx.ra_client_key"
+ * are specified in the manifest. To obtain the SPID and the subscription key, register in
+ * the Intel API Portal: https://api.portal.trustedservices.intel.com/EPID-attestation.
+ *
+ * The remote attestation process contains four steps:
+ *
+ * (1) Initialization:
+ *
+ *    +-------------------+                    +-----------+                     +---------+
+ *    | Intel PSW Enclave |  target info (PSW) | Untrusted |  target info (PSW)  | Enclave |
+ *    |      (AESMD)      |------------------->|    PAL    |-------------------->|   PAL   |
+ *    +-------------------+                    +-----------+                     +---------+
+ *
+ *    Before the enclave is created, Graphene contacts the AESMD to retrieve the target info
+ *    of the Intel PSW enclave. The target info is used for generating local report later.
+ *
+ * (2) OCALL + Local attestation:
+ *
+ *    +---------+                        +-----------+                   +-------------------+
+ *    | Enclave | OCALL(GET_ATTESTATION) | Untrusted | Report (PAL->PSW) | Intel PSW Enclave |
+ *    |   PAL   |----------------------->|    PAL    |------------------>|      (AESMD)      |
+ *    +---------+                        +-----------+                   +-------------------+
+ *
+ *    The enclave PAL uses ENCLU[EREPORT] to generate a local report for the PSW enclave to
+ *    verify that the two enclaves are on the same platform. The enclave PAL then issues an
+ *    OCALL(GET_ATTESTATION), along with the report, the SPID, and the subscription key.
+ *    The report is given to the PSW enclave to generate a local quote. The PSW enclave will
+ *    verify the report, decide whether to trust the Graphene enclave, and then sign the
+ *    local quote with a PSW-only attestation key.
+ *
+ * (3) Contact the IAS for platform report:
+ *
+ *    The local quote from the PSW enclave needs to be verified by the IAS. Different from the
+ *    Intel SDK model, Graphene does not rely on a third party to contact the IAS.
+ *    Graphene contacts the IAS as part of its remote attestation process.
+ *
+ *    +-----------+               +--------------+                            +---------------+
+ *    | Untrusted | fork + execve | HTTPS client |  HTTPS (quote, SPID, key)  | Intel Attest. |
+ *    |    PAL    |-------------->|    (CURL)    |--------------------------->|    Service    |
+ *    +-----------+               +--------------+                            +---------------+
+ *
+ *    Graphene now uses a commodity HTTPS client (CURL) to contact the IAS. This is not fully
+ *    compliant to the TOS of the IAS, because the SPID and the key is not protected from
+ *    the untrusted host. The verification of the SGX platform does not require secrecy of the
+ *    SPID/key, because the SPID/key is only used by the IAS to identify the clients. Even if
+ *    an attacker has obtained the SPID/key, the attacker cannot tamper the quote and the IAS
+ *    attestation report.
+ *
+ * (4) Checking the IAS report:
+ *
+ *    +---------------+                   +-----------+ HTTPS resp, Certs,  +---------+
+ *    | Intel Attest. | HTTPS resp, Certs | Untrusted | Report (PSW->PAL)   | Enclave |
+ *    |    Service    |------------------>|    PAL    |-------------------->|   PAL   |
+ *    +---------------+                   +-----------+                     +---------+
+ *
+ *    Finally, Graphene returns the HTTPS response and a certificate chain from the IAS
+ *    back to the enclave PAL, along with the local report from the PSW enclave. Graphene
+ *    then verifies the attestation result based on the following criteria:
+ *
+ *    - The HTTPS response needs to be signed by the certificate chain, including the first
+ *      certificate to generate the signature, and all the following certificates to sign
+ *      the previous certificates.
+ *    - The last certificate in the chain will be signed by a known IAS root CA, hard-coded
+ *      in the enclave PAL.
+ *    - The report from the PSW enclave needs to be verified. This will establish the mutual
+ *      trust between the enclave PAL and the PSW enclave.
+ *    - The HTTPS response from the IAS needs to contain the same quote generated from the
+ *      PSW enclave, the same mrenclave, attributes, and 64-byte report data.
+ *    - The HTTPS response needs to have an acceptable status, which is "OK" by default, or
+ *      "GROUP_OUT_OF_DATE" if "sgx.ra_accept_group_out_of_date = 1" is in the manifest.
+ *      If you obtain a status besides OK, please see the SECURITY ADVISORIES in README.md.
+ */
+
+
+/*
+ * Perform the initial attestation procedure if "sgx.ra_client.spid" is specified in
+ * the manifest file.
+ */
+int init_trusted_platform(void) {
+    char spid_hex[sizeof(sgx_spid_t) * 2 + 1];
+    ssize_t len = get_config(pal_state.root_config, "sgx.ra_client_spid", spid_hex,
+                             sizeof(spid_hex));
+    if (len <= 0) {
+        SGX_DBG(DBG_E, "*** No client info specified in the manifest. "
+                "Graphene will not perform remote attestation ***\n");
+        return 0;
+    }
+
+    if (len != sizeof(sgx_spid_t) * 2) {
+        SGX_DBG(DBG_E, "Malformed sgx.ra_client_spid value in the manifest: %s\n", spid_hex);
+        return -PAL_ERROR_INVAL;
+    }
+
+    sgx_spid_t spid;
+    for (ssize_t i = 0; i < len; i++) {
+        int8_t val = hex2dec(spid_hex[i]);
+        if (val < 0) {
+            SGX_DBG(DBG_E, "Malformed sgx.ra_client_spid value in the manifest: %s\n", spid_hex);
+            return -PAL_ERROR_INVAL;
+        }
+        spid[i/2] = spid[i/2] * 16 + (uint8_t)val;
+    }
+
+    char subkey[CONFIG_MAX];
+    len = get_config(pal_state.root_config, "sgx.ra_client_key", subkey, CONFIG_MAX);
+    if (len <= 0) {
+        SGX_DBG(DBG_E, "No sgx.ra_client_key in the manifest\n");
+        return -PAL_ERROR_INVAL;
+    }
+
+    char buf[2];
+    len = get_config(pal_state.root_config, "sgx.ra_client_linkable", buf, sizeof(buf));
+    bool linkable = (len == 1 && buf[0] == '1');
+
+    len = get_config(pal_state.root_config, "sgx.ra_accept_group_out_of_date", buf, sizeof(buf));
+    bool accept_group_out_of_date = (len == 1 && buf[0] == '1');
+
+    sgx_quote_nonce_t nonce;
+    int ret = _DkRandomBitsRead(&nonce, sizeof(nonce));
+    if (ret < 0)
+        return ret;
+
+    char* status;
+    char* timestamp;
+    ret = sgx_verify_platform(&spid, subkey, &nonce, (sgx_arch_report_data_t*)&pal_enclave_state,
+                              linkable, accept_group_out_of_date, NULL, &status, &timestamp);
+    if (ret < 0)
+        return ret;
+
+    // If the attestation is successful, update the control block
+    __pal_control.attestation_status = status;
+    __pal_control.attestation_timestamp = timestamp;
+    return ret;
+}
+
+/*
+ * A simple function to parse a X509 certificate for only the certificate body, the signature,
+ * and the public key.
+ *
+ * TODO: Currently no verification of the X509 certificate.
+ *
+ * @cert:     The certificate to parse (DER format).
+ * @cert_len: The length of cert.
+ * @body:     The certificate body (the signed part).
+ * @body_len: The length of body.
+ * @sig:      The certificate signature.
+ * @sig_len:  The length of sig.
+ * @pubkey:   The RSA public key from the certificate.
+ */
+static int parse_x509(uint8_t* cert, size_t cert_len, uint8_t** body, size_t* body_len,
+                      uint8_t** sig, size_t* sig_len, LIB_RSA_KEY* pubkey) {
+    uint8_t* ptr = cert;
+    uint8_t* end = cert + cert_len;
+    enum asn1_tag tag;
+    bool is_cons;
+    uint8_t* buf;
+    size_t buf_len;
+    int ret;
+
+    // X509Certificate := SEQUENCE {
+    //     Body CertificateBody,
+    //     SignatureAlgorithm AlgorithmDescriptor,
+    //     Signature BIT STRING }
+
+    ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
+    if (ret < 0)
+        return ret;
+    if (tag != ASN1_SEQUENCE || !is_cons)
+        return -PAL_ERROR_INVAL;
+
+    uint8_t* cert_signed = ptr = buf;
+    uint8_t* cert_body;
+    uint8_t* cert_sig;
+    size_t cert_body_len, cert_sig_len;
+    end = buf + buf_len;
+
+    ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &cert_body, &cert_body_len);
+    if (ret < 0)
+        return ret;
+    if (tag != ASN1_SEQUENCE || !is_cons)
+        return -PAL_ERROR_INVAL;
+
+    size_t cert_signed_len = ptr - cert_signed;
+
+    // Skip SignatureAlgorithm
+    ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
+    if (ret < 0)
+        return ret;
+
+    ret = lib_ASN1GetBitstring(&ptr, end, &cert_sig, &cert_sig_len);
+    if (ret < 0)
+        return ret;
+
+    // CertficateBody := SEQUENCE {
+    //     Version CONSTANT,
+    //     SerialNumber INTEGER,
+    //     Signature AlgorithmDiscriptor,
+    //     Issuer Name,
+    //     Validity ValidityTime,
+    //     Subject Name,
+    //     SubjectPublicKeyInfo PublicKeyInfo,
+    //     (optional fields) }
+
+    ptr = cert_body;
+    end = cert_body + cert_body_len;
+
+    // Skip Version, SerialNumber, Signature, Issuer, Validity, and Subject
+    for (int i = 0; i < 6; i++) {
+        ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
+        if (ret < 0)
+            return ret;
+    }
+
+    // Get SubjectPublicKeyInfo
+    ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
+    if (ret < 0)
+        return ret;
+    if (tag != ASN1_SEQUENCE || !is_cons)
+        return -PAL_ERROR_INVAL;
+
+    // PublickKeyInfo := SEQUENCE {
+    //     PublicKeyAlgorithm AlgorithmDescriptor,
+    //     PublicKey BIT STRING }
+
+    ptr = buf;
+    end = buf + buf_len;
+
+    // Skip PublicKeyAlgorithm
+    ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
+    if (ret < 0)
+        return ret;
+
+    // Get PublicKey
+    uint8_t* pkey_bits;
+    size_t pkey_bits_len;
+
+    ret = lib_ASN1GetBitstring(&ptr, end, &pkey_bits, &pkey_bits_len);
+    if (ret < 0)
+        return ret;
+
+    // RSAPublicKey := SEQUENCE {
+    //    Modulus Integer,
+    //    PublicExponent Integer }
+
+    ptr = pkey_bits;
+    end = pkey_bits + pkey_bits_len;
+
+    ret = lib_ASN1GetSerial(&ptr, end, &tag, &is_cons, &buf, &buf_len);
+    if (ret < 0)
+        return ret;
+
+    uint8_t* mod;
+    uint8_t* exp;
+    size_t mod_len, exp_len;
+    ptr = buf;
+    end = buf + buf_len;
+
+    ret = lib_ASN1GetLargeNumberLength(&ptr, end, &mod_len);
+    if (ret < 0)
+        return ret;
+
+    mod = ptr;
+    ptr += mod_len;
+
+    ret = lib_ASN1GetLargeNumberLength(&ptr, end, &exp_len);
+    if (ret < 0)
+        return ret;
+
+    exp = ptr;
+    ptr += exp_len;
+
+    *body = malloc(cert_signed_len);
+    *body_len = cert_signed_len;
+    memcpy(*body, cert_signed, cert_signed_len);
+
+    *sig = malloc(cert_sig_len);
+    *sig_len = cert_sig_len;
+    memcpy(*sig, cert_sig, cert_sig_len);
+
+    ret = lib_RSAInitKey(pubkey);
+    if (ret < 0)
+        return ret;
+
+    ret = lib_RSAImportPublicKey(pubkey, exp, exp_len, mod, mod_len);
+    if (ret < 0)
+        return ret;
+
+    return 0;
+}
+
+/*
+ * Same as parse_x509(), but parse the certificate in PEM format.
+ *
+ * @cert:     The starting address for parsing the certificate.
+ * @cert_end: Returns the end of certificate after parsing.
+ * @body:     The certificate body (the signed part).
+ * @body_len: The length of body.
+ * @sig:      The certificate signature.
+ * @sig_len:  The length of sig.
+ * @pubkey:   The RSA public key from the certificate.
+ */
+static int parse_x509_pem(char* cert, char** cert_end, uint8_t** body, size_t* body_len,
+                          uint8_t** sig, size_t* sig_len, LIB_RSA_KEY* pubkey) {
+
+    int ret;
+    char* start = strchr(cert, '-');
+    if (!start) {
+        // No more certificate
+        *cert_end = NULL;
+        return 0;
+    }
+
+    if (!strpartcmp_static(start, "-----BEGIN CERTIFICATE-----"))
+        return -PAL_ERROR_INVAL;
+
+    start += static_strlen("-----BEGIN CERTIFICATE-----");
+    char* end = strchr(start, '-');
+
+    if (!strpartcmp_static(end, "-----END CERTIFICATE-----"))
+        return -PAL_ERROR_INVAL;
+
+    size_t cert_der_len;
+    ret = lib_Base64Decode(start, end - start, NULL, &cert_der_len);
+    if (ret < 0)
+        return ret;
+
+    uint8_t* cert_der = __alloca(cert_der_len);
+    ret = lib_Base64Decode(start, end - start, cert_der, &cert_der_len);
+    if (ret < 0)
+        return ret;
+
+    ret = parse_x509(cert_der, cert_der_len, body, body_len, sig, sig_len, pubkey);
+    if (ret < 0)
+        return ret;
+
+    *cert_end = end + static_strlen("-----END CERTIFICATE-----");
+    return 0;
+}
+
+/*
+ * Perform the remote attestation to verify the current SGX platform.
+ *
+ * The remote attestation procedure verifies two primary properties: (1) The current execution
+ * runs in an SGX enclave; and (2) The enclave is created on a genuine, up-to-date Intel CPU.
+ * This procedure requires interaction with the Intel PSW quoting enclave (AESMD) and the
+ * Intel Attestation Service (IAS). The quoting enclave verifies a local attestation report
+ * from the target enclave, and then generates a quoting enclave (QE) report and a platform
+ * quote signed by the platform's attestation key. The IAS then verifies the platform quote and
+ * issues a remote attestation report, signed by a certificate chain attached to the report.
+ *
+ * TODO: currently no verification of the correctness of the IAS certificate
+ *
+ * @spid:              The SPID registered for the Intel Attestation Service (IAS).
+ * @subkey:            SPID subscription key.
+ * @nonce:             A 16-byte nonce to be included in the quote.
+ * @report_data:       A 64-byte bytestring to be included in the local report and the quote.
+ * @linkable:          Specify whether the SPID is linkable.
+ * @accept_group_out_of_date: Specify whether to accept GROUP_OUT_OF_DATE from IAS
+ * @ret_attestation:   Returns the retrieved attestation data.
+ * @ret_ias_status:    Returns a pointer to the attestation status (as a string) from the IAS.
+ * @ret_ias_timestamp: Returns a pointer to the timestamp (as a string) from the IAS.
+ *                     Timestamp format: %Y-%m-%dT%H:%M:%S.%f (Ex: 2019-08-01T12:30:00.123456)
+ */
+int sgx_verify_platform(sgx_spid_t* spid, const char* subkey, sgx_quote_nonce_t* nonce,
+                        sgx_arch_report_data_t* report_data, bool linkable,
+                        bool accept_group_out_of_date, sgx_attestation_t* ret_attestation,
+                        char** ret_ias_status, char** ret_ias_timestamp) {
+
+    SGX_DBG(DBG_S, "Request quote:\n");
+    SGX_DBG(DBG_S, "  spid:  %s\n", ALLOCA_BYTES2HEXSTR(*spid));
+    SGX_DBG(DBG_S, "  type:  %s\n", linkable ? "linkable" : "unlinkable");
+    SGX_DBG(DBG_S, "  nonce: %s\n", ALLOCA_BYTES2HEXSTR(*nonce));
+
+    sgx_arch_report_t report __sgx_mem_aligned;
+    sgx_arch_targetinfo_t targetinfo __sgx_mem_aligned = pal_sec.aesm_targetinfo;
+
+    int ret = sgx_report(&targetinfo, report_data, &report);
+    if (ret) {
+        SGX_DBG(DBG_E, "Failed to get report for attestation\n");
+        return -PAL_ERROR_DENIED;
+    }
+
+    sgx_attestation_t attestation;
+    ret = ocall_get_attestation(spid, subkey, linkable, &report, nonce, &attestation);
+    if (ret < 0) {
+        SGX_DBG(DBG_E, "Failed to get attestation\n");
+        return ret;
+    }
+
+    // First, verify the report from the quoting enclave
+    ret = sgx_verify_report(&attestation.qe_report);
+    if (ret < 0) {
+        SGX_DBG(DBG_E, "Failed to verify QE report, ret = %d\n", ret);
+        goto failed;
+    }
+
+    // Verify the IAS response against the certificate chain
+    uint8_t* data_to_verify = (uint8_t*)attestation.ias_report;
+    uint8_t* data_sig       = attestation.ias_sig;
+    size_t   data_len       = attestation.ias_report_len;
+    size_t   data_sig_len   = attestation.ias_sig_len;
+
+    // Attach the IAS signing chain with the hard-coded CA certificate
+    const char* ca_cert = IAS_CA_CERT;
+    size_t len1 = strlen(attestation.ias_certs);
+    size_t len2 = static_strlen(IAS_CA_CERT);
+    char* certs = malloc(len1 + len2 + 1);
+    memcpy(certs, attestation.ias_certs, len1);
+    memcpy(certs + len1, ca_cert, len2);
+    certs[len1 + len2] = 0;
+    free(attestation.ias_certs);
+    attestation.ias_certs = certs;
+    attestation.ias_certs_len = len1 + len2 + 1;
+
+    // There can be multiple certificates in the chain. We need to use the public key from
+    // the *first* certificate to verify the IAS response. For each certificate except
+    // the last one, we need to use the public key from the *next* certificate to verify
+    // the certificate body. The last certificate will be verified by the CA certificate
+    // (hard-coded in the binary)
+
+    for (char* cert_start = attestation.ias_certs;
+         cert_start < attestation.ias_certs + attestation.ias_certs_len && *cert_start; ) {
+
+        // Generate the message digest first (without RSA)
+        LIB_SHA256_CONTEXT ctx;
+        uint8_t hash[32];
+
+        if ((ret = lib_SHA256Init(&ctx)) < 0)
+            goto failed;
+
+        if ((ret = lib_SHA256Update(&ctx, data_to_verify, data_len)) < 0)
+            goto failed;
+
+        if ((ret = lib_SHA256Final(&ctx, hash)) < 0)
+            goto failed;
+
+        // Use the public key to verify the last signature
+        uint8_t*    cert_body;
+        uint8_t*    cert_sig;
+        size_t      cert_body_len;
+        size_t      cert_sig_len;
+        LIB_RSA_KEY cert_key;
+
+        ret = parse_x509_pem(cert_start, &cert_start, &cert_body, &cert_body_len, &cert_sig,
+                             &cert_sig_len, &cert_key);
+        if (ret < 0) {
+            SGX_DBG(DBG_E, "Failed to parse IAS certificate, rv = %d\n", ret);
+            goto failed;
+        }
+
+        ret = lib_RSAVerifySHA256(&cert_key, hash, sizeof(hash), data_sig, data_sig_len);
+        if (ret < 0) {
+            SGX_DBG(DBG_E, "Failed to verify the report against the IAS certificates,"
+                    " rv = %d\n", ret);
+            lib_RSAFreeKey(&cert_key);
+            goto failed;
+        }
+
+        lib_RSAFreeKey(&cert_key);
+
+        data_to_verify = cert_body;
+        data_sig       = cert_sig;
+        data_len       = cert_body_len;
+        data_sig_len   = cert_sig_len;
+    }
+
+    // Parse the IAS report in JSON format
+    char* ias_status    = NULL;
+    char* ias_timestamp = NULL;
+    sgx_quote_nonce_t* ias_nonce = NULL;
+    sgx_quote_t*       ias_quote = NULL;
+    char* start = attestation.ias_report;
+    if (start[0] == '{') start++;
+    char* end = strchr(start, ',');
+    while (end) {
+        char* next_start = end + 1;
+
+        // Retrieve the key and value separated by the colon (:)
+        char* delim = strchr(start, ':');
+        if (!delim)
+            break;
+        char*  key  = start;
+        char*  val  = delim + 1;
+        size_t klen = delim - start;
+        size_t vlen = end - val;
+
+        // Remove quotation marks (") around the key and value if there are any
+        if (key[0] == '"') { key++; klen--; }
+        if (key[klen - 1] == '"') klen--;
+        if (val[0] == '"') { val++; vlen--; }
+        if (val[vlen - 1] == '"') vlen--;
+
+        // Scan the fields in the IAS report.
+        if (!memcmp(key, "isvEnclaveQuoteStatus", klen)) {
+            // Parse "isvEnclaveQuoteStatus":"OK"|"GROUP_OUT_OF_DATE"|...
+            ias_status = __alloca(vlen + 1);
+            memcpy(ias_status, val, vlen);
+            ias_status[vlen] = 0;
+        } else if (!memcmp(key, "nonce", klen)) {
+            // Parse "nonce":"{Hex representation of the initial nonce}"
+            if (vlen != sizeof(sgx_quote_nonce_t) * 2) {
+                SGX_DBG(DBG_E, "Malformed nonce in the IAS report\n");
+                goto failed;
+            }
+
+            ias_nonce = __alloca(sizeof(sgx_quote_nonce_t));
+            for (size_t i = 0; i < sizeof(sgx_quote_nonce_t); i++) {
+                int8_t hi = hex2dec(val[i * 2]);
+                int8_t lo = hex2dec(val[i * 2 + 1]);
+                if (hi < 0 || lo < 0) {
+                    SGX_DBG(DBG_E, "Malformed nonce in the IAS report\n");
+                    goto failed;
+                }
+                ((uint8_t*)ias_nonce)[i] = (uint8_t)hi * 16 + (uint8_t)lo;
+            }
+        } else if (!memcmp(key, "timestamp", klen)) {
+            // Parse "timestamp":"{IAS timestamp (format: %Y-%m-%dT%H:%M:%S.%f)}"
+            ias_timestamp = __alloca(vlen + 1);
+            memcpy(ias_timestamp, val, vlen);
+            ias_timestamp[vlen] = 0;
+        } else if (!memcmp(key, "isvEnclaveQuoteBody", klen)) {
+            // Parse "isvEnclaveQuoteBody":"{Quote body (in Base64 format)}"
+            size_t ias_quote_len;
+            ret = lib_Base64Decode(val, vlen, NULL, &ias_quote_len);
+            if (ret < 0) {
+                SGX_DBG(DBG_E, "Malformed quote in the IAS report\n");
+                goto failed;
+            }
+
+            ias_quote = __alloca(ias_quote_len);
+            ret = lib_Base64Decode(val, vlen, (uint8_t*)ias_quote, &ias_quote_len);
+            if (ret < 0) {
+                SGX_DBG(DBG_E, "Malformed quote in the IAS report\n");
+                goto failed;
+            }
+        }
+
+        start = next_start;
+        end = strchr(start, ',') ? : strchr(start, '}');
+    }
+
+    if (!ias_status || !ias_nonce || !ias_timestamp || !ias_quote) {
+        SGX_DBG(DBG_E, "Missing important field(s) in the IAS report\n");
+        goto failed;
+    }
+
+    SGX_DBG(DBG_S, "Quote:\n");
+    SGX_DBG(DBG_S, "  version:    %04x\n",  ias_quote->body.version);
+    SGX_DBG(DBG_S, "  sigtype:    %04x\n",  ias_quote->body.sigtype);
+    SGX_DBG(DBG_S, "  gid:        %08x\n",  ias_quote->body.gid);
+    SGX_DBG(DBG_S, "  isvsvn qe:  %08x\n",  ias_quote->body.isvsvn_qe);
+    SGX_DBG(DBG_S, "  isvsvn pce: %08x\n",  ias_quote->body.isvsvn_pce);
+
+    SGX_DBG(DBG_S, "IAS report: %s\n", attestation.ias_report);
+    SGX_DBG(DBG_S, "  status:    %s\n", ias_status);
+    SGX_DBG(DBG_S, "  timestamp: %s\n", ias_timestamp);
+
+    // Only accept status to be "OK" or "GROUP_OUT_OF_DATE" (if accept_out_of_date is true)
+    if (!strcmp_static(ias_status, "OK") &&
+        (!accept_group_out_of_date || !strcmp_static(ias_status, "GROUP_OUT_OF_DATE"))) {
+        SGX_DBG(DBG_E, "IAS returned invalid status: %s\n", ias_status);
+        goto failed;
+    }
+
+    // Check if the nonce matches the IAS report
+    if (memcmp(ias_nonce, nonce, sizeof(sgx_quote_nonce_t))) {
+        SGX_DBG(DBG_E, "IAS returned the wrong nonce\n");
+        goto failed;
+    }
+
+    // Check if the quote matches the IAS report
+    if (memcmp(&ias_quote->body, &attestation.quote->body, sizeof(sgx_quote_body_t)) ||
+        memcmp(&ias_quote->report_body, &report.body, sizeof(sgx_arch_report_body_t))) {
+        SGX_DBG(DBG_E, "IAS returned the wrong quote\n");
+        goto failed;
+    }
+
+    // Check if the quote has the right enclave report
+    if (memcmp(&attestation.quote->report_body, &report.body, sizeof(sgx_arch_report_body_t))) {
+        SGX_DBG(DBG_E, "The returned quote contains the wrong enclave report\n");
+        goto failed;
+    }
+
+    // Succeeded!!!
+    if (ret_ias_status) {
+        size_t len = strlen(ias_status) + 1;
+        *ret_ias_status = malloc(len);
+        memcpy(*ret_ias_status, ias_status, len);
+    }
+
+    if (ret_ias_timestamp) {
+        size_t len = strlen(ias_timestamp) + 1;
+        *ret_ias_timestamp = malloc(len);
+        memcpy(*ret_ias_timestamp, ias_timestamp, len);
+    }
+
+    if (ret_attestation) {
+        memcpy(ret_attestation, &attestation, sizeof(sgx_attestation_t));
+        return 0;
+    }
+    ret = 0;
+free_attestation:
+    if (attestation.quote)      free(attestation.quote);
+    if (attestation.ias_report) free(attestation.ias_report);
+    if (attestation.ias_sig)    free(attestation.ias_sig);
+    if (attestation.ias_certs)  free(attestation.ias_certs);
+    return ret;
+
+failed:
+    ret = -PAL_ERROR_DENIED;
+    goto free_attestation;
+}

+ 14 - 0
Pal/src/host/Linux-SGX/ocall_types.h

@@ -2,8 +2,12 @@
  * This is for enclave to make ocalls to untrusted runtime.
  */
 
+#include <stdbool.h>
+#include <stddef.h>
 #include "linux_types.h"
 #include "pal.h"
+#include "sgx_arch.h"
+#include "sgx_attest.h"
 
 /*
  * GCC's structure padding may cause leaking from uninialized
@@ -53,6 +57,7 @@ enum {
     OCALL_RENAME,
     OCALL_DELETE,
     OCALL_LOAD_DEBUG,
+    OCALL_GET_ATTESTATION,
     OCALL_NR,
 };
 
@@ -265,4 +270,13 @@ typedef struct {
     unsigned int ms_tid;
 } ms_ocall_schedule_t;
 
+typedef struct {
+    sgx_spid_t        ms_spid;
+    const char*       ms_subkey;
+    bool              ms_linkable;
+    sgx_arch_report_t ms_report;
+    sgx_quote_nonce_t ms_nonce;
+    sgx_attestation_t ms_attestation;
+} ms_ocall_get_attestation_t;
+
 #pragma pack(pop)

+ 1 - 0
Pal/src/host/Linux-SGX/pal_linux.h

@@ -26,6 +26,7 @@
 #include "sgx_arch.h"
 #include "sgx_tls.h"
 #include "sgx_api.h"
+#include "sgx_attest.h"
 #include "enclave_ocalls.h"
 
 #include <linux/mman.h>

+ 1 - 0
Pal/src/host/Linux-SGX/pal_security.h

@@ -28,6 +28,7 @@ struct pal_sec {
     PAL_IDX         ppid, pid, uid, gid;
 
     /* enclave information */
+    sgx_arch_targetinfo_t   aesm_targetinfo;
     sgx_arch_hash_t         mrenclave;
     sgx_arch_hash_t         mrsigner;
     sgx_arch_attributes_t   enclave_attributes;

+ 38 - 0
Pal/src/host/Linux-SGX/quote/aesm.proto

@@ -0,0 +1,38 @@
+syntax = "proto2";
+
+message Request {
+    message InitQuoteRequest {
+        optional   uint32      timeout         = 9;
+    }
+
+    message GetQuoteRequest {
+        required   bytes       report          = 1;
+        required   uint32      quote_type      = 2;
+        required   bytes       spid            = 3;
+        optional   bytes       nonce           = 4;
+        optional   bytes       sig_rl          = 5;
+        required   uint32      buf_size        = 6;
+        optional   bool        qe_report       = 7;
+        optional   uint32      timeout         = 9;
+    }
+
+    optional   InitQuoteRequest    initQuoteReq                = 1;
+    optional   GetQuoteRequest     getQuoteReq                 = 2;
+}
+
+message Response {
+    message InitQuoteResponse {
+        required   uint32      errorCode       = 1 [default = 1];
+        optional   bytes       targetInfo      = 2;
+        optional   bytes       gid             = 3;
+    }
+
+    message GetQuoteResponse {
+        required   uint32      errorCode       = 1 [default = 1];
+        optional   bytes       quote           = 2;
+        optional   bytes       qe_report       = 3;
+    }
+
+    optional   InitQuoteResponse   initQuoteRes                = 1;
+    optional   GetQuoteResponse    getQuoteRes                 = 2;
+}

+ 17 - 6
Pal/src/host/Linux-SGX/sgx_arch.h

@@ -213,6 +213,10 @@ typedef struct {
     sgx_arch_mac_t mac;
 } __attribute__((packed)) sgx_arch_token_t;
 
+typedef uint8_t sgx_arch_report_data_t[64];
+
+#define __sgx_mem_aligned __attribute__((aligned(512)))
+
 typedef struct {
     uint64_t cpusvn[2];
     uint32_t miscselect;
@@ -224,18 +228,25 @@ typedef struct {
     uint8_t  reserved3[96];
     uint16_t isvprodid, isvsvn;
     uint8_t  reserved4[60];
-    uint8_t  report_data[64];
+    sgx_arch_report_data_t report_data;
+} __attribute__((packed)) sgx_arch_report_body_t;
+
+typedef struct {
+    sgx_arch_report_body_t body;
     uint8_t  keyid[32];
     sgx_arch_mac_t mac;
-} __attribute__((packed, aligned(512))) sgx_arch_report_t;
+} __attribute__((packed)) sgx_arch_report_t;
 
 #define SGX_REPORT_SIGNED_SIZE  384
+#define SGX_REPORT_ACTUAL_SIZE  432
 
 typedef struct {
     sgx_arch_hash_t mrenclave;
     sgx_arch_attributes_t attributes;
-    uint8_t  reserved[464];
-} __attribute__((packed, aligned(512))) sgx_arch_targetinfo_t;
+    uint8_t  reserved[4];
+    uint32_t miscselect;
+    uint8_t  reserved2[456];
+} __attribute__((packed)) sgx_arch_targetinfo_t;
 
 #define SGX_TARGETINFO_FILLED_SIZE  (sizeof(sgx_arch_hash_t) + \
                                      sizeof(sgx_arch_attributes_t))
@@ -250,9 +261,9 @@ typedef struct {
     uint8_t  keyid[32];
     uint32_t miscmask;
     uint8_t  reserved2[436];
-} __attribute__((packed, aligned(512))) sgx_arch_keyrequest_t;
+} __attribute__((packed)) sgx_arch_keyrequest_t;
 
-typedef uint8_t sgx_arch_key128_t[16] __attribute__((aligned(16)));
+typedef uint8_t sgx_arch_key128_t[16];
 
 #define ENCLU ".byte 0x0f, 0x01, 0xd7"
 

+ 75 - 0
Pal/src/host/Linux-SGX/sgx_attest.h

@@ -0,0 +1,75 @@
+/* Copyright (C) 2017, Texas A&M University.
+
+   This file is part of Graphene Library OS.
+
+   Graphene Library OS is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation, either version 3 of the
+   License, or (at your option) any later version.
+
+   Graphene Library OS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef SGX_ATTEST_H
+#define SGX_ATTEST_H
+
+#include "sgx_arch.h"
+
+#include <stdint.h>
+
+typedef struct {
+    uint16_t version;
+    uint16_t sigtype;
+    uint32_t gid;
+    uint16_t isvsvn_qe;
+    uint16_t isvsvn_pce;
+    uint8_t  reserved[4];
+    uint8_t  base[32];
+} __attribute__((packed)) sgx_quote_body_t;
+
+typedef struct {
+    sgx_quote_body_t       body;
+    sgx_arch_report_body_t report_body;
+    uint32_t               sig_len;
+} __attribute__((packed)) sgx_quote_t;
+
+typedef uint8_t sgx_spid_t[16];
+typedef uint8_t sgx_quote_nonce_t[16];
+
+enum {
+    SGX_UNLINKABLE_SIGNATURE,
+    SGX_LINKABLE_SIGNATURE
+};
+
+#define SGX_QUOTE_MAX_SIZE   (2048)
+
+#define IAS_REPORT_URL \
+    "https://api.trustedservices.intel.com/sgx/dev/attestation/v3/report"
+
+int init_trusted_platform(void);
+
+typedef struct {
+    sgx_arch_report_t qe_report;
+    sgx_quote_t*      quote;
+    size_t            quote_len;
+    char*             ias_report;
+    size_t            ias_report_len;
+    uint8_t*          ias_sig;
+    size_t            ias_sig_len;
+    char*             ias_certs;
+    size_t            ias_certs_len;
+} __attribute__((packed)) sgx_attestation_t;
+
+int sgx_verify_platform(sgx_spid_t* spid, const char* subkey, sgx_quote_nonce_t* nonce,
+                        sgx_arch_report_data_t* report_data, bool linkable,
+                        bool accept_group_out_of_date, sgx_attestation_t* ret_attestation,
+                        char** ret_ias_status, char** ret_ias_timestamp);
+
+#define HTTPS_REQUEST_MAX_LENGTH   (256)
+
+#endif /* SGX_ATTEST_H */

+ 8 - 0
Pal/src/host/Linux-SGX/sgx_enclave.c

@@ -672,6 +672,13 @@ static int sgx_ocall_load_debug(void * pms)
     return 0;
 }
 
+static int sgx_ocall_get_attestation(void* pms) {
+    ms_ocall_get_attestation_t * ms = (ms_ocall_get_attestation_t *) pms;
+    ODEBUG(OCALL_GET_ATTESTATION, ms);
+    return retrieve_verified_quote(&ms->ms_spid, ms->ms_subkey, ms->ms_linkable, &ms->ms_report,
+                                   &ms->ms_nonce, &ms->ms_attestation);
+}
+
 sgx_ocall_fn_t ocall_table[OCALL_NR] = {
         [OCALL_EXIT]            = sgx_ocall_exit,
         [OCALL_PRINT_STRING]    = sgx_ocall_print_string,
@@ -710,6 +717,7 @@ sgx_ocall_fn_t ocall_table[OCALL_NR] = {
         [OCALL_RENAME]          = sgx_ocall_rename,
         [OCALL_DELETE]          = sgx_ocall_delete,
         [OCALL_LOAD_DEBUG]      = sgx_ocall_load_debug,
+        [OCALL_GET_ATTESTATION] = sgx_ocall_get_attestation,
     };
 
 #define EDEBUG(code, ms) do {} while (0)

+ 6 - 0
Pal/src/host/Linux-SGX/sgx_internal.h

@@ -98,6 +98,12 @@ int add_pages_to_enclave(sgx_arch_secs_t * secs,
                          bool skip_eextend,
                          const char * comment);
 
+int init_aesm_targetinfo(sgx_arch_targetinfo_t* aesm_targetinfo);
+
+int retrieve_verified_quote(const sgx_spid_t* spid, const char* subkey, bool linkable,
+                            const sgx_arch_report_t* report, const sgx_quote_nonce_t* nonce,
+                            sgx_attestation_t* attestation);
+
 int init_enclave(sgx_arch_secs_t * secs,
                  sgx_arch_sigstruct_t * sigstruct,
                  sgx_arch_token_t * token);

+ 13 - 13
Pal/src/host/Linux-SGX/sgx_main.c

@@ -1,6 +1,8 @@
 #include <pal_linux.h>
 #include <pal_linux_error.h>
 #include <pal_rtld.h>
+#include <hex.h>
+
 #include "sgx_internal.h"
 #include "sgx_tls.h"
 #include "sgx_enclave.h"
@@ -12,6 +14,7 @@
 #include <linux/in.h>
 #include <linux/in6.h>
 #include <asm/errno.h>
+#include <ctype.h>
 
 #include <sysdep.h>
 #include <sysdeps/generic/ldsodefs.h>
@@ -53,18 +56,12 @@ static unsigned long parse_int (const char * str)
     }
 
     while ((c = *(str++))) {
-        int val;
-        if (c >= 'A' && c <= 'F')
-            val = c - 'A' + 10;
-        else if (c >= 'a' && c <= 'f')
-            val = c - 'a' + 10;
-        else if (c >= '0' && c <= '9')
-            val = c - '0';
-        else
+        int8_t val = hex2dec(c);
+        if (val < 0)
             break;
-        if (val >= radix)
+        if ((uint8_t) val >= radix)
             break;
-        num = num * radix + val;
+        num = num * radix + (uint8_t) val;
     }
 
     if (c == 'G' || c == 'g')
@@ -652,10 +649,9 @@ void getrand (void * buffer, size_t size)
 
 static void create_instance (struct pal_sec * pal_sec)
 {
-    unsigned int id;
+    PAL_NUM id;
     getrand(&id, sizeof(id));
-    snprintf(pal_sec->pipe_prefix, sizeof(pal_sec->pipe_prefix),
-             "/graphene/%x/", id);
+    snprintf(pal_sec->pipe_prefix, sizeof(pal_sec->pipe_prefix), "/graphene/%016lx/", id);
     pal_sec->instance_id = id;
 }
 
@@ -938,6 +934,10 @@ static int load_enclave (struct pal_enclave * enclave,
     if (ret < 0)
         return ret;
 
+    ret = init_aesm_targetinfo(&pal_sec->aesm_targetinfo);
+    if (ret < 0)
+        return ret;
+
     current_enclave = enclave;
     map_tcs(INLINE_SYSCALL(gettid, 0), /* created_by_pthread=*/false);
 

+ 479 - 0
Pal/src/host/Linux-SGX/sgx_platform.c

@@ -0,0 +1,479 @@
+/* Copyright (C) 2019, Texas A&M University.
+
+   This file is part of Graphene Library OS.
+
+   Graphene Library OS is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation, either version 3 of the
+   License, or (at your option) any later version.
+
+   Graphene Library OS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pal_linux.h>
+#include <pal_rtld.h>
+#include <pal_crypto.h>
+#include <hex.h>
+
+#include "sgx_internal.h"
+#include "sgx_arch.h"
+#include "sgx_enclave.h"
+#include "sgx_attest.h"
+#include "quote/aesm.pb-c.h"
+
+#include <asm/errno.h>
+#include <linux/fs.h>
+#include <linux/un.h>
+#define __USE_XOPEN2K8
+#include <stdlib.h>
+
+/*
+ * Connect to the AESM service to interact with the architectural enclave. Must reconnect
+ * for each request to the AESM service.
+ *
+ * Some older versions of AESM service use a UNIX socket at "\0sgx_aesm_socket_base".
+ * The latest AESM service binds the socket at "/var/run/aesmd/aesm.socket". This function
+ * tries to connect to either of the paths to ensure connectivity.
+ */
+static int connect_aesm_service(void) {
+    int sock = INLINE_SYSCALL(socket, 3, AF_UNIX, SOCK_STREAM, 0);
+    if (IS_ERR(sock))
+        return -ERRNO(sock);
+
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    (void)strcpy_static(addr.sun_path, "\0sgx_aesm_socket_base", sizeof(addr.sun_path));
+
+    int ret = INLINE_SYSCALL(connect, 3, sock, &addr, sizeof(addr));
+    if (!IS_ERR(ret))
+        return sock;
+    if (ERRNO(ret) != ECONNREFUSED)
+        goto err;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    (void)strcpy_static(addr.sun_path, "/var/run/aesmd/aesm.socket", sizeof(addr.sun_path));
+
+    ret = INLINE_SYSCALL(connect, 3, sock, &addr, sizeof(addr));
+    if (!IS_ERR(ret))
+        return sock;
+
+err:
+    INLINE_SYSCALL(close, 1, sock);
+    return -ERRNO(ret);
+}
+
+/*
+ * A wrapper for both creating a connection to the AESM service and submitting a request
+ * to the service. Upon success, the function returns a response from the AESM service
+ * back to the caller.
+ */
+static int request_aesm_service(Request* req, Response** res) {
+
+    int aesm_socket = connect_aesm_service();
+    if (aesm_socket < 0)
+        return aesm_socket;
+
+    uint32_t req_len = (uint32_t) request__get_packed_size(req);
+    uint8_t* req_buf = __alloca(req_len);
+    request__pack(req, req_buf);
+
+    int ret = INLINE_SYSCALL(write, 3, aesm_socket, &req_len, sizeof(req_len));
+    if (IS_ERR(ret))
+        goto err;
+
+    ret = INLINE_SYSCALL(write, 3, aesm_socket, req_buf, req_len);
+    if (IS_ERR(ret))
+        goto err;
+
+    uint32_t res_len;
+    ret = INLINE_SYSCALL(read, 3, aesm_socket, &res_len, sizeof(res_len));
+    if (IS_ERR(ret))
+        goto err;
+
+    uint8_t* res_buf = __alloca(res_len);
+    ret = INLINE_SYSCALL(read, 3, aesm_socket, res_buf, res_len);
+    if (IS_ERR(ret))
+        goto err;
+
+    *res = response__unpack(NULL, res_len, res_buf);
+    ret = *res == NULL ? -EINVAL : 0;
+err:
+    INLINE_SYSCALL(close, 1, aesm_socket);
+    return -ERRNO(ret);
+}
+
+// Retrieve the targetinfo for the AESM enclave for generating the local attestation report.
+int init_aesm_targetinfo(sgx_arch_targetinfo_t* aesm_targetinfo) {
+
+    Request req = REQUEST__INIT;
+    Request__InitQuoteRequest initreq = REQUEST__INIT_QUOTE_REQUEST__INIT;
+    req.initquotereq = &initreq;
+
+    Response* res = NULL;
+    int ret = request_aesm_service(&req, &res);
+    if (ret < 0)
+        return ret;
+
+    ret = -EPERM;
+    if (!res->initquoteres) {
+        SGX_DBG(DBG_E, "aesm_service returned wrong message\n");
+        goto failed;
+    }
+
+    Response__InitQuoteResponse* r = res->initquoteres;
+    if (r->errorcode != 0) {
+        SGX_DBG(DBG_E, "aesm_service returned error: %d\n", r->errorcode);
+        goto failed;
+    }
+
+    if (r->targetinfo.len != sizeof(*aesm_targetinfo)) {
+        SGX_DBG(DBG_E, "aesm_service returned invalid target info\n");
+        goto failed;
+    }
+
+    memcpy(aesm_targetinfo, r->targetinfo.data, sizeof(*aesm_targetinfo));
+    ret = 0;
+failed:
+    response__free_unpacked(res, NULL);
+    return ret;
+}
+
+/*
+ * Contact to Intel Attestation Service and retrieve the signed attestation report
+ *
+ * @subkey:      SPID subscription key.
+ * @nonce:       Random nonce generated in the enclave.
+ * @quote:       Platform quote retrieved from AESMD.
+ * @attestation: Attestation data to be returned to the enclave.
+ */
+int contact_intel_attest_service(const char* subkey, const sgx_quote_nonce_t* nonce,
+                                 const sgx_quote_t* quote, sgx_attestation_t* attestation) {
+
+    size_t quote_len = sizeof(sgx_quote_t) + quote->sig_len;
+    size_t quote_str_len;
+    lib_Base64Encode((uint8_t*)quote, quote_len, NULL, &quote_str_len);
+    char* quote_str = __alloca(quote_str_len);
+    int ret = lib_Base64Encode((uint8_t*)quote, quote_len, quote_str, &quote_str_len);
+    if (ret < 0)
+        return ret;
+
+    size_t nonce_str_len = sizeof(sgx_quote_nonce_t) * 2 + 1;
+    char* nonce_str = __alloca(nonce_str_len);
+    __bytes2hexstr((void *)nonce, sizeof(sgx_quote_nonce_t), nonce_str, nonce_str_len);
+
+    // Create two temporary files for dumping the header and output of HTTPS request to IAS
+    char    https_header_path[] = "/tmp/gsgx-ias-header-XXXXXX";
+    char    https_output_path[] = "/tmp/gsgx-ias-output-XXXXXX";
+    char*   https_header = NULL;
+    char*   https_output = NULL;
+    ssize_t https_header_len = 0;
+    ssize_t https_output_len = 0;
+    int header_fd = -1;
+    int output_fd = -1;
+    int pipefds[2] = { -1, -1 };
+
+    header_fd = mkstemp(https_header_path);
+    if (header_fd < 0)
+        goto failed;
+
+    output_fd = mkstemp(https_output_path);
+    if (output_fd < 0)
+        goto failed;
+
+    ret = INLINE_SYSCALL(pipe, 1, pipefds);
+    if (IS_ERR(ret))
+        goto failed;
+
+    // Write HTTPS request in XML format
+    size_t https_request_len = quote_str_len + nonce_str_len + HTTPS_REQUEST_MAX_LENGTH;
+    char*  https_request = __alloca(https_request_len);
+    https_request_len = snprintf(https_request, https_request_len,
+                                 "{\"isvEnclaveQuote\":\"%s\",\"nonce\":\"%s\"}",
+                                 quote_str, nonce_str);
+
+    ret = INLINE_SYSCALL(write, 3, pipefds[1], https_request, https_request_len);
+    if (IS_ERR(ret))
+        goto failed;
+    INLINE_SYSCALL(close, 1, pipefds[1]);
+    pipefds[1] = -1;
+
+    char subscription_header[64];
+    snprintf(subscription_header, 64, "Ocp-Apim-Subscription-Key: %s", subkey);
+
+    // Start a HTTPS client (using CURL)
+    const char* https_client_args[] = {
+            "/usr/bin/curl", "-s", "--tlsv1.2", "-X", "POST",
+            "-H", "Content-Type: application/json",
+            "-H", subscription_header,
+            "--data", "@-", "-o", https_output_path, "-D", https_header_path,
+            IAS_REPORT_URL, NULL,
+        };
+
+    int pid = ARCH_VFORK();
+    if (IS_ERR(pid))
+        goto failed;
+
+    if (!pid) {
+        INLINE_SYSCALL(dup2, 2, pipefds[0], 0);
+        extern char** environ;
+        INLINE_SYSCALL(execve, 3, https_client_args[0], https_client_args, environ);
+
+        /* shouldn't get to here */
+        SGX_DBG(DBG_E, "unexpected failure of new process\n");
+        __asm__ volatile ("hlt");
+        return 0;
+    }
+
+    // Make sure the HTTPS client has exited properly
+    int status;
+    ret = INLINE_SYSCALL(wait4, 4, pid, &status, 0, NULL);
+    if (IS_ERR(ret) || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
+        goto failed;
+
+    // Read the HTTPS output
+    ret = INLINE_SYSCALL(open, 2, https_output_path, O_RDONLY);
+    if (IS_ERR(ret))
+        goto failed;
+    INLINE_SYSCALL(close, 1, output_fd);
+    output_fd = ret;
+    https_output_len = INLINE_SYSCALL(lseek, 3, output_fd, 0, SEEK_END);
+    if (IS_ERR(https_output_len) || !https_output_len)
+        goto failed;
+    https_output = (char*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGNUP(https_output_len),
+                                         PROT_READ, MAP_PRIVATE|MAP_FILE, output_fd, 0);
+    if (IS_ERR_P(https_output))
+        goto failed;
+
+    // Read the HTTPS headers
+    ret = INLINE_SYSCALL(open, 2, https_header_path, O_RDONLY);
+    if (IS_ERR(ret))
+        goto failed;
+    INLINE_SYSCALL(close, 1, header_fd);
+    header_fd = ret;
+    https_header_len = INLINE_SYSCALL(lseek, 3, header_fd, 0, SEEK_END);
+    if (IS_ERR(https_header_len) || !https_header_len)
+        goto failed;
+    https_header = (char*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGNUP(https_header_len),
+                                         PROT_READ, MAP_PRIVATE|MAP_FILE, header_fd, 0);
+    if (IS_ERR_P(https_header))
+        goto failed;
+
+    // Parse the HTTPS headers
+    size_t   ias_sig_len     = 0;
+    uint8_t* ias_sig         = NULL;
+    size_t   ias_certs_len   = 0;
+    char*    ias_certs       = NULL;
+    char*    start       = https_header;
+    char*    end         = strchr(https_header, '\n');
+    while (end) {
+        char* next_start = end + 1;
+        // If the eol (\n) is preceded by a return (\r), move the end pointer.
+        if (end > start + 1 && *(end - 1) == '\r')
+            end--;
+
+        if (strpartcmp_static(start, "X-IASReport-Signature: ")) {
+            start += static_strlen("X-IASReport-Signature: ");
+
+            // Decode IAS report signature
+            ret = lib_Base64Decode(start, end - start, NULL, &ias_sig_len);
+            if (ret < 0) {
+                SGX_DBG(DBG_E, "Malformed IAS signature\n");
+                goto failed;
+            }
+
+            ias_sig = (uint8_t*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGNUP(ias_sig_len),
+                                               PROT_READ|PROT_WRITE,
+                                               MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+            if (IS_ERR_P(ias_sig)) {
+                SGX_DBG(DBG_E, "Cannot allocate memory for IAS report signature\n");
+                goto failed;
+            }
+
+            ret = lib_Base64Decode(start, end - start, ias_sig, &ias_sig_len);
+            if (ret < 0) {
+                SGX_DBG(DBG_E, "Malformed IAS report signature\n");
+                goto failed;
+            }
+        } else if (strpartcmp_static(start, "X-IASReport-Signing-Certificate: ")) {
+            start += static_strlen("X-IASReport-Signing-Certificate: ");
+
+            // Decode IAS signature chain
+            ias_certs_len = end - start;
+            ias_certs = (char*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGNUP(ias_certs_len),
+                                              PROT_READ|PROT_WRITE,
+                                              MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+            if (IS_ERR_P(ias_certs)) {
+                SGX_DBG(DBG_E, "Cannot allocate memory for IAS certificate chain\n");
+                goto failed;
+            }
+
+            /*
+             * The value of x-iasreport-signing-certificate is a certificate chain which
+             * consists of multiple certificates represented in the PEM format. The value
+             * is escaped using the % character. For example, a %20 in the certificate
+             * needs to be replaced as the newline ("\n"). The following logic iteratively
+             * reads the character and converts the escaped characters into the buffer.
+             */
+            size_t total_bytes = 0;
+            // Convert escaped characters
+            for (size_t i = 0; i < ias_certs_len; i++) {
+                if (start[i] == '%') {
+                    int8_t hex1 = hex2dec(start[i + 1]), hex2 = hex2dec(start[i + 2]);
+                    if (hex1 < 0 || hex2 < 0)
+                        goto failed;
+
+                    char c = hex1 * 16 + hex2;
+                    if (c != '\n') ias_certs[total_bytes++] = c;
+                    i += 2;
+                } else {
+                    ias_certs[total_bytes++] = start[i];
+                }
+            }
+
+            // Adjust certificate chain length
+            ias_certs[total_bytes++] = '\0';
+            if (ALLOC_ALIGNUP(total_bytes) < ALLOC_ALIGNUP(ias_certs_len))
+                INLINE_SYSCALL(munmap, 2, ALLOC_ALIGNUP(total_bytes),
+                               ALLOC_ALIGNUP(ias_certs_len) - ALLOC_ALIGNUP(total_bytes));
+            ias_certs_len = total_bytes;
+        }
+
+        start = next_start;
+        end   = strchr(start, '\n');
+    }
+
+    if (!ias_sig) {
+        SGX_DBG(DBG_E, "IAS returned invalid headers: no report signature\n");
+        goto failed;
+    }
+
+    if (!ias_certs) {
+        SGX_DBG(DBG_E, "IAS returned invalid headers: no certificate chain\n");
+        goto failed;
+    }
+
+    // Now return the attestation data, including the IAS response, signature, and the
+    // certificate chain back to the caller.
+    attestation->ias_report     = https_output;
+    attestation->ias_report_len = https_output_len;
+    attestation->ias_sig        = ias_sig;
+    attestation->ias_sig_len    = ias_sig_len;
+    attestation->ias_certs      = ias_certs;
+    attestation->ias_certs_len  = ias_certs_len;
+    https_output = NULL; // Don't free the HTTPS output
+    ret = 0;
+done:
+    if (https_header)
+        INLINE_SYSCALL(munmap, 2, https_header, ALLOC_ALIGNUP(https_header_len));
+    if (https_output)
+        INLINE_SYSCALL(munmap, 2, https_output, ALLOC_ALIGNUP(https_output_len));
+    if (pipefds[0] != -1) INLINE_SYSCALL(close, 1, pipefds[0]);
+    if (pipefds[1] != -1) INLINE_SYSCALL(close, 1, pipefds[1]);
+    if (header_fd != -1) {
+        INLINE_SYSCALL(close,  1, header_fd);
+        INLINE_SYSCALL(unlink, 1, https_header_path);
+    }
+    if (output_fd != -1) {
+        INLINE_SYSCALL(close,  1, output_fd);
+        INLINE_SYSCALL(unlink, 1, https_output_path);
+    }
+    return ret;
+failed:
+    ret = -PAL_ERROR_DENIED;
+    goto done;
+}
+
+/*
+ * This wrapper function performs the whole attestation procedure outside the enclave (except
+ * retrieving the local remote attestation and verification). The function first contacts
+ * the AESM service to retrieve a quote of the platform and the report of the quoting enclave.
+ * Then, the function submits the quote to the IAS through a HTTPS client (CURL) to exchange
+ * for a remote attestation report signed by a Intel-approved certificate chain. Finally, the
+ * function returns the QE report, the quote, and the response from the IAS back to the enclave
+ * for verification.
+ *
+ * @spid:        The client SPID registered with IAS.
+ * @subkey:      SPID subscription key.
+ * @linkable:    A boolean that represents whether the SPID is linkable.
+ * @report:      The local report of the target enclave.
+ * @nonce:       A 16-byte nonce randomly generated inside the enclave.
+ * @attestation: A structure for storing the response from the AESM service and the IAS.
+ */
+int retrieve_verified_quote(const sgx_spid_t* spid, const char* subkey, bool linkable,
+                            const sgx_arch_report_t* report, const sgx_quote_nonce_t* nonce,
+                            sgx_attestation_t* attestation) {
+
+    int ret = connect_aesm_service();
+    if (ret < 0)
+        return ret;
+
+    Request req = REQUEST__INIT;
+    Request__GetQuoteRequest getreq = REQUEST__GET_QUOTE_REQUEST__INIT;
+    getreq.report.data   = (uint8_t*) report;
+    getreq.report.len    = SGX_REPORT_ACTUAL_SIZE;
+    getreq.quote_type    = linkable ? SGX_LINKABLE_SIGNATURE : SGX_UNLINKABLE_SIGNATURE;
+    getreq.spid.data     = (uint8_t*) spid;
+    getreq.spid.len      = sizeof(*spid);
+    getreq.has_nonce     = true;
+    getreq.nonce.data    = (uint8_t*) nonce;
+    getreq.nonce.len     = sizeof(*nonce);
+    getreq.buf_size      = SGX_QUOTE_MAX_SIZE;
+    getreq.has_qe_report = true;
+    getreq.qe_report     = true;
+    req.getquotereq      = &getreq;
+
+    Response* res = NULL;
+    ret = request_aesm_service(&req, &res);
+    if (ret < 0)
+        return ret;
+
+    if (!res->getquoteres) {
+        SGX_DBG(DBG_E, "aesm_service returned wrong message\n");
+        goto failed;
+    }
+
+    Response__GetQuoteResponse* r = res->getquoteres;
+    if (r->errorcode != 0) {
+        SGX_DBG(DBG_E, "aesm_service returned error: %d\n", r->errorcode);
+        goto failed;
+    }
+
+    if (!r->has_quote     || r->quote.len < sizeof(sgx_quote_t) ||
+        !r->has_qe_report || r->qe_report.len != SGX_REPORT_ACTUAL_SIZE) {
+        SGX_DBG(DBG_E, "aesm_service returned invalid quote or report\n");
+        goto failed;
+    }
+
+    sgx_quote_t* quote = (sgx_quote_t*) INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGNUP(r->quote.len),
+                                                       PROT_READ|PROT_WRITE,
+                                                       MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+    if (IS_ERR_P(quote)) {
+        SGX_DBG(DBG_E, "Failed to allocate memory for the quote\n");
+        goto failed;
+    }
+
+    memcpy(quote, r->quote.data, r->quote.len);
+    attestation->quote = quote;
+    attestation->quote_len = r->quote.len;
+
+    ret = contact_intel_attest_service(subkey, nonce, (sgx_quote_t *) quote, attestation);
+    if (ret < 0) {
+        INLINE_SYSCALL(munmap, 2, quote, ALLOC_ALIGNUP(r->quote.len));
+        goto failed;
+    }
+
+    memcpy(&attestation->qe_report, r->qe_report.data, sizeof(sgx_arch_report_t));
+    response__free_unpacked(res, NULL);
+    return 0;
+
+failed:
+    response__free_unpacked(res, NULL);
+    return -PAL_ERROR_DENIED;
+}

+ 22 - 0
Pal/src/host/Linux-SGX/signer/pal-sgx-sign

@@ -818,6 +818,28 @@ if __name__ == "__main__":
     print >>sys.stderr, "    xfrms:     %016x" % (bytes_to_int(attr['xfrms']))
     print >>sys.stderr, "    miscs:     %08x"  % (bytes_to_int(attr['miscs']))
 
+    # Check client info for remote attestation. Skip and warn if sgx.ra_client.spid is not provided.
+    print >>sys.stderr, "Attestation:"
+    if 'sgx.ra_client_spid' in manifest and manifest['sgx.ra_client_spid']:
+        print >>sys.stderr, "    spid:     " + manifest['sgx.ra_client_spid']
+        need_client_info = False
+        if 'sgx.ra_client_key' not in manifest or not manifest['sgx.ra_client_key']:
+            print >>sys.stderr, "    *** sgx.ra_client_key not specified ***"
+            need_client_info = True
+        else:
+            print >>sys.stderr, "    key:   " + manifest['sgx.ra_client_key']
+        if 'sgx.ra_client_linkable' in manifest:
+            print >>sys.stderr, "    linkable: " + manifest['sgx.ra_client_linkable']
+        else:
+            print >>sys.stderr, "    linkable: 0"
+        if need_client_info: sys.exit(-1)
+
+    else:
+        print >>sys.stderr, "    *** Client info is not specified. Graphene" + \
+            " will not perform remote attestation before execution." + \
+            " Please provide sgx.ra_client_spid and sgx.ra_client_key in the manifest. ***"
+
+
     # Get trusted checksums and measurements
     print >>sys.stderr, "Trusted files:"
     for key, val in get_trusted_files(manifest, args).items():

+ 4 - 0
Pal/src/pal.h

@@ -208,6 +208,10 @@ typedef struct {
     /* Memory information (only required ones) */
     PAL_MEM_INFO mem_info;
 
+    /* Attestation information */
+    PAL_STR attestation_status;
+    PAL_STR attestation_timestamp;
+
     /* Purely for profiling */
     PAL_NUM startup_time;
     PAL_NUM host_specific_startup_time;

+ 41 - 1
README.rst

@@ -61,7 +61,7 @@ Run the following command on Ubuntu to install dependencies for Graphene::
 
 For building Graphene for SGX, run the following command in addition::
 
-    sudo apt-get install -y python-protobuf
+    sudo apt-get install -y python-protobuf libprotobuf-c-dev protobuf-c-compiler
 
 To run unit tests locally, you also need the python3-pytest package::
 
@@ -188,6 +188,46 @@ of the source tree::
 
    git submodule update --init -- LibOS/shim/test/apps/
 
+Testing the remote attestation feature
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To enable tests for the built-in remote attestation feature for Graphene-SGX, obtain a SPID
+and a subscription key (can be linkable or unlinkable) from the Intel API Portal:
+https://api.portal.trustedservices.intel.com/EPID-attestation
+
+Specify the SPID, subscription key, and the type of the SPID/key in the manifest:
+
+    sgx.ra_client_spid = <SPID>
+    sgx.ra_client_key = <KEY>
+    sgx.ra_client_linkable = 1 # or 0 if the SPID/key is unlinkable (default)
+
+If the remote attestation feature is enabled, Graphene-SGX will terminate if the platform
+is not successfully verified by the Intel Attestation Service (IAS). The feature ensures that
+Graphene-SGX only executes on a genuine, up-to-date SGX hardware.
+
+
+To enable remote attestation tests in ``Pal/regression``, specify the following variables:
+
+    cd PAL/regression
+    make SGX=1 RA_CLIENT_SPID=<SPID> RA_CLIENT_KEY=<KEY>
+    make SGX_RUN=1
+
+
+If you receive a "GROUP_OUT_OF_DATE" status from IAS, this status indicates that your CPU
+is out of date and can be vulnerable to hardware attacks. If you wish to bypass this error,
+you can specify the following option in the manifest:
+
+    sgx.ra_accept_group_out_of_date = 1
+
+SECURITY ADVISORIES:
+
+"GROUP_OUT_OF_DATE" may indicate that the firmware (microcode) of you CPU is not updated
+according to INTEL-SA-00233 (Load/store data sampling) and INTEL-SA-00161 (L1 terminal fault).
+It is recommended that you keep the BIOS of your platform up-to-date.
+
+If you receive status "CONFIGURATION_NEEDED" from the IAS after updating your BIOS, you may
+need to disable hyperthreading in your BIOS to mitigate L1 terminal fault.
+
 How to run an application in Graphene?
 ======================================