/**
* \file CryptoPlatform.c
* \brief Platform independent high level crypto functions.
* \author Dmytro Podgornyi (d.podgornyi@samsung.com)
* \version 0.1
* \date Created May 28, 2013
* \par In Samsung Ukraine R&D Center (SURC) under a contract between
* \par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
* \par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
* \par Copyright: (c) Samsung Electronics Co, Ltd 2012. All rights reserved.
**/

#include <stdint.h>
#include <string.h>
#include <time.h>

#include "CryptoPlatform.h"
#include "CommLayerData.h"
#include "ServiceName.h"
#include "da_cert_parcer.h"
#include "da_cert_gencer.h"
#include "asn1rsa.h"
#include "asn1ec.h"
#include "x509v3.h"
#include "rsa/rsa_wrapper.h"
#include "hash/hash_wrapper.h"
#include "TLV.h"
#include "sec_alloc.h"
#include "objects/obj_mac.h"
#include "base64.h"

#ifdef USE_MOBICORE
#include "mobicore_utils.h"
#endif

#include "log.h"


/*
#include "tlStd.h"
#include "TlApi/TlApi.h"
#include "tlRsa_Api.h"
*/

#if defined(USE_QSEE)
#include "SfsFileOperations.h"
#endif

//#define HARDCODED_SD_KEY
uint8_t cert_CCC[] =
{
#ifdef HARDCODED_SD_KEY
	0x30, 0x82, 0x02, 0xef, 0x30, 0x82, 0x01, 0xd7, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
	0x90, 0x1b, 0xbb, 0x41, 0x4c, 0xc8, 0xba, 0x7e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
	0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x0e, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55,
	0x04, 0x03, 0x0c, 0x03, 0x43, 0x43, 0x43, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32,
	0x36, 0x30, 0x39, 0x33, 0x37, 0x32, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x33, 0x32, 0x38,
	0x30, 0x39, 0x33, 0x37, 0x32, 0x30, 0x5a, 0x30, 0x0e, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55,
	0x04, 0x03, 0x0c, 0x03, 0x43, 0x43, 0x43, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
	0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
	0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xf8, 0x53, 0xf0, 0x78, 0xab, 0xfc, 0xbd, 0x80,
	0x37, 0x45, 0xd3, 0xa5, 0xe3, 0xc7, 0x23, 0x39, 0x0f, 0x49, 0x66, 0x81, 0xc5, 0xdd, 0x98, 0x24,
	0xd6, 0x9c, 0x16, 0xd7, 0xa8, 0x43, 0xac, 0xdd, 0xe7, 0x71, 0x50, 0xd0, 0xbd, 0x44, 0x0e, 0xc2,
	0x3e, 0x2c, 0x3e, 0xbb, 0x78, 0x92, 0xe6, 0x1b, 0x6f, 0x95, 0xd1, 0x5f, 0x3a, 0x10, 0xa2, 0x79,
	0x65, 0x69, 0x03, 0xca, 0x1e, 0xa6, 0x59, 0x2c, 0x46, 0x93, 0xdb, 0x3c, 0x8c, 0x3f, 0x5d, 0x6c,
	0xa1, 0xf2, 0x4f, 0x99, 0x03, 0x90, 0x91, 0xa7, 0x26, 0x6b, 0xd9, 0xe3, 0x14, 0xfd, 0x92, 0x16,
	0x8e, 0xb4, 0x40, 0xa2, 0x25, 0xb1, 0x7a, 0xbf, 0xb7, 0xcd, 0x41, 0x73, 0xb1, 0xac, 0x52, 0x93,
	0x10, 0x5b, 0x9c, 0x3f, 0x26, 0xec, 0x2e, 0x75, 0x61, 0x3e, 0x61, 0xd0, 0x38, 0x6d, 0xd3, 0xdc,
	0xe0, 0xed, 0xbc, 0xab, 0xd7, 0x65, 0x0f, 0x42, 0x6d, 0xf6, 0x06, 0x5c, 0x1d, 0x1f, 0x33, 0xde,
	0xa5, 0x7e, 0xcc, 0xb4, 0x50, 0x31, 0x41, 0x74, 0x84, 0xf2, 0x23, 0xcd, 0x5f, 0x34, 0x0e, 0x51,
	0x6c, 0x5a, 0xd6, 0xf3, 0x75, 0xb5, 0x03, 0x73, 0x90, 0x30, 0x46, 0x07, 0x3b, 0x24, 0xf5, 0xe5,
	0xd4, 0xe8, 0x65, 0xf1, 0xe5, 0x42, 0x18, 0xf1, 0x72, 0x0a, 0xe3, 0x45, 0xdf, 0xbf, 0x64, 0x87,
	0x30, 0x66, 0x82, 0x85, 0x78, 0xf0, 0x24, 0x0e, 0x5c, 0x5e, 0x6b, 0xe7, 0x43, 0x45, 0xde, 0x9a,
	0x69, 0x35, 0x54, 0x90, 0x4a, 0x25, 0x73, 0x69, 0x8f, 0xbf, 0xea, 0x0b, 0x3f, 0xc7, 0xea, 0x33,
	0xae, 0x28, 0x06, 0xb6, 0xde, 0x2c, 0xc8, 0x6b, 0xa3, 0x62, 0x0d, 0xdd, 0x5a, 0x38, 0xa0, 0x52,
	0xcf, 0x09, 0xb7, 0x03, 0xdc, 0x84, 0x5f, 0x7f, 0xd8, 0xd1, 0x06, 0xe7, 0x03, 0x52, 0x0d, 0x16,
	0x03, 0x66, 0x9b, 0x5a, 0x81, 0xd2, 0x08, 0xbf, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30,
	0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xeb, 0x7b, 0x02, 0x12,
	0xaf, 0xe7, 0x20, 0x2f, 0x6f, 0x7f, 0x85, 0xd2, 0x5d, 0x4f, 0x0b, 0xf8, 0xd3, 0x4b, 0x9e, 0xcc,
	0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xeb, 0x7b, 0x02,
	0x12, 0xaf, 0xe7, 0x20, 0x2f, 0x6f, 0x7f, 0x85, 0xd2, 0x5d, 0x4f, 0x0b, 0xf8, 0xd3, 0x4b, 0x9e,
	0xcc, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
	0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
	0x01, 0x01, 0x00, 0x6e, 0x53, 0x30, 0xdd, 0x23, 0x75, 0x1e, 0x61, 0x88, 0x03, 0x70, 0x85, 0xa9,
	0xc7, 0xf3, 0xa5, 0xd4, 0xec, 0x0f, 0x9d, 0xea, 0x05, 0x37, 0xd5, 0x72, 0xc2, 0x91, 0x93, 0x17,
	0xea, 0x63, 0xae, 0x1b, 0x02, 0x42, 0x8e, 0x50, 0xdb, 0x71, 0x68, 0x99, 0x01, 0xb9, 0xa3, 0x66,
	0x48, 0x28, 0x26, 0xe0, 0x0a, 0xe4, 0x1e, 0x01, 0xc8, 0x13, 0xb4, 0x1d, 0x71, 0xad, 0x1b, 0x7e,
	0xf8, 0x04, 0xad, 0x1a, 0xd0, 0xdc, 0x77, 0x3a, 0xd2, 0x61, 0xba, 0x4b, 0xb1, 0x7b, 0x9a, 0xe0,
	0xb2, 0x9c, 0xf5, 0x46, 0xca, 0x85, 0x62, 0x98, 0xe1, 0xc6, 0xd4, 0x3e, 0x5c, 0xca, 0xef, 0x0e,
	0xb4, 0x0b, 0x96, 0xcb, 0x6f, 0x32, 0x9b, 0xda, 0xd4, 0x67, 0x5b, 0xe8, 0xb0, 0xc8, 0x62, 0xa0,
	0xa5, 0x8a, 0x62, 0x08, 0x2f, 0xd4, 0x4a, 0xb5, 0x41, 0x75, 0xa2, 0x91, 0xa5, 0x2e, 0xa1, 0xc4,
	0x6e, 0xe0, 0xdd, 0xcd, 0xba, 0x05, 0x1c, 0x6d, 0x5b, 0x82, 0xe3, 0xac, 0x74, 0xaa, 0xb2, 0xea,
	0x33, 0xcf, 0xf1, 0x69, 0x77, 0x62, 0x12, 0xa3, 0x96, 0xa7, 0xe4, 0xfd, 0xa0, 0x9c, 0x4d, 0xdd,
	0x3b, 0x04, 0xb8, 0x91, 0x95, 0xf7, 0x28, 0x59, 0xdd, 0x36, 0x86, 0x19, 0x6d, 0xed, 0xfa, 0xb0,
	0x3e, 0x88, 0x21, 0xcc, 0x55, 0xe2, 0x05, 0xc4, 0xa9, 0xd0, 0x9f, 0xd2, 0xd8, 0x5a, 0x95, 0x43,
	0xe3, 0x25, 0xf6, 0xf6, 0xa1, 0x7b, 0xa4, 0x3a, 0xc2, 0x35, 0x07, 0x05, 0xef, 0xd0, 0x5d, 0xfa,
	0x92, 0x53, 0x1e, 0x37, 0xf2, 0x0e, 0x9e, 0x52, 0xdb, 0xed, 0x28, 0x42, 0xed, 0x5d, 0xfe, 0xd8,
	0xf0, 0xd7, 0x0d, 0x3e, 0x87, 0xe3, 0x9c, 0xbd, 0x31, 0x83, 0xd1, 0xcb, 0x39, 0x21, 0xad, 0x7b,
	0xf5, 0xdf, 0xb4, 0xf3, 0x4d, 0x04, 0xd3, 0x7f, 0x9b, 0x36, 0xd4, 0xcd, 0x06, 0x7e, 0xbc, 0x25,
	0x81, 0x00, 0xbc
#else
	0x30, 0x82, 0x05, 0xa7, 0x30, 0x82, 0x03, 0x8f, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
	0xe3, 0xee, 0xb1, 0x5c, 0x85, 0x7b, 0x63, 0xb6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
	0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00, 0x30, 0x3c, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55,
	0x04, 0x03, 0x13, 0x0b, 0x43, 0x43, 0x43, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31,
	0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x43, 0x61, 0x72, 0x20, 0x43, 0x6f,
	0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x6f,
	0x72, 0x74, 0x69, 0x75, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x31, 0x30, 0x32, 0x33, 0x31,
	0x30, 0x32, 0x36, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33, 0x32, 0x31, 0x30, 0x32, 0x33, 0x31, 0x30,
	0x32, 0x36, 0x34, 0x34, 0x5a, 0x30, 0x3c, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03,
	0x13, 0x0b, 0x43, 0x43, 0x43, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x24, 0x30,
	0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x43, 0x61, 0x72, 0x20, 0x43, 0x6f, 0x6e, 0x6e,
	0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x72, 0x74,
	0x69, 0x75, 0x6d, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
	0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02,
	0x82, 0x02, 0x01, 0x00, 0xc5, 0xdd, 0xe3, 0x72, 0x22, 0x2e, 0x5a, 0x85, 0x93, 0x2a, 0x2d, 0x97,
	0xcb, 0x57, 0xae, 0x9b, 0x5a, 0x9e, 0xc8, 0x6d, 0xe4, 0xbd, 0xdb, 0x44, 0xcd, 0x58, 0x99, 0x0e,
	0x08, 0x67, 0x03, 0xe4, 0xf1, 0x7a, 0xe5, 0x79, 0xd1, 0x85, 0x6d, 0x3a, 0x07, 0x2f, 0xe2, 0xb4,
	0xbf, 0xcb, 0x84, 0xb2, 0x25, 0x7b, 0xa9, 0x85, 0x4b, 0x75, 0x45, 0x82, 0xcd, 0x70, 0xca, 0xde,
	0xe6, 0xb6, 0x60, 0xce, 0x6b, 0xf3, 0xc5, 0xf5, 0x18, 0x24, 0x29, 0xd7, 0xbb, 0xba, 0x57, 0x22,
	0x39, 0x39, 0x86, 0x2e, 0xe6, 0x1a, 0x89, 0x03, 0xc6, 0x2f, 0x3f, 0x3f, 0x61, 0x2a, 0xed, 0xf4,
	0xe7, 0xdc, 0xf9, 0xfc, 0xfc, 0x69, 0x57, 0xac, 0xc1, 0xab, 0xd5, 0xdb, 0x80, 0x5e, 0x0e, 0x6a,
	0x72, 0xc1, 0x84, 0x58, 0x9c, 0x4c, 0x54, 0xeb, 0x80, 0x24, 0x52, 0xe8, 0xe3, 0x75, 0xfa, 0x4f,
	0x53, 0x43, 0xa7, 0x65, 0xf7, 0x39, 0x5a, 0xad, 0x42, 0xef, 0x87, 0x8b, 0xac, 0x08, 0x04, 0x8a,
	0x82, 0xee, 0x18, 0x7f, 0x2d, 0x7c, 0xc5, 0xed, 0x0e, 0xc1, 0x34, 0x83, 0x3b, 0x4d, 0x18, 0xde,
	0xb3, 0x59, 0xa8, 0x05, 0xd4, 0xc3, 0x04, 0x41, 0xef, 0x23, 0xdf, 0x22, 0x6a, 0xbc, 0x95, 0x2c,
	0x4e, 0xb5, 0x38, 0xc3, 0x4d, 0x11, 0xb9, 0xf5, 0x94, 0x2f, 0x40, 0x89, 0x63, 0xe2, 0xfc, 0xc6,
	0x8a, 0xb3, 0x30, 0x53, 0x1d, 0x5c, 0x31, 0x72, 0x08, 0x8e, 0xfb, 0x10, 0x9f, 0x3a, 0xb1, 0x3e,
	0xe5, 0x22, 0x97, 0x90, 0xfe, 0xda, 0xef, 0xa5, 0xce, 0x9c, 0xcb, 0x30, 0x29, 0x90, 0x43, 0xf1,
	0x90, 0xa8, 0xdc, 0xc5, 0xa0, 0xc0, 0xf0, 0xbd, 0x70, 0x65, 0xb3, 0x93, 0x87, 0x7b, 0x89, 0x75,
	0xb0, 0x7d, 0x2d, 0x4c, 0xc6, 0x36, 0x1a, 0xe2, 0x85, 0x61, 0xe9, 0x4f, 0x75, 0x19, 0xec, 0x1c,
	0x8d, 0x76, 0x3c, 0xe9, 0xd7, 0x46, 0x81, 0xb0, 0x55, 0xc1, 0xb8, 0x79, 0xfb, 0x56, 0xd1, 0x97,
	0xd2, 0x4d, 0xb1, 0xeb, 0x85, 0x14, 0x61, 0x2b, 0x2c, 0xca, 0xaf, 0xe9, 0x97, 0x93, 0x35, 0x08,
	0x3a, 0x5b, 0x0f, 0x3a, 0x9c, 0x69, 0x25, 0xc1, 0xe6, 0xfb, 0x2c, 0xf9, 0x24, 0xca, 0x7a, 0x90,
	0x40, 0x30, 0xbb, 0xcc, 0x20, 0x93, 0x82, 0x25, 0x69, 0x60, 0x2b, 0x2f, 0xc8, 0x0a, 0x53, 0x19,
	0x2c, 0x3e, 0x9a, 0x18, 0x2e, 0x64, 0x42, 0xc8, 0x22, 0x4d, 0x58, 0xde, 0xc8, 0xfc, 0xcb, 0x80,
	0x39, 0x6e, 0x08, 0xd3, 0xf7, 0xf0, 0xce, 0x0b, 0xb3, 0x63, 0xde, 0xe7, 0x92, 0x51, 0xea, 0x2f,
	0x1d, 0x09, 0x23, 0x69, 0x7d, 0x9d, 0x87, 0x5d, 0xc0, 0xec, 0xbd, 0x15, 0xfa, 0xfb, 0x94, 0xd6,
	0x7a, 0x02, 0x7a, 0xc6, 0x5e, 0x7e, 0x1d, 0xd1, 0x6d, 0x1f, 0x06, 0x67, 0x0d, 0x27, 0x95, 0xfe,
	0xba, 0x98, 0x23, 0x33, 0x57, 0x15, 0x6d, 0x13, 0x02, 0xdb, 0xfa, 0x58, 0x16, 0x9b, 0xe6, 0x4b,
	0x7d, 0x8f, 0x79, 0x48, 0x13, 0xaf, 0x9c, 0x0f, 0x73, 0x79, 0x59, 0xc7, 0xdc, 0x17, 0x70, 0xed,
	0xb7, 0xa6, 0xa0, 0xb7, 0xa0, 0x3d, 0x37, 0xf1, 0xb8, 0x6e, 0x24, 0xdf, 0x49, 0x02, 0x9f, 0xf6,
	0xba, 0x37, 0x5e, 0x0d, 0x81, 0xba, 0x12, 0x2a, 0x37, 0xce, 0x0c, 0x0d, 0x20, 0xa7, 0x32, 0xda,
	0x12, 0x9a, 0x09, 0x62, 0x16, 0xbb, 0x7f, 0x93, 0x83, 0xa5, 0xf3, 0xac, 0x9c, 0x64, 0x8b, 0x9c,
	0xf0, 0xda, 0x13, 0xd1, 0x32, 0x91, 0x73, 0xa3, 0x52, 0x13, 0x89, 0xe3, 0x47, 0x3a, 0x35, 0xce,
	0x1c, 0xe5, 0xab, 0x73, 0x6e, 0x45, 0x14, 0xe5, 0xf1, 0xc2, 0xa2, 0x0d, 0x98, 0x13, 0xf2, 0x3d,
	0xb9, 0xf6, 0xfc, 0x6a, 0x8c, 0xc7, 0x8a, 0x9e, 0xd9, 0xcc, 0x19, 0xac, 0xd6, 0x73, 0xd5, 0x57,
	0x40, 0x5c, 0xd2, 0x17, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xab, 0x30, 0x81, 0xa8, 0x30,
	0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x52, 0x7c, 0x16, 0x40, 0x94, 0x8a,
	0xe4, 0xd7, 0xba, 0x01, 0x24, 0x72, 0xab, 0x1e, 0x95, 0xe3, 0x1a, 0x12, 0x0c, 0xc3, 0x30, 0x6c,
	0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x65, 0x30, 0x63, 0x80, 0x14, 0x52, 0x7c, 0x16, 0x40, 0x94,
	0x8a, 0xe4, 0xd7, 0xba, 0x01, 0x24, 0x72, 0xab, 0x1e, 0x95, 0xe3, 0x1a, 0x12, 0x0c, 0xc3, 0xa1,
	0x40, 0xa4, 0x3e, 0x30, 0x3c, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0b,
	0x43, 0x43, 0x43, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x24, 0x30, 0x22, 0x06,
	0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x43, 0x61, 0x72, 0x20, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
	0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x72, 0x74, 0x69, 0x75,
	0x6d, 0x82, 0x09, 0x00, 0xe3, 0xee, 0xb1, 0x5c, 0x85, 0x7b, 0x63, 0xb6, 0x30, 0x0c, 0x06, 0x03,
	0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
	0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
	0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x0c, 0x3b, 0x32, 0x91, 0xbb,
	0x63, 0xb6, 0x2c, 0xd6, 0xa7, 0x40, 0x47, 0x24, 0x7e, 0x72, 0xa9, 0x1c, 0x1f, 0xdd, 0x18, 0x38,
	0x34, 0xc9, 0x35, 0x07, 0x7d, 0xad, 0x17, 0x88, 0xff, 0xdf, 0xd5, 0x42, 0x00, 0x4f, 0x49, 0xd0,
	0x8f, 0x7d, 0xc0, 0x12, 0x9b, 0x2d, 0xee, 0xaf, 0x1d, 0x4e, 0xf1, 0xc3, 0xcd, 0x70, 0xae, 0x4c,
	0xe3, 0x33, 0x7c, 0xa0, 0xfc, 0x45, 0x70, 0x57, 0x98, 0x09, 0xfd, 0xbf, 0x7d, 0x24, 0x98, 0xfd,
	0x2d, 0xc3, 0xc5, 0x13, 0x2f, 0x7e, 0x45, 0x23, 0xff, 0xfd, 0xd1, 0xcf, 0x25, 0x1c, 0xd6, 0x1c,
	0xe6, 0x86, 0xf3, 0x43, 0xc3, 0xae, 0xd4, 0xcb, 0x40, 0xbf, 0x9f, 0x6c, 0x52, 0x71, 0xdd, 0x82,
	0x7f, 0xe9, 0x13, 0x22, 0x2f, 0xf2, 0x90, 0xab, 0x0d, 0x6f, 0x14, 0x82, 0xdb, 0xd1, 0x31, 0xa5,
	0x27, 0x83, 0x86, 0xb1, 0x25, 0x97, 0xa0, 0xde, 0xfd, 0x09, 0xa9, 0xc6, 0xaa, 0xf8, 0xbf, 0xff,
	0x86, 0x1c, 0xd6, 0x0c, 0x8d, 0xe3, 0x27, 0x5b, 0x93, 0x71, 0x09, 0x38, 0x07, 0xd7, 0x92, 0x02,
	0x9b, 0x21, 0xac, 0x6d, 0x57, 0xd3, 0xa5, 0xf3, 0x90, 0xe4, 0x02, 0x93, 0xdb, 0x76, 0x37, 0x20,
	0x56, 0x28, 0xb0, 0x28, 0x79, 0x7e, 0x53, 0xc8, 0xa2, 0x3c, 0x90, 0xcc, 0x1d, 0x84, 0xee, 0x85,
	0xc5, 0x2f, 0xd3, 0x5c, 0xb5, 0xe4, 0xba, 0x14, 0x35, 0xd9, 0x45, 0x22, 0xb3, 0x52, 0x4d, 0xe2,
	0xae, 0x96, 0x56, 0x7e, 0xa1, 0xb7, 0x4a, 0xae, 0xed, 0xe5, 0x7f, 0xf2, 0xb2, 0x95, 0xa3, 0x41,
	0x87, 0xe9, 0x01, 0x20, 0x51, 0x03, 0xa0, 0xe6, 0xbb, 0xc8, 0x5d, 0x6b, 0xcb, 0xbf, 0xb2, 0x4d,
	0x4f, 0x15, 0x83, 0x53, 0x57, 0x35, 0x3e, 0x7d, 0xa1, 0xc6, 0x5e, 0xe1, 0xe2, 0xad, 0x25, 0xf9,
	0xf1, 0x45, 0xce, 0xe6, 0x33, 0x89, 0x38, 0x60, 0x9f, 0xd2, 0xc9, 0x1e, 0x38, 0x19, 0x31, 0x82,
	0x6e, 0x20, 0x20, 0xc4, 0x5f, 0xe0, 0xc9, 0x4b, 0xff, 0xbc, 0xbd, 0xf0, 0xf0, 0x49, 0x23, 0x4e,
	0xd5, 0x6b, 0x2d, 0x83, 0xc0, 0xbd, 0xa2, 0xf2, 0x21, 0xf0, 0x5a, 0xa5, 0x02, 0x05, 0xec, 0x07,
	0x10, 0x3f, 0x06, 0x76, 0x2f, 0x4b, 0xc1, 0x04, 0xfa, 0xae, 0xf0, 0xe8, 0x7a, 0x81, 0x78, 0x6b,
	0xdd, 0xaf, 0x89, 0x30, 0x33, 0xf4, 0x71, 0xaa, 0x72, 0x1d, 0xb8, 0x08, 0x5d, 0x59, 0x36, 0x69,
	0x9a, 0x3a, 0xba, 0x10, 0x11, 0xc9, 0x5a, 0xd8, 0xac, 0x63, 0x10, 0xe4, 0xf8, 0x49, 0x12, 0xa4,
	0x7b, 0xa2, 0xd7, 0x62, 0xba, 0x63, 0x48, 0x3f, 0xfe, 0x19, 0xeb, 0x40, 0x39, 0x86, 0xe0, 0x99,
	0x79, 0x63, 0xb0, 0x4d, 0x60, 0x7b, 0x3a, 0x91, 0xff, 0x73, 0x6e, 0xa2, 0x25, 0x67, 0x26, 0x15,
	0x73, 0xc1, 0xba, 0x3b, 0x45, 0x93, 0xf6, 0x2b, 0xbc, 0x8e, 0x79, 0xf2, 0x04, 0x95, 0x8f, 0x47,
	0x7e, 0xb2, 0xa1, 0xb5, 0x58, 0xc7, 0x59, 0x11, 0x83, 0x3d, 0x2b, 0xe4, 0x25, 0x4c, 0x50, 0xae,
	0x6c, 0x22, 0x60, 0x70, 0x1d, 0xaa, 0xaa, 0xf6, 0x13, 0x82, 0x02, 0x79, 0x9c, 0xd5, 0x80, 0xd8,
	0x58, 0xea, 0x19, 0x0a, 0x4e, 0xa7, 0xe6, 0x8f, 0x94, 0x10, 0xb1, 0x39, 0xdc, 0x01, 0xd6, 0xa0,
	0xcc, 0x68, 0x3b, 0x63, 0xe6, 0x99, 0x6c, 0x49, 0xc9, 0x74, 0x76, 0x7b, 0x8b, 0x6a, 0x83, 0x16,
	0x1e, 0xe8, 0xce, 0xa9, 0x6b, 0x9e, 0xcb, 0x86, 0x68, 0xea, 0x6c, 0xdb, 0x35, 0x32, 0x1d, 0x6c,
	0xc6, 0x96, 0x67, 0x65, 0x04, 0x0b, 0x02, 0xe2, 0x9b, 0x99, 0x49, 0x8b, 0x7a, 0x4c, 0x23, 0x75,
	0xbc, 0xfd, 0x1e, 0x54, 0x96, 0x37, 0xc3, 0xf8, 0xaf, 0x83, 0xac, 0x94, 0x02, 0xc8, 0x8d, 0xb4,
	0xea, 0xb3, 0xf7, 0xf2, 0xdc, 0x31, 0xa3, 0x91, 0x3e, 0xd9, 0xc3
#endif
};

