/** * \brief Sets a Register of the TDC-GP22. * \param[in] reg is the readable register of the GP22. * \param[out] value is a pointer to the storage of the register value. * \param[in] len indicates how many bytes have to read. * \return FALSE if the SPI transmission get a time out. */ uint8_t bsp_GP22RegRead(uint8_t reg, uint32_t *value, uint8_t len) { uint8_t success; uint8_t tx_bytes[5]; uint8_t rx_bytes[5]; assert(GP22_IS_RD(reg)); assert(len==2 || len==4); /* First send a read command */ tx_bytes[0] = reg; success = bsp_SPITransmitBlocked(BSP_SPI_CS_GP22, tx_bytes, len+1, rx_bytes); /* Transform received bytes to a 32 bit integer */ if (success) { if (len == 2) { *value = bytes2short(&(rx_bytes[1])); } else if (len == 4) { *value = bytes2long(&(rx_bytes[1])); } else { success = 0; } } return success; }
ReturnValue BleApiTest_TestEncodingLongExactLength(pBleDevice dev) { ReturnValue retval; /* write U2F VERSION command to get U2F protocol version */ unsigned char request[] = { 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x06 }; unsigned char reply[256]; unsigned int replyLength = sizeof(reply); unsigned char replyCmd; // short encoding, requesting 65535 return bytes. retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, 7, &replyCmd, reply, &replyLength); if (!retval) return retval; CHECK_EQ(FIDO_RESP_SUCCESS, bytes2short(reply, replyLength - 2), "expects FIDO_RESP_SUCCESS (0x9000)"); /* drop reply status code. */ reply[replyLength - 2] = '\0'; /* check U2F Protocol version */ CHECK_EQ((replyLength - 2), 6, "U2FProtocolVersion length should be 6 bytes and 2 bytes result code."); CHECK_EQ(memcmp(reply, "U2F_V2", 6), 0, "U2FProtocolVersion should be U2F_V2"); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
ReturnValue BleApiTest_BadCLA(pBleDevice dev) { ReturnValue retval; /* write U2F VERSION command to get U2F protocol version */ unsigned char request[] = { 0x01 /* != 0 */ , 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }; unsigned char reply[256]; unsigned int replyLength = sizeof(reply); unsigned char replyCmd; request[0] = 0x01 + (rand() % 0xFF); retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, 7, &replyCmd, reply, &replyLength); if (!retval) return retval; /* check reply */ CHECK_EQ(replyLength, 2, "expects a 2 byte error reply."); CHECK_NE(FIDO_RESP_SUCCESS, bytes2short(reply, 0), "message does not accept FIDO_RESP_SUCCESS (0x9000)."); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
ReturnValue BleApiTest_TestEncodingLongDataExactLength(pBleDevice dev) { ReturnValue retval; U2F_AUTHENTICATE_REQ authReq; unsigned char reply[2048]; unsigned int replyLength = sizeof(reply); unsigned char request[256]; unsigned int requestlen; unsigned char replyCmd; // pick random challenge and use registered appId. for (size_t i = 0; i < sizeof(authReq.nonce); ++i) authReq.nonce[i] = rand(); memcpy(authReq.appId, regReq.appId, sizeof(authReq.appId)); authReq.keyHandleLen = regRsp.keyHandleLen; memcpy(authReq.keyHandle, regRsp.keyHandleCertSig, authReq.keyHandleLen); uint64_t t = dev->TimeMs(); /* prepare register request */ request[0] = 0x00; request[1] = U2F_INS_AUTHENTICATE; request[2] = U2F_AUTH_ENFORCE; request[3] = 0x00; request[4] = 0x00; request[5] = 0x00; request[6] = U2F_NONCE_SIZE + U2F_APPID_SIZE + 1 + authReq.keyHandleLen; memcpy(request + 7, reinterpret_cast < char *>(&authReq), request[6]); requestlen = 7 + request[6]; request[requestlen++] = 0x00; /* 1 byte user presence + 4 bytes counter + upto (6 + 33 + 33) bytes signature */ request[requestlen++] = 0x01 + 0x04 + 0x48; /* write command */ retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, requestlen, &replyCmd, reply, &replyLength); CHECK_EQ(retval, ReturnValue::BLEAPI_ERROR_SUCCESS); CHECK_EQ(replyCmd, FIDO_BLE_CMD_MSG, "Reply is not a FIDO_BLE_CMD_MSG (0x83)"); CHECK_EQ(FIDO_RESP_SUCCESS, bytes2short(reply, replyLength - 2), "expects FIDO_RESP_SUCCESS (0x9000)"); CHECK_NE(replyLength, 2); CHECK_LE(replyLength - 2, sizeof(U2F_AUTHENTICATE_RESP), "Returned authentication response does not match expected length."); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
ReturnValue BleApiTest_TestEncodingLongDataWrongLength(pBleDevice dev) { ReturnValue retval; U2F_AUTHENTICATE_REQ authReq; unsigned char reply[2048]; unsigned int replyLength = sizeof(reply); unsigned char request[256]; unsigned int requestlen; unsigned char replyCmd; // pick random challenge and use registered appId. for (size_t i = 0; i < sizeof(authReq.nonce); ++i) authReq.nonce[i] = rand(); memcpy(authReq.appId, regReq.appId, sizeof(authReq.appId)); authReq.keyHandleLen = regRsp.keyHandleLen; memcpy(authReq.keyHandle, regRsp.keyHandleCertSig, authReq.keyHandleLen); uint64_t t = dev->TimeMs(); /* prepare register request */ request[0] = 0x00; request[1] = U2F_INS_AUTHENTICATE; request[2] = U2F_AUTH_ENFORCE; request[3] = 0x00; request[4] = 0x00; request[5] = 0x00; request[6] = U2F_NONCE_SIZE + U2F_APPID_SIZE + 1 + authReq.keyHandleLen; memcpy(request + 7, reinterpret_cast < char *>(&authReq), request[6]); requestlen = 7 + request[6]; request[requestlen++] = 0x00; request[requestlen++] = 0x08; /* fixed at 8 bytes, which is too short */ /* write command */ retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, requestlen, &replyCmd, reply, &replyLength); CHECK_EQ(retval, ReturnValue::BLEAPI_ERROR_SUCCESS); /* check U2F Protocol version */ CHECK_EQ(replyLength, 2); CHECK_EQ(FIDO_RESP_WRONG_LENGTH, bytes2short(reply, 0), "expects FIDO_RESP_WRONG_LENGTH (0x6700)"); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
/* Reads a short from the file */ static uint16_t read_short(struct tiff_file_desc *tfd, bool *error) { uint16_t value = -1; if (tfd != NULL && tfd->file != NULL && !*error) { uint8_t buf[2]; size_t ret; ret = fread(buf, 1, sizeof(buf), tfd->file); if (ret != sizeof(buf)) *error = true; else value = bytes2short(buf, tfd->is_le); } else *error = true; return value; }
ReturnValue BleApiTest_VersionWrongLength(pBleDevice dev) { ReturnValue retval; /* write U2F VERSION command to get U2F protocol version */ unsigned char request[] = { 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03 }; unsigned char reply[256]; unsigned int replyLength = sizeof(reply); unsigned char replyCmd; retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, 10, &replyCmd, reply, &replyLength); if (!retval) return retval; /* check reply */ CHECK_EQ(replyLength, 2, "expects a 2 byte error reply."); CHECK_EQ(FIDO_RESP_WRONG_LENGTH, bytes2short(reply, 0), "message expects FIDO_RESP_WRONG_LENGTH (0x6700)."); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
ReturnValue BleApiTest_TestEncodingLongWrongLength(pBleDevice dev) { ReturnValue retval; /* write U2F VERSION command to get U2F protocol version */ unsigned char request[] = { 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02 }; unsigned char reply[256]; unsigned int replyLength = sizeof(reply); unsigned char replyCmd; // short encoding, requesting 255 return bytes. retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, 7, &replyCmd, reply, &replyLength); if (!retval) return retval; /* check U2F Protocol version */ CHECK_EQ(replyLength, 2); CHECK_EQ(FIDO_RESP_WRONG_LENGTH, bytes2short(reply, 0), "expects FIDO_RESP_WRONG_LENGTH (0x6700)"); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
ReturnValue BleApiTest_UnknownINS(pBleDevice dev) { ReturnValue retval; /* write U2F VERSION command to get U2F protocol version */ unsigned char request[] = { 0x00, 0x00 /* not U2F instruction */ , 0x00, 0x00, 0x00, 0x00, 0x00 }; unsigned char reply[256]; unsigned int replyLength = sizeof(reply); unsigned char replyCmd; retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, 7, &replyCmd, reply, &replyLength); if (!retval) return retval; /* check reply */ CHECK_EQ(replyLength, 2, "expects a 2 byte error reply."); CHECK_EQ(FIDO_RESP_INVALID_INSTRUCTION, bytes2short(reply, 0), "expects FIDO_RESP_INVALID_INSTRUCTION (0x6D00)."); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
/* Reads an IFD entry and updates the right tiff fields */ static void read_ifd_entry(struct tiff_file_desc *tfd, bool *error) { if (error == NULL || *error) return; uint16_t tag; uint16_t type; uint32_t count; uint32_t offset; uint32_t value; uint32_t next_value = 0; uint32_t cur_pos; uint8_t buf[4]; size_t ret; /* Identification tag */ tag = read_short(tfd, error); /* Type */ type = read_short(tfd, error); /* Number of values */ count = read_long(tfd, error); /* Value */ ret = fread(buf, 1, sizeof(buf), tfd->file); if (ret != sizeof(buf)) *error = true; offset = bytes2long(buf, tfd->is_le); switch (type) { case BYTE: value = buf[0]; break; case SHORT: value = bytes2short(buf, tfd->is_le); uint8_t *next_buf = &buf[2]; next_value = bytes2short(next_buf, tfd->is_le); break; case LONG: value = offset; break; /* Unused values */ case ASCII: case RATIONAL: default: value = 0; } switch(tag) { /* ImageWidth */ case 0x0100: tfd->width = value; break; /* ImageLength */ case 0x0101: tfd->height = value; break; /* BitsPerSample */ case 0x0102: if (count <= 2) tfd->bits_per_sample = value; else { cur_pos = ftell(tfd->file); fseek(tfd->file, offset, SEEK_SET); tfd->bits_per_sample = read_short(tfd, error); fseek(tfd->file, cur_pos, SEEK_SET); } break; /* Compression */ case 0x0103: tfd->compression = value; break; /* PhotometricInterpretation */ case 0x0106: tfd->photometric_interpretation = value; break; /* StripOffsets */ case 0x0111: tfd->nb_strips = count; tfd->strip_offsets = calloc(count, sizeof(uint32_t)); if (tfd->strip_offsets == NULL) { *error = true; return; } if ((count > 1 && type == LONG) || (count > 2 && type == SHORT)) { cur_pos = ftell(tfd->file); fseek(tfd->file, offset, SEEK_SET); for (uint32_t i = 0; i < count; ++i) tfd->strip_offsets[i] = read_long(tfd, error); fseek(tfd->file, cur_pos, SEEK_SET); } else if (type == SHORT && count == 2) { tfd->strip_offsets[0] = value; tfd->strip_offsets[1] = next_value; } else tfd->strip_offsets[0] = value; break; /* SamplesPerPixel */ case 0x0115: tfd->samples_per_pixels = value; break; /* RowsPerStrip */ case 0x0116: /* * Big values mean infinity, * in which case there are no strips */ if (value > 0xFFFFFF) value = 0; tfd->rows_per_strip = value; break; /* StripByteCounts */ case 0x0117: tfd->strip_bytes_count = count; tfd->strip_bytes = calloc(count, sizeof(uint32_t)); if (tfd->strip_bytes == NULL) { *error = true; return; } if ((count > 1 && type == LONG) || (count > 2 && type == SHORT)) { cur_pos = ftell(tfd->file); fseek(tfd->file, offset, SEEK_SET); for (uint32_t i = 0; i < count; ++i) tfd->strip_bytes[i] = read_long(tfd, error); fseek(tfd->file, cur_pos, SEEK_SET); } else if (type == SHORT && count == 2) { tfd->strip_bytes[0] = value; tfd->strip_bytes[1] = next_value; } else tfd->strip_bytes[0] = value; } }
ReturnValue BleApiTest_Sign(pBleDevice dev, uint32_t *ctr, int expectedSW12, bool checkOnly, bool corruptKH, bool corruptAddId) { ReturnValue retval; U2F_AUTHENTICATE_REQ authReq; unsigned char reply[2048]; unsigned int replyLength = sizeof(reply); unsigned char request[256]; unsigned int requestlen; unsigned char replyCmd; // pick random challenge and use registered appId. for (size_t i = 0; i < sizeof(authReq.nonce); ++i) authReq.nonce[i] = rand(); memcpy(authReq.appId, regReq.appId, sizeof(authReq.appId)); authReq.keyHandleLen = regRsp.keyHandleLen; memcpy(authReq.keyHandle, regRsp.keyHandleCertSig, authReq.keyHandleLen); if (corruptKH) authReq.keyHandle[0] ^= 0x55; if (corruptAddId) authReq.appId[0] ^= 0xAA; uint64_t t = dev->TimeMs(); /* prepare register request */ request[0] = 0x00; request[1] = U2F_INS_AUTHENTICATE; request[2] = checkOnly ? U2F_AUTH_CHECK_ONLY : U2F_AUTH_ENFORCE; request[3] = 0x00; request[4] = 0x00; request[5] = 0x00; request[6] = U2F_NONCE_SIZE + U2F_APPID_SIZE + 1 + authReq.keyHandleLen; memcpy(request + 7, reinterpret_cast < char *>(&authReq), request[6]); requestlen = 7 + request[6]; request[requestlen++] = 0x00; request[requestlen++] = 0x00; /* write command */ retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, requestlen, &replyCmd, reply, &replyLength); CHECK_EQ(retval, ReturnValue::BLEAPI_ERROR_SUCCESS); if (expectedSW12 != FIDO_RESP_SUCCESS) { CHECK_EQ(expectedSW12, bytes2short(reply, replyLength - 2), "Returned error does not match expected value."); CHECK_EQ(replyLength, 2, "Returned value does not match expected length."); return ReturnValue::BLEAPI_ERROR_SUCCESS; } CHECK_EQ(replyCmd, FIDO_BLE_CMD_MSG, "Reply is not a FIDO_BLE_CMD_MSG (0x83)"); CHECK_EQ(FIDO_RESP_SUCCESS, bytes2short(reply, replyLength - 2), "Status code is not FIDO_RESP_SUCCESS (0x9000)"); CHECK_NE(replyLength, 2, "Reply length is only status code."); CHECK_LE(replyLength - 2, sizeof(U2F_AUTHENTICATE_RESP), "Returned authentication response does not match expected length."); U2F_AUTHENTICATE_RESP resp; memcpy(&resp, reply, replyLength - 2); CHECK_EQ(resp.flags, 0x01, "Flags value in authentication response is always 1"); INFO << "Sign: " << (replyLength - 2) << " bytes in " << ((float)(dev->TimeMs() - t)) / 1000.0 << "s"; // Parse signature from authenticate response. p256_int sig_r, sig_s; CHECK_EQ(1, dsa_sig_unpack(resp.sig, replyLength - 2 - sizeof(resp.flags) - sizeof(resp.ctr), &sig_r, &sig_s)); // Compute hash as integer. p256_int h; SHA256_CTX sha; SHA256_init(&sha); SHA256_update(&sha, regReq.appId, sizeof(regReq.appId)); // O SHA256_update(&sha, &resp.flags, sizeof(resp.flags)); // T SHA256_update(&sha, &resp.ctr, sizeof(resp.ctr)); // CTR SHA256_update(&sha, authReq.nonce, sizeof(authReq.nonce)); // d p256_from_bin(SHA256_final(&sha), &h); // Parse public key from registration response. p256_int pk_x, pk_y; p256_from_bin(regRsp.pubKey.x, &pk_x); p256_from_bin(regRsp.pubKey.y, &pk_y); // Verify signature. CHECK_EQ(1, p256_ecdsa_verify(&pk_x, &pk_y, &h, &sig_r, &sig_s), "Signature does not match."); *ctr = ntohl(resp.ctr); return ReturnValue::BLEAPI_ERROR_SUCCESS; }
ReturnValue BleApiTest_Enroll(pBleDevice dev, int expectedSW12) { uint64_t t = dev->TimeMs(); ReturnValue retval; int i; unsigned char reply[2048]; unsigned int replyLength = sizeof(reply); unsigned char request[256]; unsigned int requestlen; unsigned char replyCmd; memset(reply, 0, sizeof(reply)); /* generate appid and nonce */ for (i = 0; i < sizeof(regReq.appId); i++) regReq.appId[i] = (rand() & 0xFF); for (i = 0; i < sizeof(regReq.nonce); i++) regReq.nonce[i] = (rand() & 0xFF); /* prepare register request */ request[0] = 0x00; request[1] = 0x01; request[2] = 0x00; request[3] = 0x00; request[4] = 0x00; request[5] = 0x00; request[6] = sizeof(regReq.nonce) + sizeof(regReq.appId); memcpy(request + 7, regReq.nonce, sizeof(regReq.nonce)); memcpy(request + 7 + sizeof(regReq.nonce), regReq.appId, sizeof(regReq.appId)); requestlen = 7 + sizeof(regReq.nonce) + sizeof(regReq.appId); request[requestlen++] = 0x00; request[requestlen++] = 0x00; /* write command */ retval = dev->CommandWrite(FIDO_BLE_CMD_MSG, request, requestlen, &replyCmd, reply, &replyLength); CHECK_EQ(retval, ReturnValue::BLEAPI_ERROR_SUCCESS); if (expectedSW12 != FIDO_RESP_SUCCESS) { CHECK_EQ(expectedSW12, bytes2short(reply, replyLength - 2), "Returned error does not match expected value."); CHECK_EQ(replyLength, 2, "Returned value does not match expected length."); return ReturnValue::BLEAPI_ERROR_SUCCESS; } /* check reply */ CHECK_EQ(replyCmd, FIDO_BLE_CMD_MSG, "Reply is not a FIDO_BLE_CMD_MSG (0x83)"); CHECK_EQ(FIDO_RESP_SUCCESS, bytes2short(reply, replyLength - 2), "Status code is not FIDO_RESP_SUCCESS (0x9000)"); CHECK_NE(replyLength, 2, "Reply length is only status code."); CHECK_LE(replyLength - 2, sizeof(U2F_REGISTER_RESP), "Returned register response does not match expected length."); memcpy(®Rsp, reply, replyLength - 2); CHECK_EQ(regRsp.registerId, U2F_REGISTER_ID, "Register ID is not 0x05"); CHECK_EQ(regRsp.pubKey.format, UNCOMPRESSED_POINT, "Public Key format is not uncompressed point."); INFO << "Enroll: " << (replyLength - 2) << " bytes in " << ((float)(dev->TimeMs() - t)) / 1000.0 << "s"; // Check crypto of enroll response. std::string cert; CHECK_EQ(getCertificate(regRsp, &cert), true, "Cannot extract certificate."); INFO << "cert: " << bytes2ascii(cert); std::string pk; CHECK_EQ(getSubjectPublicKey(cert, &pk), true, "Cannot extract public key."); INFO << "pk : " << bytes2ascii(pk); std::string sig; CHECK_EQ(getSignature(regRsp, static_cast<int>(cert.size()), &sig), true, "Cannot extract signature."); INFO << "sig : " << bytes2ascii(sig); // Parse signature into two integers. p256_int sig_r, sig_s; CHECK_EQ(1, dsa_sig_unpack((uint8_t *) (sig.data()), static_cast<int>(sig.size()), &sig_r, &sig_s), "Cannot unpack signature"); // Compute hash as integer. const uint8_t *hash; p256_int h; SHA256_CTX sha; SHA256_init(&sha); uint8_t rfu = 0; SHA256_update(&sha, &rfu, sizeof(rfu)); // 0x00 SHA256_update(&sha, regReq.appId, sizeof(regReq.appId)); // O SHA256_update(&sha, regReq.nonce, sizeof(regReq.nonce)); // d SHA256_update(&sha, regRsp.keyHandleCertSig, regRsp.keyHandleLen); // hk SHA256_update(&sha, ®Rsp.pubKey, sizeof(regRsp.pubKey)); // pk hash = SHA256_final(&sha); p256_from_bin(hash, &h); INFO << "hash : " << bytes2ascii((char *)hash, 32); // Parse subject public key into two integers. CHECK_EQ(pk.size(), P256_POINT_SIZE, "Public key does not match P256 point size."); p256_int pk_x, pk_y; p256_from_bin((uint8_t *) pk.data() + 1, &pk_x); p256_from_bin((uint8_t *) pk.data() + 1 + P256_SCALAR_SIZE, &pk_y); // Verify signature. CHECK_EQ(1, p256_ecdsa_verify(&pk_x, &pk_y, &h, &sig_r, &sig_s), "Signature does not match."); return ReturnValue::BLEAPI_ERROR_SUCCESS; }