/* * * Copyright (c) 2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FactoryDataParser.h" #include #include #include #define MAX_FACTORY_DATA_NESTING_LEVEL 4 static inline bool uint16_decode(zcbor_state_t * states, uint16_t * value) { uint32_t u32; if (zcbor_uint32_decode(states, &u32)) { *value = (uint16_t) u32; return true; } return false; } static inline bool uint8_decode(zcbor_state_t * states, uint8_t * value) { uint32_t u32; if (zcbor_uint32_decode(states, &u32)) { *value = (uint8_t) u32; return true; } return false; } static bool DecodeEntry(zcbor_state_t * states, void * buffer, size_t bufferSize, size_t * outlen) { struct zcbor_string tempString; int32_t tempInt = 0; // Try to decode entry as string bool res = zcbor_tstr_decode(states, &tempString); if (res) { if (bufferSize < tempString.len) { return false; } memcpy(buffer, tempString.value, tempString.len); *outlen = tempString.len; return res; } // Try to decode entry as int32 res = zcbor_int32_decode(states, &tempInt); if (res) { if (bufferSize < sizeof(tempInt)) { return false; } memcpy(buffer, &tempInt, sizeof(tempInt)); *outlen = sizeof(tempInt); return res; } return res; } bool FindUserDataEntry(struct FactoryData * factoryData, const char * entry, void * buffer, size_t bufferSize, size_t * outlen) { if ((!factoryData) || (!factoryData->user.data) || (!buffer) || (!outlen)) { return false; } ZCBOR_STATE_D(states, MAX_FACTORY_DATA_NESTING_LEVEL - 1, factoryData->user.data, factoryData->user.len, 1, 0); bool res = zcbor_map_start_decode(states); bool keyFound = false; struct zcbor_string currentString; while (res) { res = zcbor_tstr_decode(states, ¤tString); if (!res) { break; } if (strncmp(entry, (const char *) currentString.value, currentString.len) == 0) { res = DecodeEntry(states, buffer, bufferSize, outlen); keyFound = true; break; } else { res = res && zcbor_any_skip(states, NULL); } } return res && keyFound && zcbor_list_map_end_force_decode(states); } bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData * factoryData) { memset(factoryData, 0, sizeof(*factoryData)); ZCBOR_STATE_D(states, MAX_FACTORY_DATA_NESTING_LEVEL, buffer, bufferSize, 1, 0); bool res = zcbor_map_start_decode(states); struct zcbor_string currentString; while (res) { res = zcbor_tstr_decode(states, ¤tString); if (!res) { res = true; break; } if (strncmp("version", (const char *) currentString.value, currentString.len) == 0) { res = res && uint16_decode(states, &factoryData->version); } else if (strncmp("hw_ver", (const char *) currentString.value, currentString.len) == 0) { res = res && uint16_decode(states, &factoryData->hw_ver); factoryData->hwVerPresent = res; } else if (strncmp("spake2_it", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_uint32_decode(states, &factoryData->spake2_it); } else if (strncmp("vendor_id", (const char *) currentString.value, currentString.len) == 0) { res = res && uint16_decode(states, &factoryData->vendor_id); factoryData->vendorIdPresent = res; } else if (strncmp("product_id", (const char *) currentString.value, currentString.len) == 0) { res = res && uint16_decode(states, &factoryData->product_id); factoryData->productIdPresent = res; } else if (strncmp("discriminator", (const char *) currentString.value, currentString.len) == 0) { res = res && uint16_decode(states, &factoryData->discriminator); factoryData->discriminatorPresent = res; } else if (strncmp("passcode", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_uint32_decode(states, &factoryData->passcode); } else if (strncmp("sn", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->sn); } else if (strncmp("date", (const char *) currentString.value, currentString.len) == 0) { // Date format is YYYY-MM-DD, so format needs to be validated and string parse to integer parts. struct zcbor_string date; res = res && zcbor_bstr_decode(states, &date); if (date.len == 10 && isdigit(date.value[0]) && isdigit(date.value[1]) && isdigit(date.value[2]) && isdigit(date.value[3]) && date.value[4] == '-' && isdigit(date.value[5]) && isdigit(date.value[6]) && date.value[7] == '-' && isdigit(date.value[8]) && isdigit(date.value[9])) { factoryData->date_year = (uint16_t) (1000 * (uint16_t) (date.value[0] - '0') + 100 * (uint16_t) (date.value[1] - '0') + 10 * (uint16_t) (date.value[2] - '0') + (uint16_t) (date.value[3] - '0')); factoryData->date_month = (uint8_t) (10 * (uint16_t) (date.value[5] - '0') + (uint16_t) (date.value[6] - '0')); factoryData->date_day = (uint8_t) (10 * (uint16_t) (date.value[8] - '0') + (uint16_t) (date.value[9] - '0')); } else { res = false; } } else if (strncmp("hw_ver_str", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->hw_ver_str); } else if (strncmp("rd_uid", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->rd_uid); } else if (strncmp("dac_cert", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->dac_cert); } else if (strncmp("dac_key", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->dac_priv_key); } else if (strncmp("pai_cert", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->pai_cert); } else if (strncmp("spake2_salt", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->spake2_salt); } else if (strncmp("spake2_verifier", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->spake2_verifier); } else if (strncmp("vendor_name", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->vendor_name); } else if (strncmp("product_name", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->product_name); } else if (strncmp("part_number", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->part_number); } else if (strncmp("product_url", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->product_url); } else if (strncmp("product_label", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->product_label); } else if (strncmp("enable_key", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->enable_key); } else if (strncmp("product_finish", (const char *) currentString.value, currentString.len) == 0) { res = res && uint8_decode(states, &factoryData->product_finish); factoryData->productFinishPresent = res; } else if (strncmp("primary_color", (const char *) currentString.value, currentString.len) == 0) { res = res && uint8_decode(states, &factoryData->primary_color); factoryData->primaryColorPresent = res; } else if (strncmp("user", (const char *) currentString.value, currentString.len) == 0) { factoryData->user.data = (void *) states->payload; res = res && zcbor_any_skip(states, NULL); factoryData->user.len = (size_t) ((void *) states->payload - factoryData->user.data); } else { res = res && zcbor_any_skip(states, NULL); } } return res && zcbor_list_map_end_force_decode(states); }