#ifdef HARDCODED_SD_KEY
uint8_t SD_PUB[] = {
	0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
	0x00, 0xf4, 0xff, 0xda, 0x52, 0xd9, 0xff, 0x51,
	0x00, 0x1c, 0x30, 0x65, 0x8e, 0x88, 0x84, 0xd4,
	0xef, 0x2c, 0x1c, 0xee, 0x17, 0xfe, 0xff, 0xd4,
	0x07, 0xd2, 0x2c, 0xdb, 0x6f, 0x62, 0x1e, 0xfd,
	0x61, 0x3b, 0xd6, 0xbd, 0x91, 0xae, 0xf2, 0xea,
	0x40, 0x23, 0x13, 0xcd, 0x73, 0x65, 0xa7, 0x04,
	0xf0, 0x36, 0xbb, 0x9c, 0x25, 0x39, 0xa4, 0x68,
	0xd1, 0x9d, 0x54, 0xda, 0x36, 0xdd, 0xc1, 0x49,
	0xc4, 0xac, 0xdb, 0xc9, 0x8f, 0xdc, 0xbe, 0xbd,
	0x67, 0xe6, 0x1b, 0xe2, 0xee, 0xc2, 0x0b, 0xc3,
	0xf3, 0x94, 0x1d, 0x36, 0xa8, 0x2c, 0x5a, 0xaa,
	0x36, 0x1c, 0xc0, 0x1b, 0x1f, 0x50, 0xd7, 0xb5,
	0x13, 0xf8, 0x8e, 0x3b, 0xe4, 0xe7, 0x58, 0xc9,
	0x76, 0x60, 0x4c, 0x3f, 0xfc, 0x35, 0x0e, 0xd8,
	0x04, 0xdd, 0xc4, 0xce, 0xed, 0x3b, 0xbf, 0xa0,
	0x3c, 0xd5, 0x44, 0x06, 0x23, 0xdd, 0x2b, 0x8c,
	0xa3, 0x7b, 0xe1, 0xc4, 0x47, 0xc3, 0x78, 0x9f,
	0x5e, 0xa6, 0xae, 0x55, 0x64, 0x6a, 0x7f, 0xa9,
	0x0b, 0x1c, 0x91, 0x90, 0x29, 0xdd, 0xd0, 0xff,
	0x93, 0x22, 0x77, 0x16, 0xf6, 0x74, 0x83, 0x7a,
	0xa3, 0x6e, 0xd8, 0xc5, 0x2e, 0xbf, 0xe1, 0x77,
	0x49, 0xcb, 0x22, 0xfc, 0xee, 0xdc, 0x69, 0xb8,
	0x50, 0x29, 0x5a, 0x31, 0x64, 0xc9, 0x09, 0x64,
	0x6b, 0xc1, 0x2b, 0xfc, 0xd3, 0xe4, 0x69, 0x11,
	0x19, 0x43, 0xbe, 0x38, 0x31, 0x51, 0xd6, 0xa6,
	0xc5, 0x46, 0x6d, 0xd0, 0xe7, 0xec, 0x6c, 0xb4,
	0xc2, 0x34, 0x17, 0x35, 0xd2, 0x5c, 0x50, 0x75,
	0x35, 0xd8, 0xb4, 0x03, 0x33, 0xdd, 0x3d, 0x9d,
	0x65, 0x15, 0x8b, 0x0f, 0x90, 0xa3, 0xc3, 0x17,
	0x77, 0x19, 0xe2, 0xac, 0x28, 0x60, 0xf4, 0x8c,
	0x6e, 0xe1, 0xb4, 0x4d, 0xd9, 0xd9, 0x81, 0x81,
	0xd7, 0x76, 0xed, 0x2f, 0x41, 0x10, 0x4e, 0x95,
	0x7f, 0x02, 0x03, 0x01, 0x00, 0x01
};

