5.7.32. Secure Authenticator (Qi) Authentication demo¶
This project is used to demonstrate the Qi authentication flow between a Power Transmitter and
a Power Receiver. The Power Transmitter implements 3 functions for the 3 authentication requests
a Receiver can issue to the Transmitter : GetCertificateChainDigest
, ReadCertificates
, Authenticate
.

5.7.32.1. Pre-requisites¶
The secure element should be trust provisioned with correct keys and certificates for Qi Authentication. Keys and certificates can be provisioned for test purpose by updating keys in
demos/se05x/sa_qi_provisioning/sa_qi_credentials.c
and running example Secure Authenticator (Qi) Provisioning demo.By default WPC Root certificate is used in the certificate chain. If example Secure Authenticator (Qi) Provisioning demo is run, you would need to disable macro
USE_ROOT_WPCCA
insa_qi_rootcert.c
to use test RootCA:#ifndef USE_ROOT_WPCCA #define USE_ROOT_WPCCA 1 #endif
5.7.32.2. GetCertificateChainDigest (GET_DIGESTS)¶
This function reads the digests of certificate chains stored inside the secure element and returns all the digests as requested by the Power Receiver.
void GetCertificateChainDigest(const uint8_t *pGetDigestRequest,
const size_t getDigestRequestLen,
uint8_t *pDigestResponse,
size_t *pDigestResponseLen)
{
smStatus_t retStatus = SM_NOT_OK;
uint32_t certChainId = 0;
uint16_t objectSize = 0;
size_t readSize = 0;
uint8_t *pData = NULL;
pSe05xSession_t session_ctx = pgSe05xSessionctx;
uint8_t *pDigestPtr = NULL;
qi_error_code_t errorCode = kQiErrorUnspecified;
uint8_t authMsgHeader = 0;
uint8_t requestedSlotMask = 0;
uint8_t slotsPopulated = 0x00;
uint8_t slotsReturned = 0x00;
uint8_t slotsReturnedCount = 0;
if (NULL == pGetDigestRequest || NULL == pDigestResponse) {
LOG_E("Null buffer");
errorCode = kQiErrorInvalidRequest;
goto error;
}
authMsgHeader = pGetDigestRequest[0];
if (getDigestRequestLen != GET_DIGESTS_CMD_LEN) {
LOG_E("Invalid request length");
errorCode = kQiErrorInvalidRequest;
goto error;
}
requestedSlotMask = pGetDigestRequest[1] & 0x0F;
/* Invalid slot requested */
if (requestedSlotMask == 0) {
errorCode = kQiErrorInvalidRequest;
LOG_E("No slot requested");
goto error;
}
/* Get all populated slots */
retStatus = getPopulatedSlots(session_ctx, &slotsPopulated);
if (SM_OK != retStatus) {
errorCode = kQiErrorUnspecified;
LOG_E("Failed to retrieve populated slots");
goto error;
}
/* Is Slot 0 empty? - Return error as slot 0 must always be populated */
if (!(slotsPopulated & 0x01)) {
errorCode = kQiErrorUnspecified;
goto error;
}
/* We will return slots which were requested AND are provisioned */
slotsReturned = requestedSlotMask & slotsPopulated;
pDigestPtr = &pDigestResponse[2];
/* Validate response buffer size */
for (size_t i = 0; i < MAX_SLOTS; i++) {
if ((slotsReturned) & (0x01 << i)) {
slotsReturnedCount++;
}
}
if (*pDigestResponseLen < (size_t)((slotsReturnedCount * DIGEST_SIZE_BYTES) + 2)) {
/* Response buffer size too less */
errorCode = kQiErrorUnspecified;
goto error;
}
pData = (uint8_t *)SSS_MALLOC(slotsReturnedCount * DIGEST_SIZE_BYTES);
if (!pData) {
errorCode = kQiErrorUnspecified;
goto error;
}
for (size_t i = 0; i < MAX_SLOTS; i++) {
if ((slotsReturned) & (0x01 << i)) {
certChainId = QI_SLOT_ID_TO_CERT_ID(i);
/* Read size of object to allocate necessary memory
* so that we can read the complete object */
retStatus = Se05x_API_ReadSize(session_ctx, certChainId, &objectSize);
if (retStatus != SM_OK) {
errorCode = kQiErrorUnspecified;
LOG_E("Failed Se05x_API_ReadSize");
goto error;
}
/* Size of binary object cannot be less than Digest size */
if (objectSize < DIGEST_SIZE_BYTES) {
errorCode = kQiErrorUnspecified;
goto error;
}
readSize = DIGEST_SIZE_BYTES;
retStatus = Se05x_API_ReadObject(session_ctx, certChainId, 0, DIGEST_SIZE_BYTES, pData, &readSize);
if (retStatus != SM_OK) {
errorCode = kQiErrorUnspecified;
LOG_E("Failed Se05x_API_ReadObject");
goto error;
}
memcpy(pDigestPtr, pData, DIGEST_SIZE_BYTES);
pDigestPtr += DIGEST_SIZE_BYTES;
}
}
/* Successfully filled all digests - fill response buffer header now */
pDigestResponse[0] = (uint8_t)(authMsgHeader & 0xF0) + (uint8_t)(kQiResponseDigest);
pDigestResponse[1] = (uint8_t)(slotsPopulated << 4) + (uint8_t)(slotsReturned);
*pDigestResponseLen = (slotsReturnedCount * DIGEST_SIZE_BYTES) + 2;
if (pData) {
SSS_FREE(pData);
}
return;
error:
if (pData) {
SSS_FREE(pData);
}
if (pDigestResponse) {
pDigestResponse[0] = (uint8_t)(authMsgHeader & 0xF0) + (uint8_t)(kQiResponseError);
pDigestResponse[1] = (uint8_t)(errorCode);
pDigestResponse[2] = 0x00;
*pDigestResponseLen = 3;
}
}
5.7.32.3. ReadCertificates (GET_CERTIFICATE)¶
This function reads the certificate chain on the provided slot ID starting from the provided offset and reading provided length bytes.
If the provided offset exceeds 0x600
then that indicates the power
transmitter to offset from the Product Unit Certificate. Otherwise the offset
starts from the beginning of the certificate chain.
void ReadCertificates(const uint8_t *pGetCertificateRequest,
const size_t getCertificateRequestLen,
uint8_t *pCertificateResponse,
size_t *pCertificateResponseLen)
{
smStatus_t retStatus = SM_NOT_OK;
uint16_t objectSize = 0;
pSe05xSession_t session_ctx = pgSe05xSessionctx;
uint32_t certChainId = 0;
size_t readSize = 0;
uint8_t *pData = NULL;
qi_error_code_t errorCode = kQiErrorUnspecified;
uint8_t authMsgHeader = 0;
uint8_t requestedslots = 0;
uint8_t certificateOffsetA8 = 0;
uint8_t certificateOffset70 = 0;
uint8_t certificateLengthA8 = 0;
uint8_t certificateLength70 = 0;
uint16_t certificateOffset = 0;
uint16_t certificatelength = 0;
/* Offset first DIGEST bytes to skip certificate chain hash */
uint16_t offset = DIGEST_SIZE_BYTES;
uint16_t N_MC = 0;
uint16_t bytesToRead = 0;
if (NULL == pGetCertificateRequest || NULL == pCertificateResponse) {
LOG_E("Null buffer");
errorCode = kQiErrorInvalidRequest;
goto error;
}
authMsgHeader = pGetCertificateRequest[0];
if (getCertificateRequestLen != GET_CERTIFICATE_CMD_LEN) {
LOG_E("Invalid request length");
errorCode = kQiErrorInvalidRequest;
goto error;
}
requestedslots = pGetCertificateRequest[1] & 0x03;
certificateOffsetA8 = (pGetCertificateRequest[1] & 0xE0) >> 5;
certificateOffset70 = pGetCertificateRequest[2];
certificateLengthA8 = (pGetCertificateRequest[1] & 0x1C) >> 2;
certificateLength70 = pGetCertificateRequest[3];
certificateOffset = certificateOffsetA8 * 256 + certificateOffset70;
certificatelength = certificateLengthA8 * 256 + certificateLength70;
certChainId = QI_SLOT_ID_TO_CERT_ID(requestedslots);
retStatus = Se05x_API_ReadSize(session_ctx, certChainId, &objectSize);
if (retStatus != SM_OK) {
errorCode = kQiErrorUnspecified;
LOG_E("Se05x_API_ReadSize failed");
goto error;
}
/* Read length of manufacturer certificate to determine the offset for PUC */
retStatus = getManufacturerCertificateLength(session_ctx, certChainId, &N_MC);
if (retStatus != SM_OK) {
LOG_E("Failed to read manufacturer certificate length");
errorCode = kQiErrorUnspecified;
goto error;
}
if (certificateOffset >= MAXIMUM_CERT_OFFSET) {
LOG_D("Read PUC");
/* N_RH is length of root Hash Certificate and
* N_MC is length of Manufacturer Certificate
*/
offset += 2 /* Length of length field */
/* Length of RootHash */
+ DIGEST_SIZE_BYTES
/* Length of Manufacturer certificate */
+ N_MC
/* Offset from Product Unit Certificate */
+ certificateOffset - MAXIMUM_CERT_OFFSET;
}
else {
offset += certificateOffset;
}
/* Calculate actual bytes to read */
if (certificatelength == 0) {
bytesToRead = objectSize - offset;
}
else {
bytesToRead = certificatelength;
}
if (bytesToRead == 0) {
/* Cannot read 0 bytes */
errorCode = kQiErrorInvalidRequest;
goto error;
}
/* Bytes to read cannot exceed the total object size */
if ((offset + bytesToRead) > objectSize) {
errorCode = kQiErrorInvalidRequest;
goto error;
}
readSize = bytesToRead;
pData = (uint8_t *)SSS_MALLOC(bytesToRead * sizeof(uint8_t));
if (!pData) {
errorCode = kQiErrorUnspecified;
goto error;
}
if (*pCertificateResponseLen < (size_t)(bytesToRead + 1)) {
LOG_E("Insufficient buffer");
errorCode = kQiErrorUnspecified;
goto error;
}
/* Read certificate chain */
retStatus = readCertificateChain(session_ctx, certChainId, offset, bytesToRead, pData, &readSize);
if (retStatus != SM_OK) {
errorCode = kQiErrorUnspecified;
LOG_E("Se05x_API_ReadObject failed");
goto error;
}
LOG_MAU8_D("ReadCertificate object", pData, readSize);
/* Copy the data read out to response buffer */
memcpy(&pCertificateResponse[1], pData, readSize);
if (pData) {
SSS_FREE(pData);
}
pCertificateResponse[0] = (uint8_t)(authMsgHeader & 0xF0) + (uint8_t)(kQiResponseCertificate);
*pCertificateResponseLen = (bytesToRead) + 1;
return;
error:
if (pData) {
SSS_FREE(pData);
}
if (pCertificateResponse) {
pCertificateResponse[0] = (uint8_t)(authMsgHeader & 0xF0) + (uint8_t)(kQiResponseError);
pCertificateResponse[1] = (uint8_t)(errorCode);
pCertificateResponse[2] = 0x00;
*pCertificateResponseLen = 3;
}
}
5.7.32.4. Authenticate (CHALLENGE)¶
This function performs the CHALLENGE operation and returns the signature R
and signature S
values to the power receiver.
void Authenticate(const uint8_t *pChallengeRequest,
const size_t challengeRequestLen,
uint8_t *pChallengeAuthResponse,
size_t *pChallengeAuthResponseLen)
{
smStatus_t retStatus = SM_NOT_OK;
uint16_t objectSize = 0;
pSe05xSession_t session_ctx = pgSe05xSessionctx;
uint8_t *pTbsAuthPtr = NULL;
size_t readSize = 0;
uint8_t certChainHash[DIGEST_SIZE_BYTES] = {0};
uint8_t hash[DIGEST_SIZE_BYTES] = {0};
size_t hashLen = sizeof(hash);
uint8_t authMsgHeader = 0;
uint8_t requestedSlot = 0;
uint8_t slotsPopulated = 0x00;
uint8_t tbsAuth[TBSAUTH_MAX_SIZE] = {0};
size_t tbsAuthlen = sizeof(tbsAuth);
uint8_t signature[MAX_SIGNATURE_LEN] = {0};
size_t sigLen = sizeof(signature);
qi_error_code_t errorCode = kQiErrorUnspecified;
uint32_t certChainId = 0;
uint32_t keyId = 0;
if (NULL == pChallengeRequest || NULL == pChallengeAuthResponse) {
LOG_E("Null buffer");
errorCode = kQiErrorInvalidRequest;
goto error;
}
authMsgHeader = pChallengeRequest[0];
if (challengeRequestLen != CHALLENGE_CMD_LEN) {
LOG_E("Invalid request length");
errorCode = kQiErrorInvalidRequest;
goto error;
}
requestedSlot = pChallengeRequest[1] & 0x03;
certChainId = QI_SLOT_ID_TO_CERT_ID(requestedSlot);
keyId = QI_SLOT_ID_TO_KEY_ID(requestedSlot);
if (*pChallengeAuthResponseLen < CHALLENGE_AUTH_RESPONSE_LEN) {
LOG_E("Insufficient buffer");
errorCode = kQiErrorUnspecified;
goto error;
}
retStatus = getPopulatedSlots(session_ctx, &slotsPopulated);
if (SM_OK != retStatus) {
errorCode = kQiErrorUnspecified;
LOG_E("Failed to retrieve populated slots");
goto error;
}
if (!(slotsPopulated & 0x01)) {
/* Slot 0 is empty */
errorCode = kQiErrorUnspecified;
goto error;
}
/* Check if the requested slot is populated */
if (!((1 << requestedSlot) & slotsPopulated)) {
errorCode = kQiErrorInvalidRequest;
LOG_E("Requested slot not populated");
goto error;
}
retStatus = Se05x_API_ReadSize(session_ctx, certChainId, &objectSize);
if (retStatus != SM_OK) {
errorCode = kQiErrorUnspecified;
LOG_E("Se05x_API_ReadSize failed");
goto error;
}
/* Certificate chain size cannot be less than DIGEST_SIZE_BYTES */
if (objectSize < DIGEST_SIZE_BYTES) {
errorCode = kQiErrorUnspecified;
goto error;
}
/* Read Certificate chain hash value */
pTbsAuthPtr = &tbsAuth[1];
readSize = DIGEST_SIZE_BYTES;
retStatus = Se05x_API_ReadObject(session_ctx, certChainId, 0, DIGEST_SIZE_BYTES, certChainHash, &readSize);
if (retStatus != SM_OK) {
errorCode = kQiErrorUnspecified;
LOG_E("Se05x_API_ReadObject failed");
goto error;
}
memcpy(pTbsAuthPtr, certChainHash, DIGEST_SIZE_BYTES);
/* Copy Challenge request to TBS Auth */
pTbsAuthPtr = &tbsAuth[TBSAUTH_CHALLENGE_REQ_OFFSET];
memcpy(pTbsAuthPtr, pChallengeRequest, challengeRequestLen);
pChallengeAuthResponse[0] = (uint8_t)(authMsgHeader & 0xF0) + (uint8_t)(kQiResponseChallengeAuth);
pChallengeAuthResponse[1] = (uint8_t)(AUTH_PROTOCOL_VERSION << 4) + (uint8_t)(slotsPopulated);
pChallengeAuthResponse[2] = (uint8_t)(certChainHash[DIGEST_SIZE_BYTES - 1]);
tbsAuth[0] = CHALLENGE_AUTH_RESPONSE_PREFIX; // ASCII representation of A
memcpy(&tbsAuth[TBSAUTH_CHALLENGE_AUTH_RESP_OFFSET], pChallengeAuthResponse, 3);
/* Calculate SHA256 of TBSAuth for signature */
retStatus = getSha256Hash(session_ctx, tbsAuth, tbsAuthlen, hash, &hashLen);
if (retStatus != SM_OK) {
errorCode = kQiErrorUnspecified;
LOG_E("Failed getSha256Hash");
goto error;
}
/* Calculate signature */
retStatus =
Se05x_API_ECDSASign(session_ctx, keyId, kSE05x_ECSignatureAlgo_SHA_256, hash, hashLen, &signature[0], &sigLen);
if (retStatus != SM_OK) {
LOG_E(" sss_asymmetric_sign_digest Failed...");
errorCode = kQiErrorUnspecified;
goto error;
}
/* Extract R and S values from signature */
retStatus = EcSignatureToRandS(signature, &sigLen);
if (retStatus != SM_OK) {
LOG_E(" EcSignatureToRandS Failed...");
errorCode = kQiErrorUnspecified;
goto error;
}
*pChallengeAuthResponseLen = (sigLen) + 3;
memcpy(&pChallengeAuthResponse[3], signature, sigLen);
return;
error:
if (pChallengeAuthResponse) {
pChallengeAuthResponse[0] = (uint8_t)(authMsgHeader & 0xF0) + (uint8_t)(kQiResponseError);
pChallengeAuthResponse[1] = (uint8_t)(errorCode);
pChallengeAuthResponse[2] = 0x00;
*pChallengeAuthResponseLen = 3;
}
}
5.7.32.5. Building the Demo¶
Build Plug & Trust middleware stack. (Refer Building / Compiling)
Select CMake options:
PTMW_SE05X_Ver=07_02
Note
If you have run Secure Authenticator (Qi) Provisioning demo, do not select
PTMW_SE05X_Auth=AESKey
Build project:
Project:
sa_qi_auth
5.7.32.6. Running the Example¶
If you have built a binary, flash the binary on to the board and reset the board.
If you have built an exe to be run from Windows using VCOM, run as:
sa_qi_auth.exe <PORT NAME>
Where <PORT NAME> is the VCOM COM port.
On successful execution you should be able to see logs as:
App :INFO :PlugAndTrust_v04.01.01_20220112
sss :INFO :atr (Len=35)
01 A0 00 00 03 96 04 03 E8 00 FE 02 0B 03 E8 00
01 00 00 00 00 64 13 88 0A 00 65 53 45 30 35 31
00 00 00
sss :WARN :Communication channel is Plain.
sss :WARN :!!!Not recommended for production use.!!!
App :INFO :Send command GET_DIGESTS
App :INFO :Retrieved digest (Len=32)
46 29 65 3A D1 CE B3 7C 6A 36 F0 CC 11 B4 29 16
86 39 27 85 F0 F8 26 DF DE D3 5E AC 5F CC 50 FC
App :INFO :Send command GET_CERTIFICATE
App :INFO :Certificate chain digest successfully verified
App :INFO :Retrieved PUC (Len=442)
30 82 01 B6 30 82 01 5B A0 03 02 01 02 02 0A 00
B0 9B 9F 24 00 A7 9E A0 AE 30 0A 06 08 2A 86 48
CE 3D 04 03 02 30 12 31 10 30 0E 06 03 55 04 03
0C 07 43 41 43 41 2D 58 31 30 22 18 0F 32 30 32
31 30 38 32 33 31 35 35 31 35 33 5A 18 0F 32 30
32 31 30 38 32 34 31 35 35 31 35 33 5A 30 81 8B
31 2C 30 2A 06 03 55 04 03 0C 23 30 30 30 31 32
33 2D 52 61 70 69 64 20 63 68 61 72 67 69 6E 67
20 62 61 67 65 6C 20 74 6F 61 73 74 65 72 31 29
30 27 06 03 55 04 5C 04 20 53 58 4D 67 64 47 68
70 63 79 42 68 62 69 42 46 59 58 4E 30 5A 58 49
67 52 57 64 6E 50 77 3D 3D 31 30 30 2E 06 0A 09
92 26 89 93 F2 2C 64 01 01 0C 20 44 6F 20 6E 6F
74 20 75 73 65 20 61 73 20 61 20 66 6C 6F 74 61
74 69 6F 6E 20 64 65 76 69 63 65 30 59 30 13 06
07 2A 86 48 CE 3D 02 01 06 08 2A 86 48 CE 3D 03
01 07 03 42 00 04 07 7B 1F 30 E5 D7 9A 63 FB CC
35 DE 84 36 E4 5D 89 C1 5F 99 98 E8 B8 F2 C6 00
1C AE DA E5 F8 59 3A 50 76 D2 C7 A4 AF 0B C5 6B
47 9D E1 6A DA 11 0C 0A EF D7 39 E1 F0 4D 0D D7
65 7E B9 32 13 53 A3 1B 30 19 30 17 06 05 67 81
14 01 02 01 01 FF 04 0B 04 09 F1 02 D3 C4 15 06
E7 68 79 30 0A 06 08 2A 86 48 CE 3D 04 03 02 03
49 00 30 46 02 21 00 A8 29 0E C3 C9 AF DC 08 52
58 CB B4 7A B7 02 3A BC D5 CD 36 78 F4 1D CE 2F
7C 4C CC 95 46 FC 56 02 21 00 FA EB 67 F9 14 79
DE 6D 31 DE E7 0B 0C 51 3E 0A 36 1D C4 49 F7 FA
F4 E9 33 FE 90 49 57 AD EC 10
App :INFO :PUC successfully verified
App :INFO :Manufacturer certificate successfully verified
App :INFO :Certificate chain successfully verified
App :INFO :Retrieved PUC public key (Len=65)
04 07 7B 1F 30 E5 D7 9A 63 FB CC 35 DE 84 36 E4
5D 89 C1 5F 99 98 E8 B8 F2 C6 00 1C AE DA E5 F8
59 3A 50 76 D2 C7 A4 AF 0B C5 6B 47 9D E1 6A DA
11 0C 0A EF D7 39 E1 F0 4D 0D D7 65 7E B9 32 13
53
App :INFO :Send command CHALLENGE
App :INFO :Challenge Signature (Len=64)
7E B0 82 D3 3D 91 02 37 AA FE 81 15 DF 02 A2 57
D1 8C D6 A1 BB C0 20 DE CF E2 69 B0 57 35 93 5E
62 E1 E6 37 E0 64 92 5A D8 BB 7E 92 FB 52 1E 84
F9 DD A1 43 F9 7B 11 48 0B 1C F9 CD 31 40 77 AD
App :INFO :TBSAuth (Len=54)
41 46 29 65 3A D1 CE B3 7C 6A 36 F0 CC 11 B4 29
16 86 39 27 85 F0 F8 26 DF DE D3 5E AC 5F CC 50
FC 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 13 11 FC
App :INFO :Challenge successfully verified
App :INFO :ex_sss Finished
5.7.32.7. Porting¶
The example allows porting of host verification and host RNG functions
in case mbedTLS is not available. If you want to add your own implementation
of these operations, update the following APIs in sa_qi_auth/port/sa_qi_helper_port.c
:
/* Port to implement RNG to get 16 byte nonce value
* for authentication operation.
* This API does not guarantee the randomness of the RNG.
* User should make sure that the RNG seed is from a trusted source
* and that the randomness of the source is NIST compliant
*/
int port_getRandomNonce(uint8_t *nonce, size_t *pNonceLen)
{
int ret = 0;
size_t random_length = (*pNonceLen > NONCE_LEN) ? NONCE_LEN : (*pNonceLen);
*pNonceLen = random_length;
ret = getRandom(nonce, random_length);
if (0 != ret) {
*pNonceLen = 0;
}
return ret;
}
/* Port to implement function which will
* parse an X.509 certificate and extract the public key
* from it.
*/
void port_parseCertGetPublicKey(uint8_t *pCert, size_t certLen, uint8_t *pPublicKey, size_t *publicKeylen)
{
parseCertGetPublicKey(pCert, certLen, pPublicKey, publicKeylen);
}
/* Port to implement function which will
* verify the complete certificate chain as passed in certificate_chain
*/
int port_hostVerifyCertificateChain(uint8_t *certificate_chain,
size_t certificate_chain_size,
uint16_t pucCertOffset,
uint16_t manufacturerCertLenOffset)
{
return hostVerifyCertificateChain(
certificate_chain, certificate_chain_size, pucCertOffset, manufacturerCertLenOffset);
}
/* Port to implement function which will
* verify CHALLENGE on host
*/
int port_hostVerifyChallenge(uint8_t *pPublicKey,
size_t publicKeyLen,
uint8_t *pCertificateChainHash,
uint8_t *pChallengeRequest,
uint8_t *pChallengeResponse)
{
return hostVerifyChallenge(pPublicKey, publicKeyLen, pCertificateChainHash, pChallengeRequest, pChallengeResponse);
}