// Copyright (c) 2015-2019 The HomeKit ADK Contributors // // Licensed under the Apache License, Version 2.0 (the “License”); // you may not use this file except in compliance with the License. // See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors. #include #include #include "HAP+Internal.h" int main(int argc, char* argv[]) { HAPError err; if (argc <= 1) { #ifndef _WIN32 #define B(stringLiteral) "\x1B[1m" stringLiteral "\x1B[0m" #define U(stringLiteral) "\x1B[4m" stringLiteral "\x1B[0m" #else #define B(stringLiteral) stringLiteral #define U(stringLiteral) stringLiteral #endif const char* categoryDescriptions = " 1 Other.\n" " 2 Bridges.\n" " 3 Fans.\n" " 4 Garage Door Openers.\n" " 5 Lighting.\n" " 6 Locks.\n" " 7 Outlets.\n" " 8 Switches.\n" " 9 Thermostats.\n" " 10 Sensors.\n" " 11 Security Systems.\n" " 12 Doors.\n" " 13 Windows.\n" " 14 Window Coverings.\n" " 15 Programmable Switches.\n" " 16 Range Extenders.\n" " 19 Air Purifiers.\n" " 20 Heaters.\n" " 21 Air Conditioners.\n" " 22 Humidifiers.\n" " 23 Dehumidifiers.\n" " 28 Sprinklers.\n" " 29 Faucets.\n" " 30 Shower Systems.\n"; const char* exampleOutput = " 1\n" " 518-08-582\n" " 263FEA64889756A8E25FD53DD5FA1022\n" " D0BE3DFCC3B28A4D612943215AD71005CA4E240A5672EFF427F30EEAC173756167AC4D73779\n" " 3AF18937B1770E173ED346AB790E428B2771ACA62FE11C1A0FC8E01169824632BB914863\n" " 760918841CB3F263D5D71C431A2141C51797A91022C5BCD30D7BC9259A2037C4BDEE8F74\n" " 8D65B15AEA33DF2F00193FBAAC603C921820D2E4FE5747F965F31F3DD16D8A7228FE8FC8\n" " 5AD70138C797CB91B47488283C568D1CDAFCF6E950A1D117BD4E42FB0B90FF97992BCCE0\n" " C86F62F866489BC2F556D342F4C20AC26B12A48299C642BE86270F0D3F1E6E86E84115A7\n" " 12931F7FE1D53E6230FB14C29AD2E23B16E0B8F6AFD4D709B562DC4921F550450AC8FD09\n" " 73DD80DAE629CB399DD6E3E96695E2E8060196D5FFFD292A1246AD76219E998FDD0E690B\n" " 405A0D2AD9C9CADF905520C4E6B66952E0DA27E523060DE310A539F6BF30E48B69A5F26D\n" " 5E283DE6EE8F51AFB920E00D1B1AE3BA423041A63BA788B6F6BCBA2AD7C89946EEE79D72\n" " 6649BCEAB43BB920F11260F8017C9921A60C169B28569\n" " 7OSX\n" " X-HM://007JNU5AE7OSX\n"; printf( B("HomeKit Accessory Setup Generator")" - Version %s (%s)\n" "\n" B("USAGE")"\n" " "B("AccessorySetupGenerator")" [OPTION]...\n" "\n" B("DESCRIPTION")"\n" " This tool generates information for provisioning of a HomeKit accessory,\n" " namely a setup code, a corresponding SRP salt and verifier, and a setup ID.\n" " The setup code is used by the controller to set up an encrypted link with\n" " the accessory during HomeKit pairing. The setup ID is used to identify\n" " the accessory to which a scanned label belongs.\n" " \n" " "B("Each accessory needs to be provisioned with unique accessory setup")"\n" " "B("information before it may be used.")"\n" "\n" B("OPTIONS")"\n" " The following options are available:\n" " \n" " "B("--ip")"\n" " Accessory supports HAP over IP transport; \n" " \n" " "B("--ble")"\n" " Accessory supports HAP over BLE transport.\n" " \n" " "B("--category")" "U("Category")"\n" " The accessory category.\n" " \n" " An accessory with support for multiple categories should advertise the\n" " primary category. An accessory for which a primary category cannot be\n" " determined or the primary category isn't among the well defined\n" " categories falls in the `Other` category.\n" " \n" " Well defined categories:\n" "%s\n" " "B("--setup-code")" "U("Setup code")"\n" " Generates accessory setup information that allows pairing using the\n" " specified setup code (e.g. for development).\n" " Format is `XXX-XX-XXX` with X being a digit from 0-9.\n" " - Setup codes that only consist of a repeating digit are not allowed.\n" " - `123-45-678` and `876-54-321` are not allowed.\n" " If this option is not present, a random setup code is generated.\n" " \n" " "B("--setup-id")" "U("Setup ID")"\n" " Provisions accessory setup information using a specific setup ID.\n" " Format is `XXXX` with X being a digit from 0-9 or a character from A-Z.\n" " - Lowercase characters are not allowed.\n" " If this option is not present, a random setup ID is generated.\n" "\n" B("OUTPUT")"\n" " Output consists of a series of lines in a machine-readable format.\n" " Lines are terminated with a \\n character.\n" " \n" " 1. "B("Output format version")" which is `1` for this version.\n" " \n" " 2. "B("Setup code")" in format `XXX-XX-XXX` with X being a digit from 0-9.\n" " - Must be deployed to the accessory if it has a programmable NFC tag but\n" " is not connected to a display.\n" " - Must be printed on labels affixed to the accessory and its packaging\n" " if the accessory is not connected to a display.\n" " \n" " 3. "B("SRP salt")" as a hexadecimal string.\n" " - Must be deployed to the accessory if it is not connected to a display.\n" " \n" " 4. "B("SRP verifier")" as a hexadecimal string.\n" " - Must be deployed to the accessory if it is not connected to a display.\n" " \n" " 5. "B("Setup ID")" in format `XXX` with X being a digit from 0-9 or a\n" " character from A-Z.\n" " - Must be deployed to the accessory.\n" " \n" " 6. "B("Setup payload")" as a string.\n" " - Must be printed on labels affixed to the accessory and its packaging\n" " if the accessory is not connected to a display.\n" "\n" B("EXAMPLE")"\n" " Example output for an "B("Outlet")" (category identifier "B("7")") accessory supporting\n" " "B("HAP over IP")" and "B("Wi-Fi Accessory Configuration")" with setup code `"B("518-08-582")"`\n" " and setup ID `"B("7OSX")"`.\n" " \n" "%s", HAPGetVersion(), HAPGetBuild(), categoryDescriptions, exampleOutput); #undef B #undef U exit(EXIT_SUCCESS); } // Parse arguments. const char* fixedSetupCode = NULL; const char* fixedSetupID = NULL; HAPAccessorySetupSetupPayloadFlags flags = { .isPaired = false, .ipSupported = false, .bleSupported = false }; HAPAccessoryCategory category = (HAPAccessoryCategory) 0; for (int i = 1; i < argc; i++) { if (HAPStringAreEqual(argv[i], "--ip")) { if (flags.ipSupported) { fprintf(stderr, "--ip specified multiple times."); exit(EXIT_FAILURE); } flags.ipSupported = true; } else if (HAPStringAreEqual(argv[i], "--ble")) { if (flags.bleSupported) { fprintf(stderr, "--ble specified multiple times."); exit(EXIT_FAILURE); } flags.bleSupported = true; } else if (HAPStringAreEqual(argv[i], "--category")) { if (i == argc) { fprintf(stderr, "--category specified without accessory category identifier."); exit(EXIT_FAILURE); } i++; uint64_t categoryID; err = HAPUInt64FromString(argv[i], &categoryID); if (err) { HAPAssert(err == kHAPError_InvalidData); fprintf(stderr, "--category specified with malformed accessory category identifier."); exit(EXIT_FAILURE); } if (!categoryID || categoryID > UINT8_MAX) { fprintf(stderr, "--category specified with out-of-range accessory category identifier."); exit(EXIT_FAILURE); } if (category) { fprintf(stderr, "--category specified multiple times."); exit(EXIT_FAILURE); } category = (HAPAccessoryCategory) categoryID; } else if (HAPStringAreEqual(argv[i], "--setup-code")) { if (i == argc) { fprintf(stderr, "--setup-code specified without setup code."); exit(EXIT_FAILURE); } i++; if (!HAPAccessorySetupIsValidSetupCode(argv[i])) { fprintf(stderr, "--setup-code specified with invalid setup code."); exit(EXIT_FAILURE); } if (fixedSetupCode) { fprintf(stderr, "--setup-code specified multiple times."); exit(EXIT_FAILURE); } fixedSetupCode = argv[i]; } else if (HAPStringAreEqual(argv[i], "--setup-id")) { if (i == argc) { fprintf(stderr, "--setup-id specified without setup ID."); exit(EXIT_FAILURE); } i++; if (!HAPAccessorySetupIsValidSetupID(argv[i])) { fprintf(stderr, "--setup-id specified with invalid setup ID."); exit(EXIT_FAILURE); } if (fixedSetupID) { fprintf(stderr, "--setup-id specified multiple times."); exit(EXIT_FAILURE); } fixedSetupID = argv[i]; } else { fprintf(stderr, "Too many arguments specified."); exit(EXIT_FAILURE); } } if (!category) { fprintf(stderr, "No accessory category identifier specified."); exit(EXIT_FAILURE); } if (!flags.ipSupported && !flags.bleSupported) { fprintf(stderr, "No transport specified."); exit(EXIT_FAILURE); } // Setup code. HAPSetupCode setupCode; if (fixedSetupCode) { HAPRawBufferCopyBytes(setupCode.stringValue, fixedSetupCode, sizeof setupCode.stringValue); } else { HAPAccessorySetupGenerateRandomSetupCode(&setupCode); } // Setup info. HAPSetupInfo setupInfo; HAPPlatformRandomNumberFill(setupInfo.salt, sizeof setupInfo.salt); const uint8_t srpUserName[] = "Pair-Setup"; HAP_srp_verifier( setupInfo.verifier, setupInfo.salt, srpUserName, sizeof srpUserName - 1, (const uint8_t*) setupCode.stringValue, sizeof setupCode.stringValue - 1); // Setup ID. HAPSetupID setupID; if (fixedSetupID) { HAPRawBufferCopyBytes(setupID.stringValue, fixedSetupID, sizeof setupID.stringValue); } else { HAPAccessorySetupGenerateRandomSetupID(&setupID); } // Setup payload. HAPSetupPayload setupPayload; HAPAccessorySetupGetSetupPayload(&setupPayload, &setupCode, &setupID, flags, category); // Output. printf("1\n"); printf("%s\n", setupCode.stringValue); for (size_t i = 0; i < sizeof setupInfo.salt; i++) { printf("%02X", setupInfo.salt[i]); } printf("\n"); for (size_t i = 0; i < sizeof setupInfo.verifier; i++) { printf("%02X", setupInfo.verifier[i]); } printf("\n"); printf("%s\n", setupID.stringValue); printf("%s\n", setupPayload.stringValue); return 0; }