uint8_t SD_PRIV[] = {
	0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02,
	0x82, 0x01, 0x01, 0x00, 0xf4, 0xff, 0xda, 0x52,
	0xd9, 0xff, 0x51, 0x00, 0x1c, 0x30, 0x65, 0x8e,
	0x88, 0x84, 0xd4, 0xef, 0x2c, 0x1c, 0xee, 0x17,
	0xfe, 0xff, 0xd4, 0x07, 0xd2, 0x2c, 0xdb, 0x6f,
	0x62, 0x1e, 0xfd, 0x61, 0x3b, 0xd6, 0xbd, 0x91,
	0xae, 0xf2, 0xea, 0x40, 0x23, 0x13, 0xcd, 0x73,
	0x65, 0xa7, 0x04, 0xf0, 0x36, 0xbb, 0x9c, 0x25,
	0x39, 0xa4, 0x68, 0xd1, 0x9d, 0x54, 0xda, 0x36,
	0xdd, 0xc1, 0x49, 0xc4, 0xac, 0xdb, 0xc9, 0x8f,
	0xdc, 0xbe, 0xbd, 0x67, 0xe6, 0x1b, 0xe2, 0xee,
	0xc2, 0x0b, 0xc3, 0xf3, 0x94, 0x1d, 0x36, 0xa8,
	0x2c, 0x5a, 0xaa, 0x36, 0x1c, 0xc0, 0x1b, 0x1f,
	0x50, 0xd7, 0xb5, 0x13, 0xf8, 0x8e, 0x3b, 0xe4,
	0xe7, 0x58, 0xc9, 0x76, 0x60, 0x4c, 0x3f, 0xfc,
	0x35, 0x0e, 0xd8, 0x04, 0xdd, 0xc4, 0xce, 0xed,
	0x3b, 0xbf, 0xa0, 0x3c, 0xd5, 0x44, 0x06, 0x23,
	0xdd, 0x2b, 0x8c, 0xa3, 0x7b, 0xe1, 0xc4, 0x47,
	0xc3, 0x78, 0x9f, 0x5e, 0xa6, 0xae, 0x55, 0x64,
	0x6a, 0x7f, 0xa9, 0x0b, 0x1c, 0x91, 0x90, 0x29,
	0xdd, 0xd0, 0xff, 0x93, 0x22, 0x77, 0x16, 0xf6,
	0x74, 0x83, 0x7a, 0xa3, 0x6e, 0xd8, 0xc5, 0x2e,
	0xbf, 0xe1, 0x77, 0x49, 0xcb, 0x22, 0xfc, 0xee,
	0xdc, 0x69, 0xb8, 0x50, 0x29, 0x5a, 0x31, 0x64,
	0xc9, 0x09, 0x64, 0x6b, 0xc1, 0x2b, 0xfc, 0xd3,
	0xe4, 0x69, 0x11, 0x19, 0x43, 0xbe, 0x38, 0x31,
	0x51, 0xd6, 0xa6, 0xc5, 0x46, 0x6d, 0xd0, 0xe7,
	0xec, 0x6c, 0xb4, 0xc2, 0x34, 0x17, 0x35, 0xd2,
	0x5c, 0x50, 0x75, 0x35, 0xd8, 0xb4, 0x03, 0x33,
	0xdd, 0x3d, 0x9d, 0x65, 0x15, 0x8b, 0x0f, 0x90,
	0xa3, 0xc3, 0x17, 0x77, 0x19, 0xe2, 0xac, 0x28,
	0x60, 0xf4, 0x8c, 0x6e, 0xe1, 0xb4, 0x4d, 0xd9,
	0xd9, 0x81, 0x81, 0xd7, 0x76, 0xed, 0x2f, 0x41,
	0x10, 0x4e, 0x95, 0x7f, 0x02, 0x03, 0x01, 0x00,
	0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0xf1,
	0xe5, 0x75, 0xb4, 0x56, 0x7e, 0xd7, 0xa2, 0x8d,
	0x68, 0xc6, 0xc0, 0xfe, 0x6f, 0xce, 0x06, 0xa0,
	0xfe, 0x63, 0xdf, 0xf9, 0xb2, 0xa7, 0x2c, 0xf1,
	0x36, 0xaa, 0x56, 0xc2, 0x46, 0x74, 0xa1, 0xc1,
	0xdd, 0xa6, 0xd0, 0x2d, 0x35, 0x74, 0xf0, 0x2a,
	0x7b, 0x67, 0xf5, 0xe2, 0xc9, 0x70, 0xb7, 0x59,
	0xb4, 0xac, 0xdc, 0x0e, 0xd8, 0x2f, 0x1f, 0x45,
	0xe3, 0x48, 0x79, 0x0d, 0xd6, 0x3b, 0x1b, 0x26,
	0xc4, 0xd8, 0x17, 0x25, 0xfd, 0x02, 0xfa, 0x36,
	0x78, 0x09, 0xd5, 0xe4, 0xb0, 0x18, 0x12, 0x76,
	0x09, 0xa1, 0xa1, 0xe8, 0x3d, 0xff, 0x4b, 0xda,
	0xd1, 0xba, 0xf6, 0xd4, 0x80, 0x6e, 0x63, 0xdb,
	0x2f, 0xc2, 0x0f, 0x83, 0xe7, 0x83, 0x9c, 0x9a,
	0xc2, 0x85, 0x5c, 0x8f, 0xa3, 0x8c, 0x2d, 0xf4,
	0xcd, 0x48, 0x68, 0x6c, 0x8b, 0xe5, 0x1f, 0xc7,
	0xbd, 0x11, 0x43, 0x2f, 0x66, 0x50, 0xc1, 0xf1,
	0xd8, 0xb9, 0xec, 0xf8, 0x25, 0xb5, 0xb3, 0xff,
	0xd4, 0x59, 0x64, 0x50, 0x5b, 0x8d, 0x2b, 0xe8,
	0x07, 0x40, 0x61, 0xaf, 0x01, 0x7f, 0xfd, 0xf0,
	0x72, 0xd1, 0x6e, 0x26, 0xff, 0xe4, 0x96, 0x72,
	0x2a, 0x8d, 0x19, 0x07, 0x0d, 0x0c, 0xfc, 0x4a,
	0x99, 0xad, 0x24, 0x30, 0x8a, 0x93, 0xe9, 0x56,
	0x85, 0x9d, 0x73, 0x47, 0x47, 0xcb, 0x3f, 0x8c,
	0x01, 0xf2, 0x6d, 0x05, 0xfe, 0x6e, 0x85, 0xe4,
	0x55, 0x1c, 0x7d, 0x5d, 0x40, 0x3b, 0x4f, 0xc2,
	0x06, 0x7a, 0x13, 0xfe, 0x27, 0xb7, 0xc5, 0x46,
	0x47, 0xc7, 0xc4, 0x0a, 0x79, 0x9b, 0xfb, 0x03,
	0xad, 0x75, 0xac, 0x34, 0xc6, 0x8f, 0x30, 0x08,
	0xf4, 0x52, 0x55, 0x08, 0xa6, 0x21, 0xa7, 0xb2,
	0x00, 0x46, 0xda, 0x3c, 0x9b, 0x7f, 0xa8, 0x55,
	0x33, 0x7b, 0x13, 0x7c, 0xf0, 0xeb, 0x21, 0x35,
	0xda, 0xe7, 0x6c, 0xc6, 0xb2, 0x01, 0x02, 0x81,
	0x81, 0x00, 0xfa, 0xcf, 0xee, 0x66, 0xfa, 0x6e,
	0x71, 0x4b, 0xce, 0xa2, 0x9c, 0x87, 0x64, 0x07,
	0xf9, 0xec, 0x04, 0x9c, 0xd6, 0x48, 0x2e, 0x2c,
	0x0b, 0x1e, 0x95, 0x85, 0x42, 0x6d, 0xe5, 0x0b,
	0x24, 0x01, 0xc6, 0x7a, 0x10, 0x9d, 0xd8, 0x12,
	0x29, 0x9c, 0x4c, 0x20, 0x08, 0x0a, 0x4b, 0x13,
	0x08, 0x08, 0x23, 0x1e, 0x4e, 0x46, 0x33, 0xe0,
	0xfd, 0xe0, 0x63, 0x61, 0x8e, 0xf4, 0x45, 0xb4,
	0x8e, 0x85, 0x47, 0x51, 0x31, 0xb1, 0x63, 0x0e,
	0x66, 0x44, 0x40, 0xc6, 0x4f, 0xb7, 0x25, 0x85,
	0xaf, 0x63, 0x70, 0x0f, 0xee, 0x87, 0x07, 0xbd,
	0xc3, 0x96, 0xb3, 0x61, 0x54, 0x50, 0xee, 0x19,
	0x80, 0xce, 0x10, 0x69, 0xc0, 0x7e, 0x8a, 0x70,
	0x5d, 0xd2, 0x89, 0xee, 0x04, 0x95, 0x4c, 0x12,
	0x6b, 0x1b, 0x6b, 0xb3, 0xcb, 0x80, 0xed, 0x74,
	0xa0, 0xf5, 0xab, 0x37, 0xfb, 0x66, 0xe4, 0x26,
	0x04, 0xbf, 0x02, 0x81, 0x81, 0x00, 0xfa, 0x11,
	0x24, 0x70, 0x7e, 0xab, 0x2d, 0x72, 0x4c, 0x1b,
	0x92, 0x86, 0x24, 0xfd, 0x36, 0x5e, 0xda, 0xbe,
	0x96, 0x23, 0xee, 0x30, 0xe3, 0xa9, 0x51, 0x6c,
	0xda, 0xc2, 0xfb, 0x42, 0xfc, 0xa0, 0x83, 0x6c,
	0x2f, 0x4a, 0x0c, 0x0a, 0xcf, 0x55, 0xb4, 0xbe,
	0xf2, 0x8c, 0x18, 0x76, 0xa0, 0x1e, 0x40, 0xef,
	0x89, 0x02, 0x94, 0x64, 0x6f, 0xeb, 0x48, 0xeb,
	0x57, 0x17, 0x7a, 0x58, 0xa2, 0x03, 0x07, 0xeb,
	0x5a, 0x48, 0xe9, 0x51, 0x55, 0x3b, 0x75, 0x5f,
	0x95, 0x23, 0x8c, 0x20, 0x0c, 0x71, 0x34, 0xe5,
	0x8d, 0x2d, 0x40, 0xeb, 0xe3, 0x98, 0x29, 0xdf,
	0xdd, 0xe1, 0x37, 0x83, 0x35, 0xaa, 0x46, 0xb2,
	0xfb, 0x24, 0x15, 0xc4, 0x4e, 0xe8, 0x5d, 0xde,
	0x80, 0xc8, 0xbd, 0x34, 0xb2, 0x27, 0x91, 0xf9,
	0xb2, 0x20, 0xe3, 0xb1, 0x1e, 0xaf, 0x19, 0x75,
	0xc5, 0xf3, 0xb3, 0xbb, 0xdf, 0x41, 0x02, 0x81,
	0x80, 0x34, 0xa5, 0x08, 0x69, 0x5d, 0x0f, 0x69,
	0x80, 0x7b, 0xf2, 0xed, 0xe0, 0x0d, 0x43, 0x0f,
	0x56, 0x56, 0xf1, 0x84, 0x98, 0xc9, 0xf8, 0x3c,
	0xad, 0x42, 0xbd, 0x21, 0xe0, 0x67, 0x3f, 0x1b,
	0x63, 0xb4, 0x01, 0x53, 0x13, 0x8e, 0x92, 0xe1,
	0x61, 0x09, 0x29, 0x80, 0x06, 0x89, 0x13, 0x89,
	0x90, 0x0b, 0xaf, 0x04, 0xc6, 0xff, 0x84, 0xae,
	0x45, 0x37, 0x16, 0xf7, 0x26, 0x96, 0xaa, 0xfb,
	0x6f, 0x46, 0x34, 0xe8, 0xb5, 0x4f, 0x11, 0x6a,
	0xad, 0xd0, 0xda, 0xcf, 0x48, 0xa5, 0xfb, 0x95,
	0x80, 0xcd, 0xb8, 0xca, 0x5c, 0x55, 0x67, 0xa3,
	0x72, 0x32, 0x5c, 0xaf, 0x91, 0xfd, 0x28, 0x25,
	0x3d, 0x1b, 0x7c, 0xd7, 0x06, 0xb8, 0x90, 0x35,
	0x61, 0x47, 0xca, 0x49, 0xf3, 0xfc, 0x6f, 0xb1,
	0x43, 0xa4, 0x2b, 0xac, 0xff, 0x76, 0xd8, 0x10,
	0x21, 0x00, 0xbb, 0x53, 0x6d, 0x99, 0x5d, 0xd1,
	0x89, 0x02, 0x81, 0x80, 0x21, 0xf1, 0xb1, 0xc0,
	0x93, 0x71, 0x28, 0xfa, 0x1a, 0xda, 0xa3, 0x49,
	0x12, 0x3c, 0x4c, 0xe5, 0x13, 0x4c, 0x4b, 0x95,
	0x15, 0x00, 0x0d, 0xe7, 0x2d, 0xe6, 0x58, 0xcb,
	0x9e, 0xcf, 0xb8, 0xfb, 0x10, 0xdb, 0xde, 0x8f,
	0x6e, 0x93, 0x09, 0x11, 0xd9, 0x37, 0x12, 0x35,
	0x72, 0x1c, 0xa8, 0x1f, 0x73, 0x31, 0x10, 0xa8,
	0x08, 0xfc, 0x99, 0x34, 0x96, 0xf1, 0x5b, 0x21,
	0x14, 0xba, 0x73, 0x43, 0x76, 0x0f, 0xbc, 0x71,
	0xa9, 0x3a, 0x21, 0x17, 0xb1, 0x17, 0x08, 0x67,
	0x8a, 0xdd, 0xce, 0xd2, 0x8f, 0x1b, 0x99, 0xce,
	0x4a, 0xc1, 0xba, 0x63, 0x2d, 0x4b, 0xf6, 0x12,
	0x5c, 0x36, 0xb3, 0x52, 0x29, 0xc6, 0xa1, 0xd0,
	0x84, 0x23, 0xe1, 0x92, 0x29, 0x2b, 0xca, 0xcf,
	0x74, 0xdf, 0x72, 0x4f, 0x6c, 0xc1, 0x38, 0x2c,
	0xbe, 0xbb, 0x8c, 0x2e, 0xb8, 0x86, 0x04, 0x18,
	0xd0, 0x8c, 0xf0, 0x01, 0x02, 0x81, 0x81, 0x00,
	0xc7, 0x79, 0x04, 0x42, 0x67, 0xd5, 0xa9, 0xdf,
	0x48, 0xe6, 0x91, 0xc1, 0xb2, 0xfe, 0xb0, 0x83,
	0x9b, 0xe8, 0x4a, 0xc0, 0xbf, 0x8f, 0x13, 0x87,
	0x07, 0xa8, 0x43, 0xa3, 0xdd, 0x53, 0xfc, 0x5f,
	0xa9, 0xfe, 0x10, 0x24, 0xe7, 0xe8, 0xb3, 0xeb,
	0x20, 0x8f, 0xfc, 0x74, 0x10, 0x9c, 0x24, 0x69,
	0x84, 0xf4, 0x1d, 0x08, 0xcf, 0xdb, 0x77, 0x9e,
	0xe5, 0x7b, 0xd3, 0x7b, 0x55, 0x6e, 0xa1, 0x16,
	0x70, 0xc9, 0xaf, 0x63, 0x5a, 0x2f, 0xa0, 0x26,
	0xf3, 0xfb, 0xce, 0xa5, 0xbd, 0x50, 0xa8, 0xda,
	0xde, 0x0f, 0xcc, 0x88, 0xfd, 0xaa, 0xe5, 0x9f,
	0x00, 0xdb, 0x4a, 0x50, 0x56, 0x20, 0x2f, 0x21,
	0x00, 0xe7, 0x48, 0x6b, 0x89, 0xe7, 0x71, 0x91,
	0xd2, 0x07, 0xc6, 0xf6, 0x3d, 0x50, 0x3d, 0xd3,
	0xdf, 0x13, 0x1a, 0xce, 0xeb, 0xe5, 0x89, 0x40,
	0xab, 0x15, 0x41, 0xb5, 0xf5, 0x64, 0xf5, 0xa5
};
#endif

#ifdef __MLDAP_USE_DRK_TEST_KEY__

uint32_t DRKCertLen = 1087;
uint32_t DRKLen = 1190;

uint8_t DRKCert[] = {0x30, 0x82, 0x04, 0x3b, 0x30, 0x82, 0x03, 0x23, 0xa0, 0x03,
			0x02, 0x01, 0x02, 0x02, 0x01, 0x0b, 0x30, 0x0d, 0x06, 0x09,
			0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
			0x00, 0x30, 0x59, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
			0x04, 0x06, 0x13, 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11,
			0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0a, 0x53, 0x75, 0x77,
			0x6f, 0x6e, 0x20, 0x63, 0x69, 0x74, 0x79, 0x31, 0x17, 0x30,
			0x15, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x0e, 0x53, 0x61,
			0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20, 0x4d, 0x6f, 0x62, 0x69,
			0x6c, 0x65, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04,
			0x03, 0x0c, 0x13, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67,
			0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69,
			0x6f, 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x35,
			0x32, 0x31, 0x31, 0x33, 0x30, 0x36, 0x34, 0x31, 0x5a, 0x17,
			0x0d, 0x33, 0x33, 0x30, 0x35, 0x31, 0x36, 0x31, 0x33, 0x30,
			0x36, 0x34, 0x31, 0x5a, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30,
			0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 0x52,
			0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
			0x0a, 0x53, 0x75, 0x77, 0x6f, 0x6e, 0x20, 0x63, 0x69, 0x74,
			0x79, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03,
			0x0c, 0x13, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20,
			0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f,
			0x6e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0b,
			0x0c, 0x0e, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20,
			0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x31, 0x31, 0x30, 0x2f,
			0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64,
			0x01, 0x01, 0x0c, 0x21, 0x41, 0x50, 0x50, 0x43, 0x45, 0x2d,
			0x44, 0x45, 0x56, 0x5f, 0x32, 0x30, 0x31, 0x33, 0x30, 0x35,
			0x32, 0x31, 0x5f, 0x30, 0x31, 0x5f, 0x30, 0x31, 0x5f, 0x30,
			0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x82, 0x01,
			0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
			0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
			0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
			0xb0, 0x48, 0x8f, 0x12, 0x1a, 0x15, 0x6b, 0x66, 0x8a, 0xa0,
			0x15, 0x14, 0x44, 0x9c, 0x5b, 0xc2, 0x0c, 0x15, 0x5c, 0xe3,
			0xdf, 0xaf, 0x94, 0xd2, 0x0f, 0xd8, 0xf5, 0x56, 0x89, 0x8d,
			0x9b, 0x0e, 0x35, 0xfc, 0x08, 0x81, 0x01, 0x7d, 0x1c, 0x9d,
			0x60, 0xc3, 0xbd, 0x77, 0x0e, 0xc7, 0x3d, 0x43, 0x99, 0x2b,
			0xdc, 0x06, 0x55, 0xf9, 0x2b, 0xdb, 0x2d, 0xd0, 0x5d, 0x9c,
			0xc9, 0x0b, 0xc6, 0xe9, 0xcc, 0x46, 0xaa, 0x11, 0xb0, 0xfe,
			0xe0, 0xac, 0xf2, 0x0e, 0x37, 0x11, 0x3e, 0xb1, 0x92, 0x62,
			0x1c, 0xe0, 0x8b, 0x19, 0x08, 0x4c, 0x1e, 0xae, 0xbb, 0x99,
			0xd8, 0x6d, 0x56, 0x1d, 0x85, 0x55, 0x86, 0x6b, 0x65, 0x32,
			0x23, 0xb0, 0x9d, 0x0f, 0xa9, 0x17, 0x5a, 0xf7, 0x3f, 0x79,
			0x8a, 0x5a, 0x1b, 0xfe, 0x43, 0x2f, 0xd0, 0x36, 0x6b, 0xfb,
			0xc1, 0x55, 0x73, 0x87, 0xf7, 0x99, 0x51, 0x40, 0xae, 0x10,
			0xd8, 0xce, 0x55, 0x35, 0xca, 0xb3, 0x38, 0x6d, 0xfb, 0xcf,
			0x99, 0x93, 0x27, 0x74, 0xe0, 0x99, 0x3f, 0x06, 0x72, 0x21,
			0x34, 0x95, 0x8c, 0xdf, 0x75, 0xad, 0xa1, 0x54, 0xca, 0xcf,
			0xd4, 0xa2, 0xa1, 0x53, 0x2b, 0x25, 0xaf, 0x56, 0x31, 0xbf,
			0x95, 0x2a, 0x90, 0x2e, 0x23, 0xf0, 0x27, 0xa7, 0xd2, 0xe6,
			0xb8, 0x5c, 0x1e, 0x9b, 0x3b, 0xc2, 0xed, 0xc4, 0x33, 0x0a,
			0xd9, 0xa5, 0x23, 0x80, 0x21, 0xb8, 0x90, 0xa1, 0x45, 0x74,
			0x37, 0x84, 0x41, 0x88, 0x76, 0xe1, 0x13, 0x70, 0x2c, 0x3d,
			0x13, 0x3d, 0xb9, 0x6b, 0x9b, 0xf7, 0x02, 0x78, 0xbe, 0x0e,
			0x1b, 0x95, 0xd5, 0xdd, 0x51, 0xba, 0x0e, 0x85, 0xcc, 0xb5,
			0x33, 0x6f, 0xf1, 0x06, 0x32, 0xff, 0x39, 0xaf, 0x86, 0x82,
			0x18, 0x8c, 0x7b, 0xb3, 0x4a, 0x2c, 0x58, 0xe6, 0xd1, 0xf2,
			0xbd, 0xa8, 0x13, 0x7a, 0x22, 0xd1, 0x02, 0x03, 0x01, 0x00,
			0x01, 0xa3, 0x81, 0xd9, 0x30, 0x81, 0xd6, 0x30, 0x09, 0x06,
			0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b,
			0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x06,
			0xc0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
			0x04, 0x14, 0xd0, 0xd9, 0x5a, 0x6b, 0x9e, 0xc1, 0xbe, 0xc2,
			0x73, 0x8e, 0x0e, 0x9c, 0x13, 0xc5, 0x56, 0xac, 0x1c, 0xd5,
			0x59, 0x91, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
			0x18, 0x30, 0x16, 0x80, 0x14, 0x28, 0xb9, 0x7c, 0x8a, 0x74,
			0x81, 0xf4, 0x73, 0x77, 0xbe, 0x6f, 0xd2, 0xb0, 0x11, 0x32,
			0xb2, 0x6e, 0x8b, 0x55, 0x71, 0x30, 0x3d, 0x06, 0x08, 0x2b,
			0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30,
			0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
			0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
			0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x61, 0x6d,
			0x73, 0x75, 0x6e, 0x67, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73,
			0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x30, 0x3d,
			0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x36, 0x30, 0x34, 0x30,
			0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68, 0x74, 0x74,
			0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x61,
			0x6d, 0x73, 0x75, 0x6e, 0x67, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
			0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x72,
			0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x63, 0x72,
			0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
			0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
			0x00, 0x4d, 0x0d, 0x80, 0x6d, 0x15, 0x2e, 0x1c, 0xb8, 0xf8,
			0xa3, 0x79, 0x65, 0xae, 0x27, 0xd9, 0x62, 0xe5, 0x97, 0x8d,
			0x23, 0xc3, 0x76, 0x0f, 0x56, 0xa0, 0x0b, 0x26, 0x47, 0x87,
			0x8c, 0x62, 0x2e, 0xac, 0x3c, 0x50, 0x5e, 0x3f, 0xbe, 0x64,
			0x5c, 0x2c, 0x48, 0xa5, 0x9f, 0x0e, 0x67, 0xb1, 0x6b, 0x80,
			0x6b, 0xcc, 0x7b, 0x36, 0xbf, 0x27, 0xf8, 0x28, 0xdf, 0xc0,
			0x08, 0x04, 0x4b, 0x9e, 0x49, 0xe1, 0xe8, 0x65, 0x02, 0x2e,
			0x34, 0x58, 0x38, 0x1e, 0xe2, 0x7f, 0xfa, 0xd5, 0x5e, 0xd5,
			0xc6, 0xc0, 0xe0, 0xaf, 0x9f, 0xd5, 0xcf, 0x8c, 0xfb, 0xd0,
			0x8c, 0x74, 0x69, 0x53, 0x51, 0x0c, 0x98, 0xfe, 0x7e, 0x48,
			0x45, 0x18, 0xc4, 0x7a, 0xbe, 0x71, 0x50, 0xc0, 0x15, 0x62,
			0xfd, 0xf4, 0x1c, 0xcb, 0x4e, 0x71, 0x39, 0x1a, 0xdb, 0x05,
			0xb0, 0xe8, 0xe6, 0x66, 0xac, 0xee, 0x8d, 0xb0, 0xf9, 0x40,
			0x2e, 0x37, 0xad, 0xb0, 0x99, 0x43, 0x05, 0xf1, 0xf0, 0xfc,
			0x39, 0x45, 0x23, 0x90, 0xf3, 0x28, 0x0b, 0x0a, 0x6c, 0xb2,
			0x06, 0x85, 0xbc, 0x35, 0xce, 0xca, 0xd3, 0x5b, 0xc0, 0xe4,
			0xa2, 0xf3, 0xb9, 0x9c, 0x6b, 0x0b, 0x0f, 0xc8, 0xef, 0x8c,
			0x0e, 0x43, 0xe8, 0x32, 0xb1, 0x83, 0x46, 0xc2, 0xf4, 0xd5,
			0x87, 0xf7, 0x78, 0x3a, 0x9f, 0x9c, 0xc0, 0xbd, 0x1a, 0x5d,
			0xaa, 0x43, 0x72, 0x66, 0x93, 0x57, 0x75, 0x3e, 0x4e, 0x4b,
			0xc7, 0x0c, 0xa1, 0x6a, 0x76, 0x96, 0x39, 0x29, 0xd6, 0x0b,
			0x1b, 0x19, 0x3c, 0x57, 0x65, 0xbf, 0xc3, 0x8b, 0x33, 0x57,
			0x7f, 0xb6, 0xe5, 0xc9, 0x7b, 0x0d, 0x9d, 0x1d, 0xfe, 0x2d,
			0xfd, 0xb6, 0xb6, 0x88, 0xea, 0x11, 0x67, 0xb3, 0x4e, 0xb3,
			0x13, 0x0b, 0x65, 0xa5, 0xf5, 0x6f, 0x47, 0xa4, 0x97, 0x04,
			0x64, 0x2f, 0x9c, 0xa4, 0x01, 0x96, 0xa4};

uint8_t DRK[] = {0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01,
		0x01, 0x00, 0xb0, 0x48, 0x8f, 0x12, 0x1a, 0x15, 0x6b, 0x66,
		0x8a, 0xa0, 0x15, 0x14, 0x44, 0x9c, 0x5b, 0xc2, 0x0c, 0x15,
		0x5c, 0xe3, 0xdf, 0xaf, 0x94, 0xd2, 0x0f, 0xd8, 0xf5, 0x56,
		0x89, 0x8d, 0x9b, 0x0e, 0x35, 0xfc, 0x08, 0x81, 0x01, 0x7d,
		0x1c, 0x9d, 0x60, 0xc3, 0xbd, 0x77, 0x0e, 0xc7, 0x3d, 0x43,
		0x99, 0x2b, 0xdc, 0x06, 0x55, 0xf9, 0x2b, 0xdb, 0x2d, 0xd0,
		0x5d, 0x9c, 0xc9, 0x0b, 0xc6, 0xe9, 0xcc, 0x46, 0xaa, 0x11,
		0xb0, 0xfe, 0xe0, 0xac, 0xf2, 0x0e, 0x37, 0x11, 0x3e, 0xb1,
		0x92, 0x62, 0x1c, 0xe0, 0x8b, 0x19, 0x08, 0x4c, 0x1e, 0xae,
		0xbb, 0x99, 0xd8, 0x6d, 0x56, 0x1d, 0x85, 0x55, 0x86, 0x6b,
		0x65, 0x32, 0x23, 0xb0, 0x9d, 0x0f, 0xa9, 0x17, 0x5a, 0xf7,
		0x3f, 0x79, 0x8a, 0x5a, 0x1b, 0xfe, 0x43, 0x2f, 0xd0, 0x36,
		0x6b, 0xfb, 0xc1, 0x55, 0x73, 0x87, 0xf7, 0x99, 0x51, 0x40,
		0xae, 0x10, 0xd8, 0xce, 0x55, 0x35, 0xca, 0xb3, 0x38, 0x6d,
		0xfb, 0xcf, 0x99, 0x93, 0x27, 0x74, 0xe0, 0x99, 0x3f, 0x06,
		0x72, 0x21, 0x34, 0x95, 0x8c, 0xdf, 0x75, 0xad, 0xa1, 0x54,
		0xca, 0xcf, 0xd4, 0xa2, 0xa1, 0x53, 0x2b, 0x25, 0xaf, 0x56,
		0x31, 0xbf, 0x95, 0x2a, 0x90, 0x2e, 0x23, 0xf0, 0x27, 0xa7,
		0xd2, 0xe6, 0xb8, 0x5c, 0x1e, 0x9b, 0x3b, 0xc2, 0xed, 0xc4,
		0x33, 0x0a, 0xd9, 0xa5, 0x23, 0x80, 0x21, 0xb8, 0x90, 0xa1,
		0x45, 0x74, 0x37, 0x84, 0x41, 0x88, 0x76, 0xe1, 0x13, 0x70,
		0x2c, 0x3d, 0x13, 0x3d, 0xb9, 0x6b, 0x9b, 0xf7, 0x02, 0x78,
		0xbe, 0x0e, 0x1b, 0x95, 0xd5, 0xdd, 0x51, 0xba, 0x0e, 0x85,
		0xcc, 0xb5, 0x33, 0x6f, 0xf1, 0x06, 0x32, 0xff, 0x39, 0xaf,
		0x86, 0x82, 0x18, 0x8c, 0x7b, 0xb3, 0x4a, 0x2c, 0x58, 0xe6,
		0xd1, 0xf2, 0xbd, 0xa8, 0x13, 0x7a, 0x22, 0xd1, 0x02, 0x03,
		0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x7c, 0x45, 0xda,
		0x9e, 0x7f, 0x46, 0xe4, 0x5c, 0xf8, 0x93, 0x1b, 0xe9, 0x1a,
		0xe9, 0x43, 0x82, 0x8b, 0xb4, 0x2f, 0xb4, 0xf1, 0x47, 0x3d,
		0x59, 0xcd, 0x14, 0x3c, 0xf8, 0x91, 0xa6, 0x03, 0x4e, 0x44,
		0x5e, 0x75, 0xbc, 0x44, 0x49, 0x67, 0xf7, 0xc6, 0x74, 0x97,
		0x47, 0xac, 0x62, 0x66, 0xdd, 0x15, 0xc9, 0x26, 0xa6, 0x4c,
		0xcb, 0x88, 0xd0, 0x4d, 0xfe, 0xdb, 0x4f, 0xa1, 0x27, 0xad,
		0x45, 0xba, 0xdd, 0x82, 0x1d, 0x50, 0xb2, 0x8d, 0xbf, 0x3e,
		0x31, 0x29, 0x3e, 0xfd, 0xfc, 0x7e, 0xde, 0xad, 0xce, 0xda,
		0x20, 0x7e, 0x89, 0xf3, 0x03, 0x69, 0x7b, 0x6e, 0x68, 0xbd,
		0xd1, 0xb7, 0xe2, 0x70, 0x0d, 0x9b, 0xdc, 0x79, 0xee, 0x92,
		0xfa, 0xac, 0x50, 0xab, 0xaf, 0x2b, 0xcf, 0x49, 0xea, 0x76,
		0x5b, 0x8c, 0x0f, 0xd3, 0xef, 0xd9, 0xb6, 0x34, 0x38, 0x42,
		0x66, 0xf2, 0xf4, 0xe1, 0xa2, 0xd3, 0xab, 0xe8, 0x79, 0x6e,
		0x17, 0x2b, 0x4e, 0xc5, 0x87, 0xc5, 0x7f, 0xbe, 0x08, 0xa8,
		0xae, 0x8e, 0x2a, 0xff, 0x33, 0xc7, 0x74, 0x8f, 0x58, 0x8e,
		0xad, 0x72, 0xab, 0xb1, 0x68, 0x43, 0xaf, 0x7b, 0x15, 0x85,
		0x5b, 0x77, 0xb6, 0x81, 0x82, 0x32, 0x56, 0x65, 0x80, 0xed,
		0xd9, 0x19, 0xe6, 0xea, 0xde, 0x6c, 0x65, 0xaf, 0x8f, 0x78,
		0x8e, 0x32, 0x4d, 0xe1, 0x9a, 0x38, 0x37, 0x81, 0x61, 0xb3,
		0x31, 0xf2, 0x00, 0x7c, 0x39, 0x75, 0x7a, 0x48, 0x7d, 0x9b,
		0x1c, 0x8b, 0x3c, 0x34, 0x9e, 0xff, 0x8e, 0x1e, 0xf7, 0xeb,
		0xea, 0x2e, 0x6b, 0xb1, 0x40, 0x85, 0x6b, 0x8f, 0x20, 0x97,
		0x15, 0x15, 0x2c, 0x2a, 0x5f, 0x3c, 0x28, 0x90, 0xfa, 0x62,
		0x8c, 0xb0, 0x37, 0x8f, 0xfa, 0x73, 0xd2, 0xfa, 0x8d, 0xf5,
		0x9e, 0xeb, 0x64, 0xf7, 0x69, 0xfd, 0x14, 0xe8, 0x6f, 0xcb,
		0x46, 0x3c, 0x01, 0x02, 0x81, 0x81, 0x00, 0xe7, 0xbb, 0x89,
		0xd9, 0xd3, 0x0f, 0x34, 0x04, 0xf9, 0x25, 0x9b, 0xc7, 0xee,
		0xe0, 0x6c, 0x51, 0x3c, 0x82, 0xdd, 0xf1, 0x37, 0xb1, 0x76,
		0x9f, 0xc2, 0xbc, 0x88, 0x4a, 0x74, 0x0f, 0xf2, 0x70, 0x30,
		0x39, 0x66, 0xb4, 0x3d, 0x01, 0xe4, 0x03, 0x31, 0xf8, 0x67,
		0xd7, 0x67, 0x14, 0xd1, 0xdc, 0x5e, 0x99, 0xe5, 0xd5, 0x35,
		0x6b, 0xfa, 0xd9, 0xa5, 0x74, 0xc6, 0x0b, 0x2b, 0x08, 0x2d,
		0xdc, 0xb1, 0x2b, 0x43, 0xfa, 0xdd, 0x71, 0xb9, 0xb5, 0x7d,
		0xb7, 0xa6, 0xc1, 0x29, 0xe4, 0x7a, 0x25, 0xb6, 0x99, 0x13,
		0xda, 0x2c, 0xc9, 0x1e, 0xb3, 0xf4, 0xa1, 0x02, 0xba, 0x12,
		0x4c, 0xb5, 0xcc, 0x04, 0x47, 0x24, 0x6a, 0xa3, 0x55, 0xdc,
		0x4f, 0x02, 0x8c, 0x59, 0xe9, 0x24, 0x20, 0x44, 0x3c, 0x3f,
		0xa1, 0x6b, 0x7c, 0x8b, 0x7f, 0xa5, 0xb3, 0x46, 0xcf, 0x70,
		0x37, 0x67, 0x97, 0xc1, 0x11, 0x02, 0x81, 0x81, 0x00, 0xc2,
		0xbe, 0x7f, 0x80, 0xe7, 0x3c, 0xce, 0x4a, 0x5f, 0x18, 0x9d,
		0xf5, 0x7b, 0xda, 0x2f, 0x3e, 0x88, 0x44, 0xbd, 0x20, 0x34,
		0xf7, 0x34, 0x3f, 0xb4, 0xdb, 0xe3, 0xc1, 0xa0, 0xdd, 0x99,
		0x61, 0x1b, 0x38, 0xa6, 0x07, 0x19, 0xca, 0xc1, 0x2c, 0xbb,
		0xda, 0x64, 0xe6, 0x3a, 0x5b, 0xe5, 0xad, 0xb1, 0x90, 0x6e,
		0x8f, 0xa4, 0x41, 0x57, 0xab, 0xf6, 0x1b, 0xdd, 0x92, 0x8e,
		0x38, 0x7c, 0xcc, 0xb7, 0x2c, 0xc8, 0x0d, 0x17, 0x18, 0x8d,
		0x67, 0x38, 0xee, 0xc4, 0x61, 0x2a, 0xc0, 0x0e, 0xfd, 0xe4,
		0x97, 0xd2, 0x35, 0x0f, 0x9b, 0x5e, 0x04, 0x53, 0x7f, 0x5c,
		0x2d, 0x7c, 0x05, 0xbd, 0x6c, 0x41, 0xb9, 0x05, 0x94, 0xec,
		0x19, 0xe0, 0xa6, 0xb0, 0x41, 0x6f, 0xb1, 0xe8, 0x44, 0x70,
		0x95, 0x09, 0x8b, 0xe2, 0x6f, 0x4a, 0x6b, 0x5d, 0xf8, 0x47,
		0x9a, 0xcd, 0x70, 0x62, 0x88, 0x45, 0xc1, 0x02, 0x81, 0x80,
		0x10, 0xf5, 0x4c, 0x75, 0x40, 0x58, 0xd0, 0x79, 0xbc, 0xb1,
		0xdf, 0xe0, 0x95, 0xbe, 0x9e, 0xae, 0xa3, 0x3b, 0x00, 0x7a,
		0xf2, 0x94, 0x6e, 0x15, 0xea, 0xee, 0x59, 0x1a, 0xf9, 0xbb,
		0x61, 0x06, 0x8c, 0xc0, 0xe6, 0x9f, 0x32, 0x07, 0xbd, 0x63,
		0xee, 0x78, 0x3b, 0x41, 0x2c, 0x2d, 0xfd, 0xdd, 0x9e, 0x9e,
		0xac, 0x8f, 0x19, 0xc5, 0xb9, 0x29, 0xcc, 0x4d, 0xeb, 0x60,
		0x4d, 0xd9, 0xdf, 0x61, 0x53, 0x25, 0xd3, 0x67, 0xbc, 0x64,
		0xe2, 0x2e, 0x41, 0xf0, 0xfa, 0xa7, 0x10, 0x25, 0xc4, 0x3a,
		0x96, 0x4e, 0x45, 0x81, 0xa5, 0xdd, 0x61, 0xfd, 0xd1, 0x5d,
		0x27, 0x11, 0xee, 0xdc, 0xe6, 0x40, 0xf3, 0xdf, 0x30, 0xef,
		0x0c, 0xdc, 0xef, 0xae, 0x68, 0x85, 0x54, 0xdf, 0x16, 0xe1,
		0xb5, 0x9c, 0xa3, 0x1b, 0x70, 0xfe, 0x40, 0x47, 0x2b, 0x1d,
		0x1f, 0x85, 0x76, 0xb4, 0xd7, 0x43, 0xe7, 0xc1, 0x02, 0x81,
		0x80, 0x6d, 0xcf, 0x30, 0xe2, 0x24, 0x81, 0x5a, 0xe9, 0x00,
		0xba, 0x46, 0xc7, 0x57, 0x30, 0xee, 0x16, 0xda, 0x8b, 0xb8,
		0x00, 0xe3, 0x1c, 0xc9, 0x2f, 0x6a, 0xf1, 0xda, 0x79, 0x22,
		0x16, 0x12, 0x18, 0x79, 0x5a, 0xf5, 0xf3, 0x67, 0x0d, 0xb6,
		0xe3, 0x5c, 0x81, 0x87, 0x07, 0x72, 0xbc, 0xc2, 0xc3, 0xb0,
		0x2d, 0xfa, 0xc9, 0xb0, 0x1a, 0xd0, 0x2e, 0xbc, 0xb5, 0x75,
		0xb8, 0xdf, 0xcc, 0xe6, 0x42, 0x20, 0xbf, 0x75, 0x86, 0x3c,
		0x11, 0xb2, 0x1a, 0x14, 0x36, 0x3e, 0x31, 0x74, 0x03, 0x9b,
		0xf0, 0x79, 0xe7, 0x49, 0xb6, 0x75, 0xb8, 0x6b, 0xc9, 0x10,
		0xb3, 0xed, 0xec, 0x0f, 0xd4, 0xc4, 0xbc, 0xb8, 0x4a, 0x75,
		0xe5, 0x45, 0x44, 0x4a, 0x32, 0x73, 0x8c, 0x52, 0x49, 0x04,
		0xe9, 0x22, 0x7a, 0x47, 0x49, 0xc3, 0x42, 0x71, 0xba, 0x1c,
		0xe0, 0x94, 0xaf, 0xab, 0x4b, 0xc5, 0xa3, 0x85, 0x81, 0x02,
		0x81, 0x80, 0x7a, 0xfa, 0x84, 0x3d, 0x96, 0x19, 0xb3, 0x9f,
		0xf9, 0xfd, 0xb0, 0x62, 0x6d, 0x7b, 0x7e, 0x9d, 0x60, 0x27,
		0x9e, 0xe0, 0x02, 0xf9, 0xa5, 0x25, 0xdd, 0xf7, 0x74, 0xb4,
		0xe8, 0x07, 0xdd, 0x1a, 0x42, 0x12, 0x27, 0x37, 0x85, 0xab,
		0xfd, 0x92, 0x7a, 0xa2, 0x43, 0x5a, 0xd2, 0x28, 0x9e, 0xa8,
		0xc4, 0x0a, 0x4a, 0x65, 0x55, 0x42, 0x8a, 0x39, 0x82, 0xbc,
		0x41, 0x49, 0xdd, 0x33, 0x6f, 0x1b, 0x7c, 0x27, 0x04, 0xf1,
		0x3b, 0x03, 0xa0, 0x38, 0x90, 0xb8, 0x95, 0x01, 0x5a, 0x67,
		0x91, 0x44, 0x3b, 0x3a, 0xe6, 0x0b, 0xa5, 0x48, 0x80, 0xe6,
		0xfb, 0xa4, 0x6a, 0x09, 0xbe, 0xe1, 0x96, 0x7f, 0xa8, 0xf0,
		0xc7, 0x72, 0x44, 0xe1, 0x9b, 0xdc, 0x3e, 0xa6, 0x08, 0x91,
		0x8e, 0xa9, 0x3b, 0xb4, 0x57, 0x05, 0xad, 0xd1, 0x3f, 0x9a,
		0x06, 0x37, 0x9c, 0x68, 0x90, 0x8f, 0xcb, 0x2d, 0x90, 0x8d};

int32_t getRawDeviceKeys(uint8_t* cert, uint32_t* certLen, uint8_t* prk, uint32_t* prkLen, void* wrapped, uint32_t wrappedLen)
{
	int i;
	*certLen = DRKCertLen;
	*prkLen = DRKLen;

	for (i = 0; i < DRKCertLen; ++i)
		cert[i] = DRKCert[i];

	for (i = 0; i < DRKLen; ++i)
		prk[i] = DRK[i];

	return NO_ERROR;
}
#endif

/* set 16bit number and increment N */
#define SET_UINT16T_LE_INC(N, INTG, ARR) \
	do { \
		ARR[N++] = (uint8_t)(INTG & 0xFF); \
		ARR[N++] = (uint8_t)((INTG >> 8) & 0xFF); \
	} while (0);

#define TL_SIZE 3

/* may set some fields to default values if they ain't set */
static int32_t _validateKeyInfo(KeyInfo_t *keyInfo)
{
	if (!keyInfo) {
		LOGE("KeyInfo is NULL\n");
		return WRONG_DATA;
	}

	if (!serviceNameValid((char *)keyInfo->serviceName)) {
		LOGE("Service name doesn't belong to the permitted list\n");
		return WRONG_DATA;
	}

	if (keyInfo->keyLen == 0) {
		/* set default value 2048 */
		keyInfo->keyLen = 2048;
	} else if (keyInfo->keyLen != 2048 && keyInfo->keyLen != 4096) {
		LOGE("The %u bit keys are not supported\n", keyInfo->keyLen);
		return WRONG_DATA;
	}

	return NO_ERROR;
}

static int32_t getNextSKMKey(uint8_t** key, uint32_t* keyLen, uint8_t tag, uint8_t* blob, uint32_t blobLen)
{
	if (!key || !keyLen || !blob || !blobLen) {
		LOGE("Some data is invalid\n");
		return WRONG_DATA;
	}

	if (blobLen < TAGLENGTH_FIELD_SIZE) {
		return WRONG_DATA;
	}

	if (tag != blob[0]) {
		LOGE("Wrong tag field in blob, expecting %d\n", tag);
		return WRONG_DATA;
	}

	*keyLen = GET_UINT16T_LE(1, blob);
	*key = blob + TAGLENGTH_FIELD_SIZE;
		
	if (*keyLen > blobLen + TAGLENGTH_FIELD_SIZE) {
		LOGE("keyLen is invalid\n");
		return WRONG_DATA;
	}

	return NO_ERROR;
}

void TZ_LOG_HEX(const char *title, const unsigned char *data, int length)
{
	int i;
	char buffer[128] = { 0 };
	char dest_buffer[128]={ 0 };
	// print title
	LOGE("%s[%d] = ", title, length);
	// print binary data
	for (i = 0; i < length; i++) {
		if (i % 16 == 0) {
			LOGE("%s", buffer);
			buffer[0] = 0;
		}
		sprintf(dest_buffer, "%s%.2x ", buffer, data[i]);
		sprintf(buffer,"%s",dest_buffer);
	}
	sprintf(dest_buffer,"%s",buffer);
	sprintf(buffer, "%s", dest_buffer);
	LOGE("%s\n", buffer);
}

#if ((defined USE_QSEE) || (defined USE_SW_BASED_DRK))
int32_t verifyRSA_SK(KeyInfo_t *keyInfo, void *wrapped, uint32_t wrappedLen)
{
	int32_t res = PLATFORM_INTERNAL_ERROR;
	RSA *rsa_service = NULL;
	RSA *rsa_dev = NULL;

	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);

	uint8_t* certDRK;
	uint32_t certDRKLen;
	uint8_t* certML;
	uint32_t certMLLen;
	uint8_t* privML;
	uint32_t privMLLen;
	struct x509_certificate cert = {0};

	if (_validateKeyInfo(keyInfo) != NO_ERROR) {
		return WRONG_DATA;
	}

	rsa_service = mldap_RSA_new();
	rsa_dev = mldap_RSA_new();
	if (!rsa_service || !rsa_dev) {
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	res = loadMLDAPKeyBlob(wrapped, wrappedLen, keyInfo, blob, &blobLen);
	if (res != NO_ERROR) {
		LOGE("Failed to load key blob, res = %d\n", res);
		return res;
	}

	res = getSKMKeys(&certDRK, &certDRKLen, &certML, &certMLLen, &privML, &privMLLen, blob, blobLen);
	if (res != NO_ERROR) {
		LOGE("getSKMKeys failed\n");
		goto cleanup;
	}

	/* device root public key */
	res = x509_certificate_parse(certDRK, certDRKLen, &cert);
	if (res != X509_PARSE_OK) {
		LOGE("Device root certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}
	res = rsa_public_key_parse(cert.public_key, cert.public_key_len, rsa_dev);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		return PLATFORM_INTERNAL_ERROR;
	}

	/* check signature */
	res = checkRSACertificate(certML, certMLLen, rsa_dev);
	if (res != NO_ERROR) {
		LOGE("Verification of Auth RSA certificate failed! Error code: %d\n", res);
		goto cleanup;
	}

	/* service private key */
	if (rsa_private_key_parse(privML, privMLLen, rsa_service) != RSA_PARSE_OK) {
		LOGE("Service private key blob is wrong!\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	/* service public key */
	res = x509_certificate_parse(certML, certMLLen, &cert);
	if (res != X509_PARSE_OK) {
		LOGE("Service certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}
	res = rsa_public_key_parse(cert.public_key, cert.public_key_len, rsa_service);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		return WRONG_PRIV_KEY;
	}

	/* additional check that keypair is correct */
	if (mldap_rsa_check_keypair(rsa_service) != NO_ERROR) {
		LOGE("Service keypair is wrong!\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	res = NO_ERROR;

cleanup:
	/* sec_free() will zeroize RSA keys */
	if (rsa_dev)
		mldap_RSA_free(rsa_dev);
	if (rsa_service)
		mldap_RSA_free(rsa_service);

	return res;
}
#endif /* #if ((defined USE_QSEE) || (defined USE_SW_BASED_DRK)) */

/* Get SM/SD Certificate from SFS wrapped key blob file */
int32_t getMLDAPCert(certType_t certType, uint8_t *out, uint32_t *outLen, void* wrapped, uint32_t wrappedLen)
{
	int32_t res;

	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);

	uint8_t* certSM0;
	uint32_t certSM0Len;
	uint8_t* certSM1;
	uint32_t certSM1Len;
	uint8_t* certSD;
	uint32_t certSDLen;
	uint8_t* privSD = NULL;
	uint32_t privSDLen;

	KeyInfo_t keyInfo;

	if ((NULL == out) || (NULL == outLen)
#if !defined(USE_QSEE_WRAP_WITH_SFS)
		|| (NULL == wrapped) || (0 == wrappedLen)
#endif
	) {
		LOGE("Bad input data!\n");
		return WRONG_DATA;
	}

	/* Initialize Key Info for SD key */
	memset(&keyInfo, 0 , sizeof(keyInfo));
	memcpy(keyInfo.serviceName, "SD", sizeof ("SD"));

	res = loadMLDAPKeyBlob(wrapped, wrappedLen, &keyInfo, blob, &blobLen);
	if (res != NO_ERROR) {
		return res;
	}

	res = getMLDAPKeys(&certSM0, &certSM0Len, &certSM1, &certSM1Len, &certSD, &certSDLen, &privSD, &privSDLen, blob, blobLen);
	if (res != NO_ERROR) {
		return res;
	}

	/* Copy to TZ shared buffer */
	switch (certType)
	{
		case CERT_SM0:
			if (certSM0Len == 0) {
				LOGE("certSM0 is null !\n");
				memset(privSD, 0, privSDLen);
				return WRONG_DATA;
			}
			memcpy(out, certSM0, certSM0Len);
			*outLen = certSM0Len;
			break;
		case CERT_SM1:
			if (certSM1Len == 0) {
				LOGE("certSM1 is null !\n");
				memset(privSD, 0, privSDLen);
				return WRONG_DATA;
			}
			memcpy(out, certSM1, certSM1Len);
			*outLen = certSM1Len;
			break;
		case CERT_SD:
			if (certSDLen == 0) {
				LOGE("certSD is null !\n");
				memset(privSD, 0, privSDLen);
				return WRONG_DATA;
			}
			memcpy(out, certSD, certSDLen);
			*outLen = certSDLen;
			break;
		default:
			LOGE("Unknown cert type, should be either ML or DRK\n");
			res = PLATFORM_INTERNAL_ERROR;
	}

	if (privSD) {
		memset(privSD, 0, privSDLen);
	}

	return res;
}

/* Store certificates provided by NWD */
int32_t storeOTACerts(KeyInfo_t *keyInfo, void *wrappedOut, uint32_t *wrappedOutLen, void *wrappedIn, uint32_t wrappedInLen, uint8_t *TID, uint32_t TIDLen, uint8_t *attr, uint32_t attrLen)
{
	int32_t res;
	struct x509_certificate certCCC;
	struct x509_certificate certSD;
	struct x509_certificate certSM0;
	struct x509_certificate certSM1;
	uint8_t output[3*MAX_RSA_CERT_LEN + MAX_RSA_LEN + 4*TAGLENGTH_FIELD_SIZE];
	uint32_t outputLen = sizeof(output);
	RSA *rsaCCC = NULL;
	RSA *rsaSM0 = NULL;
	RSA *rsaSM1 = NULL;
	RSA *rsaSD = NULL;

	uint32_t cert_CCC_len = sizeof(cert_CCC);
	uint32_t cert_SM0_len;
	uint8_t* cert_SM0;
	uint32_t cert_SM1_len;
	uint8_t* cert_SM1;
	uint32_t cert_SD_len;
	uint8_t* cert_SD;

	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);
	uint8_t* privSD = NULL;
	uint32_t privSDLen;

	/* Validate func params */
	if ((NULL == keyInfo) || (NULL == wrappedOut) || (NULL == wrappedOutLen)
#if !defined(USE_QSEE_WRAP_WITH_SFS)
		|| (NULL == wrappedIn) || (0 == wrappedInLen)
#endif
		|| (NULL == attr) || (0 == attrLen) ) {
			LOGE("Bad input data!\n");
			res = PLATFORM_INTERNAL_ERROR;
			goto cleanup;
	}

	if (NO_ERROR != _validateKeyInfo(keyInfo)) {
		LOGE("Key Info is invalid!\n");
		return WRONG_DATA;
	}

	res = tlvGet(attr, attrLen, TLV_CERT_SM0, &cert_SM0_len, &cert_SM0);

	if (res != NO_ERROR) {
		LOGE("attr buffer doesn't have TLV_CERT_SM0\n");
		return WRONG_DATA;
	}

	res = tlvGet(attr, attrLen, TLV_CERT_SM1, &cert_SM1_len, &cert_SM1);

	if (res != NO_ERROR) {
		LOGE("attr buffer doesn't have TLV_CERT_SM1\n");
		return WRONG_DATA;
	}

	res = tlvGet(attr, attrLen, TLV_CERT_SD, &cert_SD_len, &cert_SD);

	if (res != NO_ERROR) {
		LOGE("attr buffer doesn't have TLV_CERT_SD\n");
		return WRONG_DATA;
	}

	/* --> Process CCC cert */
	/* Check CCC Certificate */
	res = x509_certificate_parse(cert_CCC, cert_CCC_len, &certCCC);
	if (res != X509_PARSE_OK) {
		LOGE("CCC certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	rsaCCC = mldap_RSA_new();
	if (!rsaCCC) {
		LOGE("Couldn't allocate RSA struct for CCC\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* Public CCC Key check */
	res = rsa_public_key_parse(certCCC.public_key, certCCC.public_key_len, rsaCCC);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() on CCC failed\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* --> Process SM0 cert */
	/* Check SM0 Certificate */
	res = x509_certificate_parse(cert_SM0, cert_SM0_len, &certSM0);
	if (res != X509_PARSE_OK) {
		LOGE("SM0 certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	rsaSM0 = mldap_RSA_new();
	if (!rsaSM0) {
		LOGE("Couldn't allocate RSA struct for SM0\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* Public SM Key check */
	res = rsa_public_key_parse(certSM0.public_key, certSM0.public_key_len, rsaSM0);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() on SM0 failed\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* check SM0 Certificate signature, using CCC */
	res = verify_cert_signature_rsa(&certSM0, rsaCCC);
	if (res != NO_ERROR) {
		LOGE("Verification of SM0 RSA certificate failed! Error code: %d\n", res);
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* --> Process SM1 cert */
	/* Check SM1 Certificate */
	res = x509_certificate_parse(cert_SM1, cert_SM1_len, &certSM1);
	if (res != X509_PARSE_OK) {
		LOGE("SM1 certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	rsaSM1 = mldap_RSA_new();
	if (!rsaSM1) {
		LOGE("Couldn't allocate RSA struct for SM1\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* Public SM Key check */
	res = rsa_public_key_parse(certSM1.public_key, certSM1.public_key_len, rsaSM1);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() on SM1 failed\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* check SM1 Certificate signature, using SM0 */
	res = verify_cert_signature_rsa(&certSM1, rsaSM0);
	if (res != NO_ERROR) {
		LOGE("Verification of SM1 RSA certificate failed! Error code: %d\n", res);
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	res = x509_certificate_parse(cert_SD, cert_SD_len, &certSD);
	if (res != X509_PARSE_OK) {
		LOGE("cert_SD certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	res = verify_cert_signature_rsa(&certSD, rsaSM1);
	if (res != NO_ERROR) {
		LOGE("Verification of Auth RSA certificate failed! Error code: %d\n", res);
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	res = loadMLDAPKeyBlob(wrappedIn, wrappedInLen, keyInfo, blob, &blobLen);
	if (res != NO_ERROR) {
		goto cleanup;
	}

	res = getMLDAPKeys(NULL, 0, NULL, 0, NULL, 0, &privSD, &privSDLen, blob, blobLen);
	if (res != NO_ERROR) {
		goto cleanup;
	}

	rsaSD = mldap_RSA_new();
	if (!rsaSD) {
		LOGE("Couldn't allocate RSA struct\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* Private SD Key check */
	if (rsa_private_key_parse(privSD, privSDLen, rsaSD) != RSA_PARSE_OK) {
		LOGE("Service private key blob is wrong!\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	res = rsa_public_key_parse(certSD.public_key, certSD.public_key_len, rsaSD);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	/* SD key pair check that it is valid */
	if (mldap_rsa_check_keypair(rsaSD) != NO_ERROR) {
		LOGE("Service keypair is wrong!\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	/* Save SM0, SM1, SD, privSD to encrypted blob */
	if (tlvInit(output, outputLen) != NO_ERROR) {
		LOGE("tlvInit(output, outputLen)\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	if (tlvAdd(output, outputLen, TLV_CERT_SM0, cert_SM0, cert_SM0_len) != NO_ERROR ||
		tlvAdd(output, outputLen, TLV_CERT_SM1, cert_SM1, cert_SM1_len) != NO_ERROR ||
		tlvAdd(output, outputLen, TLV_CERT_SD, cert_SD, cert_SD_len) != NO_ERROR ||
		tlvAdd(output, outputLen, TLV_PRIV_SD, privSD, privSDLen) != NO_ERROR)	{
			LOGE("tlvAdd TLV_CERT_SM0 | TLV_CERT_SM1 | TLV_CERT_SD | TLV_PRIV_SD error\n");
			res = PLATFORM_INTERNAL_ERROR;
			goto cleanup;
	}

	outputLen = tlvSize(output, outputLen);
	res = saveKeyBlob(output, outputLen, keyInfo, wrappedOut, wrappedOutLen, TID, TIDLen);

cleanup:
	if (rsaCCC)
		mldap_RSA_free(rsaCCC);
	if (rsaSM0)
		mldap_RSA_free(rsaSM0);
	if (rsaSM1)
		mldap_RSA_free(rsaSM1);
	if (rsaSD)
		mldap_RSA_free(rsaSD);

	if (privSD) {
		memset(privSD, 0, privSDLen);
	}
	if (blob) {
		memset(blob, 0, blobLen);
	}

	return res;
}

int32_t verifySDKey(void *wrapped, uint32_t wrappedLen, uint8_t *timeData, uint32_t timeDataLen)
{
	int32_t res = PLATFORM_INTERNAL_ERROR;
	RSA *rsa_sd = NULL;
	RSA *rsa_sm1 = NULL;

	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);

	uint8_t* certSM0;
	uint32_t certSM0Len;
	uint8_t* certSM1;
	uint32_t certSM1Len;
	uint8_t* certSD;
	uint32_t certSDLen;
	uint8_t* privSD = NULL;
	uint32_t privSDLen;

	struct x509_certificate x509_cert = {0};
	KeyInfo_t keyInfo;

	rsa_sd = mldap_RSA_new();
	rsa_sm1 = mldap_RSA_new();
	if (!rsa_sd || !rsa_sm1) {
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	strncpy((char*)keyInfo.serviceName, "SD", strlen("SD"));
	keyInfo.serviceName[2] = '\0';

	res = loadMLDAPKeyBlob(wrapped, wrappedLen, &keyInfo, blob, &blobLen);
	if (res != NO_ERROR) {
		goto cleanup;
	}

	res = getMLDAPKeys(&certSM0, &certSM0Len, &certSM1, &certSM1Len, &certSD, &certSDLen, &privSD, &privSDLen, blob, blobLen);
	if (res != NO_ERROR) {
		goto cleanup;
	}

	/* device root public key */
	res = x509_certificate_parse(certSM1, certSM1Len, &x509_cert);
	if (res != X509_PARSE_OK) {
		LOGE("Device root certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	LOGE("SM certificate validation...\n");
	res = asn1_check_cert_expiration_time(&x509_cert, timeData, timeDataLen);
	if (res != NO_ERROR) {
		LOGE("check_cert_expiration_time() failed\n");
		return CERT_EXPIRATION_TIME_ERROR;
	}

	res = rsa_public_key_parse(x509_cert.public_key, x509_cert.public_key_len, rsa_sm1);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		return PLATFORM_INTERNAL_ERROR;
	}

	/* check signature */
	res = checkRSACertificate(certSD, certSDLen, rsa_sm1);
	if (res != NO_ERROR) {
		LOGE("Verification of Auth RSA certificate failed! Error code: %d\n", res);
		goto cleanup;
	}

	/* service private key */
	if (rsa_private_key_parse(privSD, privSDLen, rsa_sd) != RSA_PARSE_OK) {
		LOGE("Service private key blob is wrong!\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	/* service public key */
	res = x509_certificate_parse(certSD, certSDLen, &x509_cert);
	if (res != X509_PARSE_OK) {
		LOGE("Service certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	LOGE("SD certificate validation...\n");
	res = asn1_check_cert_expiration_time(&x509_cert, timeData, timeDataLen);
	if (res != NO_ERROR) {
		LOGE("check_cert_expiration_time() failed\n");
		return CERT_EXPIRATION_TIME_ERROR;
	}

	res = rsa_public_key_parse(x509_cert.public_key, x509_cert.public_key_len, rsa_sd);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		return WRONG_PRIV_KEY;
	}

	/* additional check that keypair is correct */
	if (mldap_rsa_check_keypair(rsa_sd) != NO_ERROR) {
		LOGE("Service keypair is wrong!\n");
		res =  WRONG_PRIV_KEY;
		goto cleanup;
	}

	LOGD("SD key is correct!\n");
	res = NO_ERROR;

cleanup:
	/* sec_free() will zeroize RSA keys */
	if (rsa_sm1)
		mldap_RSA_free(rsa_sm1);
	if (rsa_sd)
		mldap_RSA_free(rsa_sd);

	if (privSD) {
		memset(privSD, 0, privSDLen);
	}
	if (blob) {
		memset(blob, 0, blobLen);
	}

	return res;
}

#ifdef USE_QSEE
int32_t storeServiceKey(void *wrapped, uint32_t wrappedLen, uint8_t* outData, uint32_t* outDataLen){
	int32_t ret = NO_ERROR;
	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);
	KeyInfo_t keyInfo;

	strncpy((char*)keyInfo.serviceName, "MLDAP", strlen("MLDAP"));
	keyInfo.serviceName[5] = '\0';

	// Load ServiceKey blob from wrappedkey
	ret = loadSKMKeyBlob(wrapped, wrappedLen, &keyInfo, blob, &blobLen);
	if (ret != NO_ERROR) {
		LOGE("Failed to load ServiceKey blob. ret = 0x%x!\n", ret);
		goto cleanUp;
	}

	strncpy((char*)keyInfo.serviceName, "SK", strlen("SK"));
	keyInfo.serviceName[2] = '\0';
	// Store Service key blob via SFS
	ret = saveKeyBlob(blob, blobLen, &keyInfo, outData, outDataLen, NULL, 0);
	if (ret != NO_ERROR) {
		LOGE("Failed to store ServiceKey!\n");
		goto cleanUp;
	}
	LOGD("SFS: Save ServiceKey blob done!\n");
cleanUp:
	return ret;
}
#endif /* USE_QSEE */

/* Verify ServiceKey from DRK */
int32_t verifyServiceKey(void *wrapped, uint32_t wrappedLen, uint8_t *timeData, uint32_t timeDataLen)
{
	int32_t ret = NO_ERROR;
	struct x509_certificate x509_cert = {0};
	KeyInfo_t keyInfo;

	RSA *rsa_sk = NULL;
	RSA *rsa_drk = NULL;

	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);

	uint8_t* certDRK;
	uint32_t certDRKLen;
	uint8_t* certSK;
	uint32_t certSKLen;
	uint8_t* privSK = NULL;
	uint32_t privSKLen;

	rsa_sk = mldap_RSA_new();
	rsa_drk = mldap_RSA_new();

    // Load ServiceKey blob from wrappedkey(MC) or via SFS (QSEE)
#ifdef USE_QSEE
	strncpy((char*)keyInfo.serviceName, "SK", strlen("SK"));
	keyInfo.serviceName[2] = '\0';
	ret = loadMLDAPKeyBlob(wrapped, wrappedLen, &keyInfo, blob, &blobLen);
	if (ret != NO_ERROR) {
		goto cleanUp;
	}
#else
	strncpy((char*)keyInfo.serviceName, "MLDAP", strlen("MLDAP"));
	keyInfo.serviceName[5] = '\0';
	ret = loadSKMKeyBlob(wrapped, wrappedLen, &keyInfo, blob, &blobLen);
	if (ret != NO_ERROR) {
		goto cleanUp;
	}
#endif /* USE_QSEE */
	// Parse ServiceKey blob to get certs
	ret = getSKMKeys(&certDRK, &certDRKLen, &certSK, &certSKLen, &privSK, &privSKLen, blob, blobLen);

	if (ret != NO_ERROR) {
		goto cleanUp;
	}

	/* Verify ServiceKey Certificate*/
	/* Parsing DRK Cert */
	if ((ret = x509_certificate_parse(certDRK, certDRKLen, &x509_cert)) != X509_PARSE_OK) {
		LOGE("DRK certificate is invalid\n");
		goto cleanUp;
	}

	LOGD("DRK certificate validation...\n");

	if ((ret= asn1_check_cert_expiration_time(&x509_cert, timeData, timeDataLen)) != NO_ERROR) {
		LOGE("check_cert_expiration_time() failed\n");
		goto cleanUp;
	}
	/* Get DRK Public key */
	if ((ret = rsa_public_key_parse(x509_cert.public_key, x509_cert.public_key_len, rsa_drk)) != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		return PLATFORM_INTERNAL_ERROR;
	}

	/* check signature of ServiceKey Cert */
	if ((ret = checkRSACertificate(certSK, certSKLen, rsa_drk)) != NO_ERROR) {
		LOGE("Verification of Auth RSA certificate failed! Error code: %d\n", ret);
		goto cleanUp;
	}
	/* End Verify ServiceKey Certificate*/

	/* Verify ServiceKey Private Key*/
	/* Get ServiceKey Private key */
	if ((ret = rsa_private_key_parse(privSK, privSKLen, rsa_sk)) != RSA_PARSE_OK) {
		LOGE("Service private key blob is wrong!\n");
		ret = WRONG_PRIV_KEY;
		goto cleanUp;
	}

	/* Parsing ServiceKey Cert */
	if ((ret = x509_certificate_parse(certSK, certSKLen, &x509_cert)) != X509_PARSE_OK) {
		LOGE("Service certificate is invalid\n");
		ret = PLATFORM_INTERNAL_ERROR;
		goto cleanUp;
	}

	LOGD("ServiceKey certificate validation...\n");

	if ((ret = asn1_check_cert_expiration_time(&x509_cert, timeData, timeDataLen)) != NO_ERROR) {
		LOGE("check_cert_expiration_time() failed\n");
		ret = CERT_EXPIRATION_TIME_ERROR;
		goto cleanUp;
	}

	if ((ret = rsa_public_key_parse(x509_cert.public_key, x509_cert.public_key_len, rsa_sk)) != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		ret = WRONG_PRIV_KEY;
		goto cleanUp;
	}
	/* additional check that keypair is correct */
	if (mldap_rsa_check_keypair(rsa_sk) != NO_ERROR) {
		LOGE("Service keypair is wrong!\n");
		ret = WRONG_PRIV_KEY;
		goto cleanUp;
	}
	/* End Verify ServiceKey Private Key*/

	LOGD("ServiceKey is correct!\n");
	ret = NO_ERROR;

cleanUp:
	if (rsa_drk)
		mldap_RSA_free(rsa_drk);
	if (rsa_sk)
		mldap_RSA_free(rsa_sk);

	if (privSK) {
		memset(privSK, 0, privSKLen);
	}
	 if (blob) {
		memset(blob, 0, blobLen);
	}

	return ret;
}

/* Sign data with key corresponded to declared certificate */
int32_t signDataWithKey(KeyInfo_t *keyInfo, void *signData, uint32_t *signDataLen, void *wrappedIn, uint32_t wrappedInLen, uint8_t *TID, uint32_t TIDLen, uint8_t *attr, uint32_t attrLen)
{
	int32_t  res = NO_ERROR;
	KeyInfo_t ki;

	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);

	uint8_t* cert;
	uint32_t certLen;
	uint8_t* key = NULL;
	uint32_t keyLen;

	struct x509_certificate certX509 = {0};

	uint32_t blobDataLen = 0;
	uint8_t *blobData = NULL;
	RSA *rsaKey = NULL;
	unsigned char sha256_digest[SHA256_DIGEST_LEN] = {0};
	unsigned char sha1_digest[SHA1_DIGEST_LEN] = {0};

	(void)TID;
	(void)TIDLen;

	/* Validate func params */
	if ((NULL == keyInfo) || (NULL == signData) || (NULL == signDataLen)
#if !defined(USE_QSEE_WRAP_WITH_SFS)
		|| (NULL == wrappedIn) || (0 == wrappedInLen)
#endif
		|| (NULL == attr) || (0 == attrLen)) {
			LOGE("Bad input data!\n");
			res = PLATFORM_INTERNAL_ERROR;
			goto cleanup;
	}

	res = tlvGet(attr, attrLen, TLV_SIGN_DATA_BLOB, &blobDataLen, &blobData);

	if (res != NO_ERROR) {
		LOGE("attr buffer is not data blob for signing\n");
		return WRONG_DATA;
	}

	/* Get needed cert and key */
	switch (keyInfo->crt)
	{
		case CERT_ML:
		#ifdef USE_QSEE
			strncpy((char*)ki.serviceName, "SK", strlen("SK"));
			ki.serviceName[2] = '\0';
			res = loadMLDAPKeyBlob(wrappedIn, wrappedInLen, &ki, blob, &blobLen);
		#else
			res = loadSKMKeyBlob(wrappedIn, wrappedInLen, keyInfo, blob, &blobLen);
		#endif /* USE_QSEE */
			if (res != NO_ERROR) {
				goto cleanup;
			}
			res = getSKMKeys(&cert, &certLen, &cert, &certLen, &key, &keyLen, blob, blobLen);
		break;
		case CERT_SD:
			res = loadMLDAPKeyBlob(wrappedIn, wrappedInLen, keyInfo, blob, &blobLen);
			if (res != NO_ERROR) {
				goto cleanup;
			}
			res = getMLDAPKeys(NULL, 0, NULL, 0, &cert, &certLen, &key, &keyLen, blob, blobLen);
			break;
		default:
			LOGE("Unknown cert type, should be either ML or DRK\n");
			res = PLATFORM_INTERNAL_ERROR;
			goto cleanup;
	}

	if (res != NO_ERROR) {
		LOGE("Error while extracting key!\n");
		goto cleanup;
	}

	rsaKey = mldap_RSA_new();
	if (!rsaKey) {
		LOGE("Couldn't allocate RSA struct\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* Private Key check */
	if (rsa_private_key_parse(key, keyLen, rsaKey) != RSA_PARSE_OK) {
		LOGE("SKM private key blob is wrong!\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	/* Prepare for check key pair */
	res = x509_certificate_parse(cert, certLen, &certX509);
	if (res != X509_PARSE_OK) {
		LOGE("Service certificate is invalid\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}
	res = rsa_public_key_parse(certX509.public_key, certX509.public_key_len, rsaKey);
	if (res != RSA_PARSE_OK) {
		LOGE("rsa_public_key_parse() failed\n");
		res = WRONG_PRIV_KEY;
		goto cleanup;
	}

	switch (keyInfo->crt)
	{
		case CERT_ML:
			/* Make data hash */
			if (getSHA256Digest(blobData, blobDataLen, sha256_digest) != NO_ERROR) {
				LOGE("Cannot calculate sha256 digest!\n");
				res = PLATFORM_INTERNAL_ERROR;
				goto cleanup;
			}

			/* Make message signature */
			if (mldap_rsa_pkcs1_sign(rsaKey, RSA_SHA256, SHA256_DIGEST_LEN, sha256_digest, signData) != NO_ERROR) {
				LOGE("Cannot sign data block!\n");
				res = PLATFORM_INTERNAL_ERROR;
				goto cleanup;
			}

			break;
		case CERT_SD:
			/* Make data hash */
			if (getSHA1Digest(blobData, blobDataLen, sha1_digest) != NO_ERROR) {
				LOGE("Cannot calculate sha1 digest!\n");
				res = PLATFORM_INTERNAL_ERROR;
				goto cleanup;
			}
			/* Make message signature */
			if (mldap_rsa_pkcs1_sign(rsaKey, RSA_SHA1, SHA1_DIGEST_LEN, sha1_digest, signData) != NO_ERROR) {
				LOGE("Cannot sign data block!\n");
				res = PLATFORM_INTERNAL_ERROR;
				goto cleanup;
			}

			break;
	}

	*signDataLen = BN_num_bytes(rsaKey->n);

cleanup:
	if (rsaKey)
		mldap_RSA_free(rsaKey);

	if (key) {
		memset(key, 0 , keyLen);
	}
	if (blob) {
		memset(blob, 0 , blobLen);
	}

	return res;
}

int32_t getOTA_SD_PK(KeyInfo_t *keyInfo, void *wrappedOut, uint32_t *wrappedOutLen, void *wrappedDevice, uint32_t wrappedDeviceLen, uint8_t *TID, uint32_t TIDLen, uint8_t *attr, uint32_t attrLen)
{
	int32_t res = PLATFORM_INTERNAL_ERROR;
	unsigned long exponent = 0;
	RSA *rsa_SD = NULL;
	uint8_t pub_key_SD[MAX_RSA_CERT_LEN] = {0};
	uint32_t pub_key_SD_len = sizeof(pub_key_SD);
	uint8_t priv_key_SD[MAX_RSA_LEN] = {0};
	uint32_t priv_key_SD_len = sizeof(priv_key_SD);
	uint8_t output[MAX_RSA_LEN + 2*TAGLENGTH_FIELD_SIZE] = {0};
	uint32_t outputLen = sizeof(output);
	uint8_t* outPtr = wrappedOut;

	/* Not used */
	(void)wrappedDevice;
	(void)wrappedDeviceLen;

	/* Validate func params */
	if ((NULL == keyInfo) || (NULL == wrappedOut) || (NULL == wrappedOutLen)) {
		LOGE("Bad input data!");
		return PLATFORM_INTERNAL_ERROR;
	}

	if (NO_ERROR != _validateKeyInfo(keyInfo)) {
		LOGE("Key Info is invalid!\n");
		return WRONG_DATA;
	}

#ifdef HARDCODED_SD_KEY
	/* Not used */
	(void)exponent;

	memcpy(pub_key_SD, SD_PUB, sizeof(SD_PUB));
	pub_key_SD_len = sizeof(SD_PUB);
	memcpy(priv_key_SD, SD_PRIV, sizeof(SD_PRIV));
	priv_key_SD_len = sizeof(SD_PRIV);
#else
	rsa_SD = mldap_RSA_new();
	if (!rsa_SD) {
		LOGE("Couldn't init RSA contexts\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	exponent = mldap_rsa_get_exponent();
	res = mldap_rsa_gen_key(rsa_SD, keyInfo->keyLen, exponent);

	if (res != NO_ERROR) {
		LOGE("Error while generation of RSA key pair! Error code: %d\n", res);
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}
	LOGD("RSA keypair is generated\n");

#ifdef RUN_FUNC_TESTS
	/* self-check of rsa keypair */
	if (mldap_rsa_check_keypair(rsa_SD) != NO_ERROR) {
		LOGE("Generated service keypair is wrong\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}
	LOGD("Generated service keypair is valid\n");
#endif /* RUN_FUNC_TESTS */

	/* ASN.1 representation of service public key */
	res = asn1_build_pub_rsa(rsa_SD, pub_key_SD, &pub_key_SD_len);
	if (res < 0) {
		LOGE("Error while building ASN.1 of public key\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	/* ASN.1 representation of service private key */
	res = asn1_build_pri_rsa(priv_key_SD, &priv_key_SD_len, rsa_SD);
	if (res != NO_ERROR) {
		LOGE("Error while building ASN.1 of private key\n");
		goto cleanup;
	}

#ifdef RUN_FUNC_TESTS
	if (rsa_private_key_parse(priv_key_SD, priv_key_SD_len, rsa_SD) != RSA_PARSE_OK) {
		LOGE("Generated ASN.1 private RSA key is invalid!\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}
	LOGD("Generated ASN.1 private RSA key is correct\n");
#endif /* RUN_FUNC_TESTS */

#endif /* HARDCODED_SD_KEY */

	/* Save public key */
	*(uint32_t*)outPtr = pub_key_SD_len;
	outPtr += sizeof(uint32_t);
	memcpy(outPtr, pub_key_SD, pub_key_SD_len);
	outPtr += pub_key_SD_len;
	*wrappedOutLen -= (outPtr - (uint8_t*)wrappedOut);
	LOGD("written pub_key_SD %d bytes\n", pub_key_SD_len);

	/* Save privSD to encrypted blob */
	if (tlvInit(output, outputLen) != NO_ERROR) {
		LOGE("tlvInit(output, outputLen)\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	if (tlvAdd(output, outputLen, TLV_PRIV_SD, priv_key_SD, priv_key_SD_len) != NO_ERROR) {
		LOGE("tlvAdd TLV_PRIV_SD error\n");
		res = PLATFORM_INTERNAL_ERROR;
		goto cleanup;
	}

	outputLen = tlvSize(output, outputLen);

	res = saveKeyBlob(output, outputLen, keyInfo, outPtr, wrappedOutLen, TID, TIDLen);
	if (res != NO_ERROR) {
		goto cleanup;
	}

	LOGD("written rawServiceBlob %d bytes\n", *wrappedOutLen);

	/* calculate final size */
	outPtr += *wrappedOutLen;
	*wrappedOutLen = (outPtr - (uint8_t*)wrappedOut);
	LOGD("total bytes written %d\n", *wrappedOutLen);

cleanup:
	if (rsa_SD)
		mldap_RSA_free(rsa_SD);

	memset(priv_key_SD, 0 , priv_key_SD_len);

	return res;
}

#ifndef USE_BLOWFISH
int32_t getSHA512Digest(uint8_t* data, uint32_t len, uint8_t* digest)
{
	int32_t status = PLATFORM_INTERNAL_ERROR;
	SHA512_CTX sha512_ctx = {0};
	unsigned char tmp_digest[SHA512_DIGEST_LENGTH] = {0};

	if (!data && !len && !digest) {
		return WRONG_DATA;
	}
	if (SHA512_Init( &sha512_ctx )) {
		if (SHA512_Update(&sha512_ctx, data, len)) {
			if (SHA512_Final(tmp_digest, &sha512_ctx)) {
				memcpy( digest, tmp_digest, sizeof(tmp_digest) );
				memset( tmp_digest, 0, sizeof(tmp_digest) );
				status = NO_ERROR;
			}
		}
		sec_cleanse( &sha512_ctx, sizeof(sha512_ctx) );
	}
	return status;
}
#endif

int32_t getMLDAPKeys(uint8_t** certSM0, uint32_t* certSM0Len, uint8_t** certSM1, uint32_t* certSM1Len, uint8_t** certSD, uint32_t* certSDLen, uint8_t** privSD, uint32_t* privSDLen, uint8_t* blob, uint32_t blobLen)
{
	int32_t res = NO_ERROR;

	if (certSM0 && certSM0Len) {
		res = tlvGet(blob, blobLen, TLV_CERT_SM0, certSM0Len, certSM0);
		if (res != NO_ERROR) {
			return res;
		}
	}

	if (certSM1 && certSM1Len) {
		res = tlvGet(blob, blobLen, TLV_CERT_SM1, certSM1Len, certSM1);
		if (res != NO_ERROR) {
			return res;
		}
	}

	if (certSD && certSDLen) {
		res = tlvGet(blob, blobLen, TLV_CERT_SD, certSDLen, certSD);
		if (res != NO_ERROR) {
			return res;
		}
	}

	if (privSD && privSDLen) {
		res = tlvGet(blob, blobLen, TLV_PRIV_SD, privSDLen, privSD);
	}

	return res;
}

int32_t getSKMKeys(uint8_t** devCert, uint32_t* devCertLen, uint8_t** cert, uint32_t* certLen, uint8_t** prk, uint32_t* prkLen, uint8_t* blob, uint32_t blobLen)
{
	int res = getNextSKMKey(devCert, devCertLen, RSA_CERT_TAG, blob, blobLen);
	if (res != NO_ERROR) {
		return res;
	}

	blob += *devCertLen + TAGLENGTH_FIELD_SIZE;
	if (*devCertLen + TAGLENGTH_FIELD_SIZE > blobLen ) {
		return WRONG_DATA;
	}
	
	blobLen -= *devCertLen + TAGLENGTH_FIELD_SIZE;
	if (blobLen < TAGLENGTH_FIELD_SIZE || blobLen > MAX_TRANSFER_SIZE) {
		return WRONG_DATA;
	}

	res = getNextSKMKey(cert, certLen, RSA_CERT_TAG, blob, blobLen);
	if (res != NO_ERROR) {
		return res;
	}

	blob += *certLen + TAGLENGTH_FIELD_SIZE;
	if (*certLen + TAGLENGTH_FIELD_SIZE > MAX_TRANSFER_SIZE ) {
		return WRONG_DATA;
	}
	
	blobLen -= *certLen + TAGLENGTH_FIELD_SIZE;
	if (blobLen < TAGLENGTH_FIELD_SIZE || blobLen > MAX_TRANSFER_SIZE) {
		return WRONG_DATA;
	}

	res = getNextSKMKey(prk, prkLen, KEY_TAG, blob, blobLen);

	return res;
}

int32_t getSKMCert(certType_t certType, uint8_t *out, uint32_t *outLen, void* wrapped, uint32_t wrappedLen)
{
	int32_t res = PLATFORM_INTERNAL_ERROR;
	uint8_t blob[MAX_TRANSFER_SIZE];
	uint32_t blobLen = sizeof(blob);

	uint8_t* certDRK;
	uint32_t certDRKLen;
	uint8_t* certML;
	uint32_t certMLLen;
	uint8_t* privML = NULL;
	uint32_t privMLLen;

	KeyInfo_t keyInfo;

#ifdef USE_QSEE
	strncpy((char*)keyInfo.serviceName, "MLDAP", sizeof("MLDAP"));
	strncpy((char*)keyInfo.serviceName, "SK", strlen("SK"));
	keyInfo.serviceName[2] = '\0';
	// load ServiceKey blob via SFS
	res = loadMLDAPKeyBlob(wrapped, wrappedLen, &keyInfo, blob, &blobLen);
	if (res != NO_ERROR) {
		LOGE("Cannot read ServiceKey blob via SFS\n");
		return res;
	}
#else
	res = loadSKMKeyBlob(wrapped, wrappedLen, &keyInfo, blob, &blobLen);
	if (res != NO_ERROR) {
		LOGE("Failed to load key blob, res = %d\n", res);
		return res;
	}
#endif /* USE_QSEE */

	res = getSKMKeys(&certDRK, &certDRKLen, &certML, &certMLLen, &privML, &privMLLen, blob, blobLen);
	if (res != NO_ERROR) {
		LOGE("Failed to get SKM keys, res = %d\n", res);
		return res;
	}

	switch(certType)
	{
		case CERT_DRK:
			if (certDRKLen > MAX_TRANSFER_SIZE) {
				LOGE("certDRKLen is out of size , res = %d\n", res);
				return WRONG_DATA;;
			}
			memcpy(out, certDRK, certDRKLen);
			*outLen = certDRKLen;
			break;
		case CERT_ML:
			if (certMLLen > MAX_TRANSFER_SIZE) {
				LOGE("certMLLen is out of size , res = %d\n", res);
				return WRONG_DATA;;
			}
			memcpy(out, certML, certMLLen);
			*outLen = certMLLen;
			break;
		default:
			LOGE("Unknown cert type %d\n", certType);
			res = PLATFORM_INTERNAL_ERROR;
			break;
	}

	if (blob) {
		memset(blob, 0 , blobLen);
	}
	if (privML) {
		memset(privML, 0 , privMLLen);
	}

	return res;
}
