diff --git a/CMakeLists.txt b/CMakeLists.txt index 9927bc7..444ce32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,9 +17,12 @@ cmake_minimum_required(VERSION 3.13) +set(USB_VID 0x2E8A) +set(USB_PID 0x10FE) + if(ESP_PLATFORM) set(DENABLE_POWER_ON_RESET 0) -set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src) +set(EXTRA_COMPONENT_DIRS pico-keys-sdk/config/esp32/components src/fido) include($ENV{IDF_PATH}/tools/cmake/project.cmake) else() @@ -168,7 +171,7 @@ if(ENABLE_EMULATION) -Wl,--gc-sections ) endif (APPLE) - target_link_libraries(pico_fido PRIVATE pthread m) + target_link_libraries(pico_fido PRIVATE pico_keys_sdk mbedtls pthread m) else() target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id pico_aon_timer tinyusb_device tinyusb_board) pico_add_extra_outputs(${CMAKE_PROJECT_NAME}) diff --git a/README.md b/README.md index c45d55d..0e48e7a 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,9 @@ Microcontrollers RP2350 and ESP32-S3 are designed to support secure environments If you own a Raspberry Pico (RP2040 or RP2350), go to [Download page](https://www.picokeys.com/getting-started/), select your vendor and model and download the proper firmware; or go to [Release page](https://www.github.com/polhenarejos/pico-fido/releases/) and download the UF2 file for your board. -Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App"). +UF2 files are shiped with a VID/PID granted by RaspberryPi (2E8A:10FE). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App"). -You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own. +You can use whatever VID/PID for internal purposes, but remember that you are not authorized to distribute the binary with a VID/PID that you do not own. Note that the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App") is the most recommended. diff --git a/pico-keys-sdk b/pico-keys-sdk index 8a0ef0b..668b1ac 160000 --- a/pico-keys-sdk +++ b/pico-keys-sdk @@ -1 +1 @@ -Subproject commit 8a0ef0b30cf4fa586b415a37578695efe35a04be +Subproject commit 668b1ac1dd2fdd0934ae1f92bea8e266e8319f4f diff --git a/src/fido/CMakeLists.txt b/src/fido/CMakeLists.txt index e6dfbac..e9c6774 100644 --- a/src/fido/CMakeLists.txt +++ b/src/fido/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register( SRCS ${SOURCES} - INCLUDE_DIRS . ../../pico-keys-sdk/src ../../pico-keys-sdk/src/fs ../../pico-keys-sdk/src/rng ../../pico-keys-sdk/src/usb ../../pico-keys-sdk/tinycbor/src - REQUIRES esp_tinyusb mbedtls efuse + INCLUDE_DIRS . + REQUIRES esp_tinyusb mbedtls efuse pico-keys-sdk ) idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON) diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index 7a4097c..b2c6732 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -419,6 +419,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) { hsh[1] = pin_len; hsh[2] = 1; // New format indicator mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash); + mbedtls_platform_zeroize(paddedNewPin, sizeof(paddedNewPin)); pin_derive_verifier(dhash, 16, hsh + 3); file_put_data(ef_pin, hsh, sizeof(hsh)); low_flash_available(); @@ -430,6 +431,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) { } mbedtls_platform_zeroize(hsh, sizeof(hsh)); mbedtls_platform_zeroize(dhash, sizeof(dhash)); + needs_power_cycle = false; + goto err; //No return } else if (subcommand == 0x4) { //changePIN @@ -458,6 +461,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) { if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) { CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER); } + if (needs_power_cycle) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED); + } uint8_t sharedSecret[64]; int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret); if (ret != 0) { @@ -587,6 +593,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) { low_flash_available(); resetPinUvAuthToken(); resetPersistentPinUvAuthToken(); + needs_power_cycle = false; goto err; // No return } else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions @@ -623,6 +630,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) { if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) { CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER); } + if (needs_power_cycle) { + CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED); + } uint8_t sharedSecret[64]; int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret); if (ret != 0) { @@ -720,6 +730,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) { CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1)); CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02)); CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff)); + needs_power_cycle = false; } else { CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); diff --git a/src/fido/cbor_get_assertion.c b/src/fido/cbor_get_assertion.c index fac8e01..3ba9fb5 100644 --- a/src/fido/cbor_get_assertion.c +++ b/src/fido/cbor_get_assertion.c @@ -428,7 +428,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) { if (!silent) { for (int i = 0; i < numberOfCredentials; i++) { for (int j = i + 1; j < numberOfCredentials; j++) { - if (creds[j].creation > creds[i].creation) { + if (creds[j].board_creation > creds[i].board_creation) { Credential tmp = creds[j]; creds[j] = creds[i]; creds[i] = tmp; diff --git a/src/fido/cbor_vendor.c b/src/fido/cbor_vendor.c index e102aab..b59abb3 100644 --- a/src/fido/cbor_vendor.c +++ b/src/fido/cbor_vendor.c @@ -206,7 +206,12 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) { uint8_t buffer[1024]; mbedtls_ecdsa_context ekey; mbedtls_ecdsa_init(&ekey); - int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev)); + uint8_t keydev[32] = {0}; + if (load_keydev(keydev) != 0) { + CBOR_ERROR(CTAP1_ERR_OTHER); + } + int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, keydev, 32); + mbedtls_platform_zeroize(keydev, sizeof(keydev)); if (ret != 0) { mbedtls_ecdsa_free(&ekey); CBOR_ERROR(CTAP2_ERR_PROCESSING); diff --git a/src/fido/credential.c b/src/fido/credential.c index 645417d..46b079a 100644 --- a/src/fido/credential.c +++ b/src/fido/credential.c @@ -29,6 +29,7 @@ #include "files.h" #include "otp.h" +extern bool has_set_rtc(); int credential_derive_chacha_key(uint8_t *outk, const uint8_t *); static int credential_silent_tag(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, uint8_t *outk) { @@ -148,6 +149,10 @@ int credential_create(CborCharString *rpId, } CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2)); } + if (has_set_rtc()) { + CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C)); + CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t) get_rtc_time())); + } CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id); *cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + (uint16_t)rs + CRED_TAG_LEN + CRED_SILENT_TAG_LEN; @@ -220,7 +225,7 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r CBOR_FIELD_GET_TEXT(cred->userDisplayName, 1); } else if (val_u == 0x06) { - CBOR_FIELD_GET_UINT(cred->creation, 1); + CBOR_FIELD_GET_UINT(cred->board_creation, 1); } else if (val_u == 0x07) { cred->extensions.present = true; @@ -255,6 +260,9 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r } CBOR_PARSE_MAP_END(_f1, 2); } + else if (val_u == 0x0C) { + CBOR_FIELD_GET_UINT(cred->rtc_creation, 1); + } else { CBOR_ADVANCE(1); } diff --git a/src/fido/credential.h b/src/fido/credential.h index a77b72f..76f4e67 100644 --- a/src/fido/credential.h +++ b/src/fido/credential.h @@ -43,7 +43,7 @@ typedef struct Credential { CborByteString userId; CborCharString userName; CborCharString userDisplayName; - uint64_t creation; + uint64_t board_creation; CredExtensions extensions; const bool *use_sign_count; int64_t alg; @@ -51,6 +51,7 @@ typedef struct Credential { CborByteString id; CredOptions opts; bool present; + uint64_t rtc_creation; } Credential; #define CRED_PROT_UV_OPTIONAL 0x01 diff --git a/src/fido/fido.c b/src/fido/fido.c index d6cb9d9..af4f790 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -53,6 +53,11 @@ const uint8_t fido_aid[] = { 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01 }; +const uint8_t fido_aid_backup[] = { + 8, + 0xB0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01 +}; + const uint8_t atr_fido[] = { 23, 0x3b, 0xfd, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x15, 0x80, 0x73, 0xc0, 0x21, 0xc0, 0x57, 0x59, @@ -86,6 +91,7 @@ INITIALIZER ( fido_ctor ) { get_version_major = fido_get_version_major; get_version_minor = fido_get_version_minor; register_app(fido_select, fido_aid); + register_app(fido_select, fido_aid_backup); } int fido_unload() { @@ -477,11 +483,13 @@ void scan_all() { } extern void init_otp(); +extern bool needs_power_cycle; void init_fido() { scan_all(); #ifdef ENABLE_OTP_APP init_otp(); #endif + needs_power_cycle = false; } bool wait_button_pressed() { @@ -530,18 +538,32 @@ extern int cmd_register(); extern int cmd_authenticate(); extern int cmd_version(); extern int cbor_parse(int, uint8_t *, size_t); +extern int cbor_vendor(const uint8_t *data, size_t len); extern void driver_init_hid(); #define CTAP_CBOR 0x10 +int cmd_vendor() { + uint8_t *old_buf = res_APDU; + driver_init_hid(); + int ret = cbor_vendor(apdu.data, apdu.nc); + res_APDU = old_buf; + if (ret != 0) { + return SW_EXEC_ERROR(); + } + res_APDU_size += 1; + memcpy(res_APDU, ctap_resp->init.data, res_APDU_size); + return SW_OK(); +} + int cmd_cbor() { uint8_t *old_buf = res_APDU; driver_init_hid(); int ret = cbor_parse(0x90, apdu.data, apdu.nc); + res_APDU = old_buf; if (ret != 0) { return SW_EXEC_ERROR(); } - res_APDU = old_buf; res_APDU_size += 1; memcpy(res_APDU, ctap_resp->init.data, res_APDU_size); return SW_OK(); @@ -552,6 +574,7 @@ static const cmd_t cmds[] = { { CTAP_AUTHENTICATE, cmd_authenticate }, { CTAP_VERSION, cmd_version }, { CTAP_CBOR, cmd_cbor }, + { 0x41, cmd_vendor }, { 0x00, 0x0 } }; diff --git a/src/fido/oath.c b/src/fido/oath.c index 0c293c7..f8de9a7 100644 --- a/src/fido/oath.c +++ b/src/fido/oath.c @@ -94,22 +94,22 @@ int oath_select(app_t *a, uint8_t force) { res_APDU[res_APDU_size++] = sizeof(challenge); memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge)); res_APDU_size += sizeof(challenge); - } - file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF); - if (file_has_data(ef_otp_pin)) { - const uint8_t *pin_data = file_get_data(ef_otp_pin); - res_APDU[res_APDU_size++] = TAG_PIN_COUNTER; + res_APDU[res_APDU_size++] = TAG_ALGO; res_APDU[res_APDU_size++] = 1; - res_APDU[res_APDU_size++] = *pin_data; + res_APDU[res_APDU_size++] = ALG_HMAC_SHA1; } - res_APDU[res_APDU_size++] = TAG_ALGO; - res_APDU[res_APDU_size++] = 1; - res_APDU[res_APDU_size++] = ALG_HMAC_SHA1; if (is_nk) { res_APDU[res_APDU_size++] = TAG_SERIAL_NUMBER; res_APDU[res_APDU_size++] = 8; memcpy(res_APDU + res_APDU_size, pico_serial_str, 8); res_APDU_size += 8; + file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF); + if (file_has_data(ef_otp_pin)) { + const uint8_t *pin_data = file_get_data(ef_otp_pin); + res_APDU[res_APDU_size++] = TAG_PIN_COUNTER; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size++] = *pin_data; + } } apdu.ne = res_APDU_size; return PICOKEY_OK; diff --git a/tests/pico-fido/test_010_pin.py b/tests/pico-fido/test_010_pin.py index 0e947f4..06443ae 100644 --- a/tests/pico-fido/test_010_pin.py +++ b/tests/pico-fido/test_010_pin.py @@ -51,7 +51,7 @@ def test_lockout(device, resetdevice, client_pin): res = client_pin.get_pin_retries() assert res[0] == attempts - if err == CtapError.ERR.PIN_AUTH_BLOCKED: + if e.value.code == CtapError.ERR.PIN_AUTH_BLOCKED: device.reboot() client_pin = ClientPin(resetdevice.client()._backend.ctap2) diff --git a/tools/words.py b/tools/words.py deleted file mode 100644 index 4e17003..0000000 --- a/tools/words.py +++ /dev/null @@ -1 +0,0 @@ -words = ['shape', 'reject', 'trains', 'skirt', 'magnificent', 'sloppy', 'harsh', 'valuable', 'yak', 'knit', 'ghost', 'offer', 'necessary', 'hilarious', 'wealth', 'far-flung', 'scary', 'yoke', 'cruel', 'unbecoming', 'sin', 'draconian', 'undress', 'various', 'annoying', 'great', 'flashy', 'materialistic', 'spiritual', 'writer', 'prickly', 'therapeutic', 'curvy', 'terrific', 'paste', 'reminiscent', 'range', 'female', 'trees', 'cat', 'cow', 'furtive', 'run', 'swing', 'early', 'marked', 'terrify', 'wind', 'stretch', 'bells', 'eminent', 'deserted', 'nondescript', 'basketball', 'wine', 'boast', 'food', 'fowl', 'cheerful', 'kettle', 'jazzy', 'greasy', 'sick', 'sort', 'excuse', 'impolite', 'attraction', 'ultra', 'axiomatic', 'promise', 'blood', 'camp', 'macho', 'material', 'earthquake', 'window', 'taste', 'buzz', 'identify', 'education', 'hapless', 'perfect', 'stare', 'bell', 'snail', 'powerful', 'unarmed', 'company', 'mist', 'seat', 'empty', 'plantation', 'sheet', 'pinch', 'mate', 'null', 'obtain', 'jumbled', 'awful', 'loving', 'moaning', 'chief', 'lethal', 'crabby', 'preserve', 'scratch', 'tax', 'ink', 'quack', 'produce', 'madly', 'open', 'cagey', 'mountain', 'irritating', 'attack', 'oven', 'faithful', 'sharp', 'uncle', 'wicked', 'rigid', 'theory', 'try', 'wobble', 'cloth', 'wood', 'strip', 'surround', 'uneven', 'reach', 'bright', 'agonizing', 'crate', 'rock', 'moor', 'bike', 'tired', 'tenuous', 'beg', 'boiling', 'creator', 'chance', 'scared', 'flowers', 'eye', 'limping', 'yielding', 'understood', 'longing', 'love', 'quirky', 'homely', 'crown', 'waste', 'rustic', 'thoughtless', 'flimsy', 'glossy', 'part', 'good', 'receive', 'division', 'snails', 'attractive', 'cuddly', 'release', 'gamy', 'name', 'decorate', 'move', 'plot', 'star', 'writing', 'house', 'thaw', 'robust', 'testy', 'elated', 'calm', 'sneaky', 'tomatoes', 'disgusting', 'fence', 'capable', 'rural', 'melodic', 'hysterical', 'entertaining', 'man', 'ill-informed', 'misty', 'scatter', 'jump', 'rightful', 'educated', 'adjoining', 'abashed', 'disturbed', 'fade', 'stamp', 'stream', 'boorish', 'two', 'worry', 'thank', 'trot', 'delicate', 'argument', 'creepy', 'dependent', 'rough', 'symptomatic', 'authority', 'actually', 'join', 'ignorant', 'radiate', 'aboard', 'bead', 'energetic', 'abandoned', 'shy', 'raise', 'blind', 'talented', 'stroke', 'devilish', 'death', 'type', 'taboo', 'depressed', 'level', 'equable', 'activity', 'accidental', 'ski', 'purring', 'juicy', 'maid', 'decide', 'overconfident', 'pipe', 'afternoon', 'file', 'peaceful', 'delicious', 'continue', 'imperfect', 'glow', 'haircut', 'frequent', 'arrogant', 'toothbrush', 'refuse', 'meek', 'spectacular', 'homeless', 'half', 'grateful', 'old', 'psychedelic', 'obnoxious', 'bridge', 'pies', 'invention', 'hose', 'flood', 'badge', 'unhealthy', 'deadpan', 'owe', 'undesirable', 'dock', 'insect', 'rose', 'boil', 'warlike', 'abortive', 'wriggle', 'excellent', 'fear', 'nation', 'unknown', 'press', 'wasteful', 'boundary', 'abrasive', 'glamorous', 'bag', 'army', 'punishment', 'lettuce', 'system', 'queen', 'branch', 'fretful', 'flawless', 'flap', 'observation', 'juice', 'punch', 'pretend', 'pocket', 'simplistic', 'serve', 'oval', 'weary', 'mourn', 'river', 'selective', 'heat', 'include', 'filthy', 'amuck', 'tender', 'mix', 'towering', 'shut', 'sponge', 'transport', 'communicate', 'squalid', 'unit', 'smoke', 'carriage', 'clumsy', 'abiding', 'fish', 'grease', 'frogs', 'classy', 'slim', 'needless', 'nasty', 'equal', 'relax', 'noise', 'wiry', 'cheer', 'gold', 'insidious', 'bounce', 'past', 'same', 'design', 'power', 'probable', 'sheep', 'trucks', 'agree', 'exclusive', 'smoggy', 'fumbling', 'exercise', 'astonishing', 'stupendous', 'bad', 'charming', 'birthday', 'spiteful', 'toys', 'plant', 'abnormal', 'racial', 'squeamish', 'decorous', 'woman', 'protest', 'literate', 'inquisitive', 'skillful', 'sidewalk', 'internal', 'judicious', 'hall', 'paltry', 'safe', 'deeply', 'melt', 'direction', 'relieved', 'red', 'future', 'wander', 'loaf', 'fit', 'telling', 'aquatic', 'notice', 'apathetic', 'flame', 'look', 'show', 'sable', 'hallowed', 'miss', 'premium', 'pin', 'handsome', 'volleyball', 'adhesive', 'mind', 'helpless', 'lighten', 'industry', 'mother', 'feigned', 'permit', 'plain', 'flight', 'burn', 'unruly', 'damp', 'digestion', 'air', 'x-ray', 'hook', 'squealing', 'ablaze', 'cowardly', 'dinner', 'closed', 'things', 'toothpaste', 'gaudy', 'pretty', 'finicky', 'retire', 'amount', 'sticky', 'bless', 'visitor', 'screw', 'subsequent', 'absorbing', 'left', 'parched', 'guarded', 'ants', 'squeak', 'glue', 'umbrella', 'government', 'peep', 'unkempt', 'hard', 'foolish', 'daffy', 'letters', 'ask', 'language', 'harm', 'greedy', 'wanting', 'size', 'way', 'cute', 'satisfying', 'oranges', 'second', 'pigs', 'intend', 'cast', 'somber', 'assorted', 'decision', 'ugliest', 'chop', 'excite', 'lunch', 'possessive', 'volatile', 'pricey', 'cooing', 'tearful', 'tan', 'whirl', 'worm', 'curve', 'squash', 'ceaseless', 'suck', 'obsolete', 'rabbit', 'door', 'flag', 'program', 'gullible', 'disastrous', 'lopsided', 'substance', 'kind', 'feeling', 'shiny', 'hole', 'club', 'troubled', 'heady', 'rambunctious', 'questionable', 'massive', 'balance', 'envious', 'throne', 'condemned', 'zippy', 'interesting', 'zephyr', 'thundering', 'paddle', 'nimble', 'stranger', 'fertile', 'prevent', 'hydrant', 'messy', 'floor', 'boy', 'unused', 'meal', 'occur', 'rabbits', 'spicy', 'highfalutin', 'parcel', 'action', 'dispensable', 'tree', 'better', 'bushes', 'wheel', 'fact', 'guitar', 'courageous', 'threatening', 'typical', 'paper', 'flaky', 'spoon', 'sparkle', 'sturdy', 'march', 'detail', 'coal', 'ritzy', 'team', 'duck', 'drag', 'defective', 'chivalrous', 'zesty', 'disapprove', 'cure', 'succeed', 'glib', 'grubby', 'young', 'dashing', 'clover', 'signal', 'thunder', 'illegal', 'tumble', 'wrestle', 'soft', 'waggish', 'merciful', 'stain', 'receptive', 'choke', 'jail', 'nifty', 'coast', 'berserk', 'nod', 'chilly', 'magic', 'cool', 'employ', 'alert', 'low', 'handy', 'card', 'fail', 'plate', 'lake', 'vase', 'venomous', 'violet', 'form', 'daughter', 'guttural', 'appliance', 'wren', 'doll', 'brainy', 'extend', 'trick', 'flow', 'miscreant', 'yawn', 'son', 'street', 'land', 'wing', 'grandmother', 'certain', 'rhythm', 'pray', 'cover', 'swanky', 'wrathful', 'saw', 'strong', 'amazing', 'blue', 'slave', 'hurt', 'string', 'person', 'slow', 'warm', 'babies', 'tent', 'truthful', 'white', 'bustling', 'mouth', 'property', 'art', 'oceanic', 'zip', 'parallel', 'nappy', 'dime', 'porter', 'puzzling', 'appear', 'workable', 'hesitant', 'stuff', 'defeated', 'chunky', 'bath', 'mere', 'argue', 'mature', 'waiting', 'harmonious', 'command', 'weak', 'precede', 'disagree', 'colorful', 'unequaled', 'untidy', 'tug', 'embarrassed', 'work', 'broken', 'free', 'suspect', 'event', 'limit', 'cook', 'used', 'curved', 'current', 'jolly', 'ring', 'grip', 'bizarre', 'powder', 'stew', 'careful', 'cellar', 'rely', 'ill-fated', 'sand', 'respect', 'hard-to-find', 'mixed', 'scent', 'bite', 'confuse', 'burst', 'reflect', 'breakable', 'useless', 'combative', 'trust', 'godly', 'expect', 'spiky', 'statement', 'alarm', 'ahead', 'black-and-white', 'cumbersome', 'bore', 'interfere', 'winter', 'picture', 'meaty', 'ragged', 'grouchy', 'impress', 'potato', 'edge', 'cent', 'prefer', 'roasted', 'dance', 'spot', 'itchy', 'brash', 'motion', 'bawdy', 'tour', 'old-fashioned', 'muddled', 'downtown', 'doubt', 'fortunate', 'futuristic', 'exchange', 'clean', 'flower', 'whole', 'whine', 'versed', 'groovy', 'muddle', 'divide', 'huge', 'toad', 'fast', 'lonely', 'historical', 'annoy', 'mitten', 'cattle', 'unusual', 'houses', 'magical', 'outrageous', 'pass', 'belligerent', 'knot', 'believe', 'mess up', 'dam', 'market', 'protect', 'lame', 'tame', 'sedate', 'fanatical', 'uttermost', 'list', 'dysfunctional', 'drawer', 'dirty', 'structure', 'malicious', 'hat', 'melted', 'race', 'tidy', 'pickle', 'flagrant', 'tendency', 'torpid', 'stimulating', 'cup', 'doctor', 'color', 'skip', 'bikes', 'temper', 'offbeat', 'value', 'absent', 'adorable', 'arrange', 'fine', 'space', 'changeable', 'eggs', 'remind', 'general', 'clam', 'dream', 'loss', 'repair', 'alcoholic', 'macabre', 'imminent', 'canvas', 'subdued', 'rake', 'roof', 'solid', 'military', 'physical', 'useful', 'concern', 'dislike', 'lush', 'righteous', 'wandering', 'shelf', 'rare', 'zealous', 'linen', 'grain', 'tasteful', 'camera', 'can', 'pail', 'add', 'sophisticated', 'upset', 'tremble', 'flippant', 'nail', 'snakes', 'film', 'risk', 'synonymous', 'cloistered', 'visit', 'call', 'cross', 'agreement', 'husky', 'wash', 'windy', 'hum', 'acrid', 'odd', 'depend', 'elastic', 'groan', 'ban', 'sudden', 'follow', 'rail', 'soothe', 'remove', 'intelligent', 'domineering', 'grieving', 'gaze', 'knowledge', 'birds', 'boring', 'lumpy', 'embarrass', 'many', 'drum', 'voyage', 'icicle', 'labored', 'burly', 'superficial', 'truculent', 'ice', 'obese', 'royal', 'ugly', 'van', 'high-pitched', 'tie', 'sigh', 'vacation', 'dusty', 'unfasten', 'woozy', 'treat', 'dreary', 'mellow', 'bump', 'tail', 'freezing', 'rat', 'story', 'tickle', 'onerous', 'ball', 'hungry', 'brick', 'political', 'train', 'library', 'bubble', 'imported', 'helpful', 'dull', 'unlock', 'popcorn', 'stiff', 'moan', 'crow', 'cultured', 'scale', 'force', 'zany', 'greet', 'books', 'brave', 'sweet', 'bewildered', 'thoughtful', 'dirt', 'nippy', 'pear', 'easy', 'side', 'multiply', 'gather', 'fill', 'enthusiastic', 'plan', 'living', 'blot', 'number', 'degree', 'idiotic', 'profit', 'impossible', 'nervous', 'support', 'vigorous', 'shocking', 'bottle', 'boat', 'memorize', 'trousers', 'elegant', 'unsightly', 'important', 'fabulous', 'weigh', 'erratic', 'sassy', 'absurd', 'special', 'dog', 'jittery', 'kick', 'chess', 'travel', 'song', 'sad', 'vest', 'productive', 'scene', 'stupid', 'delight', 'statuesque', 'breathe', 'awesome', 'injure', 'beam', 'class', 'screeching', 'hour', 'blow', 'bolt', 'profuse', 'bored', 'overt', 'famous', 'scissors', 'unnatural', 'kitty', 'trip', 'ruin', 'incandescent', 'jaded', 'fax', 'top', 'adjustment', 'leg', 'bait', 'cheap', 'zebra', 'develop', 'coordinated', 'educate', 'engine', 'cream', 'nine', 'admire', 'remember', 'grey', 'act', 'earthy', 'quiet', 'wound', 'awake', 'ticket', 'mundane', 'drunk', 'squeal', 'chemical', 'colour', 'connection', 'encouraging', 'roomy', 'protective', 'vessel', 'steady', 'well-groomed', 'moldy', 'cart', 'coil', 'tall', 'subtract', 'rule', 'tricky', 'alike', 'ajar', 'crash', 'mushy', 'spotless', 'volcano', 'calculate', 'uptight', 'tawdry', 'iron', 'aftermath', 'chalk', 'difficult', 'three', 'flash', 'sun', 'numerous', 'talk', 'available', 'scattered', 'striped', 'smiling', 'seashore', 'face', 'sugar', 'tick', 'overrated', 'poke', 'suit', 'elite', 'liquid', 'reply', 'habitual', 'magenta', 'clear', 'tongue', 'field', 'raspy', 'temporary', 'provide', 'sleet', 'quickest', 'scream', 'spark', 'approve', 'detect', 'exist', 'entertain', 'plastic', 'wilderness', 'crowd', 'square', 'upbeat', 'ship', 'regular', 'analyze', 'hurried', 'bathe', 'sea', 'want', 'copy', 'debt', 'pest', 'practice', 'glistening', 'tight', 'periodic', 'ubiquitous', 'aloof', 'robin', 'smile', 'confused', 'admit', 'skinny', 'whistle', 'secretary', 'satisfy', 'bulb', 'beautiful', 'unique', 'boot', 'lovely', 'inconclusive', 'childlike', 'pleasure', 'slap', 'ashamed', 'humdrum', 'interrupt', 'ignore', 'page', 'eatable', 'married', 'delay', 'object', 'foot', 'passenger', 'extra-small', 'instrument', 'industrious', 'resolute', 'dangerous', 'rate', 'big', 'deer', 'line', 'high', 'abject', 'hunt', 'lively', 'book', 'heal', 'suspend', 'smooth', 'hobbies', 'achiever', 'spring', 'possible', 'mice', 'elfin', 'snotty', 'shop', 'inexpensive', 'throat', 'suppose', 'straw', 'chicken', 'omniscient', 'steep', 'mean', 'shoe', 'moon', 'avoid', 'vanish', 'mysterious', 'fresh', 'meat', 'simple', 'wild', 'cheat', 'erect', 'kaput', 'illustrious', 'didactic', 'plough', 'fork', 'icy', 'joke', 'exotic', 'notebook', 'calendar', 'encourage', 'root', 'sulky', 'loud', 'reflective', 'school', 'expand', 'jelly', 'irritate', 'anger', 'addition', 'addicted', 'boundless', 'representative', 'lacking', 'airport', 'handle', 'hammer', 'aspiring', 'fasten', 'painful', 'cannon', 'noisy', 'offend', 'hateful', 'bedroom', 'steer', 'apparatus', 'realize', 'pastoral', 'science', 'one', 'oatmeal', 'zoo', 'dead', 'quaint', 'amused', 'exciting', 'pale', 'stem', 'end', 'discussion', 'arrive', 'bang', 'selection', 'tough', 'cushion', 'afterthought', 'dramatic', 'governor', 'sack', 'yell', 'learn', 'direful', 'locket', 'skin', 'describe', 'clever', 'obedient', 'real', 'attempt', 'surprise', 'chin', 'trace', 'pizzas', 'cough', 'weight', 'repulsive', 'hop', 'piquant', 'expert', 'box', 'dress', 'penitent', 'plants', 'substantial', 'live', 'laughable', 'abhorrent', 'friend', 'growth', 'delightful', 'sweltering', 'finger', 'monkey', 'second-hand', 'compete', 'north', 'tasteless', 'truck', 'complete', 'outgoing', 'parsimonious', 'unite', 'faulty', 'separate', 'crime', 'stitch', 'wonder', 'wary', 'squeeze', 'measure', 'treatment', 'ambiguous', 'pollution', 'cold', 'laborer', 'night', 'ambitious', 'pencil', 'bird', 'nosy', 'effect', 'soap', 'acceptable', 'pathetic', 'dust', 'spoil', 'amusing', 'lip', 'elderly', 'optimal', 'toe', 'wire', 'angry', 'example', 'heap', 'eyes', 'sneeze', 'six', 'best', 'beds', 'disgusted', 'summer', 'hate', 'petite', 'kindhearted', 'damaging', 'explode', 'steel', 'nonchalant', 'thick', 'fire', 'tip', 'unequal', 'smell', 'peel', 'lazy', 'wealthy', 'enchanting', 'chase', 'sound', 'miniature', 'efficacious', 'bouncy', 'verdant', 'abaft', 'decay', 'silent', 'late', 'grotesque', 'secretive', 'holistic', 'wiggly', 'nose', 'tranquil', 'sleep', 'pat', 'count', 'shave', 'wipe', 'seemly', 'flock', 'dare', 'wonderful', 'road', 'machine', 'children', 'stir', 'clap', 'apparel', 'strengthen', 'knock', 'pets', 'muscle', 'allow', 'insurance', 'thin', 'destruction', 'superb', 'car', 'nauseating', 'few', 'deceive', 'creature', 'momentous', 'tacit', 'curtain', 'pause', 'cooperative', 'humorous', 'hope', 'unbiased', 'clammy', 'consider', 'careless', 'sign', 'aware', 'absorbed', 'silver', 'back', 'adventurous', 'stay', 'brief', 'business', 'attract', 'pack', 'dear', 'sense', 'fireman', 'mark', 'scribble', 'girls', 'word', 'change', 'shelter', 'natural', 'funny', 'kiss', 'breath', 'holiday', 'celery', 'borrow', 'mountainous', 'coach', 'nebulous', 'harbor', 'pumped', 'round', 'voracious', 'planes', 'brush', 'common', 'trite', 'dizzy', 'grate', 'five', 'copper', 'wretched', 'fat', 'position', 'blink', 'enjoy', 'cluttered', 'legal', 'perform', 'measly', 'collect', 'tow', 'report', 'friction', 'splendid', 'silky', 'soak', 'horses', 'vegetable', 'frighten', 'regret', 'scintillating', 'welcome', 'scandalous', 'spill', 'wool', 'shiver', 'silly', 'title', 'handsomely', 'trouble', 'juggle', 'whispering', 'day', 'claim', 'women', 'repeat', 'hellish', 'rainy', 'first', 'expensive', 'fearless', 'yummy', 'wreck', 'reason', 'cable', 'prepare', 'step', 'plucky', 'bear', 'victorious', 'milky', 'sore', 'knee', 'lunchroom', 'unable', 'touch', 'trade', 'town', 'neighborly', 'approval', 'income', 'steadfast', 'scrub', 'trap', 'accessible', 'apologise', 'scare', 'bow', 'noxious', 'snatch', 'mine', 'rhetorical', 'jar', 'skate', 'staking', 'stop', 'earn', 'lamentable', 'noiseless', 'narrow', 'ruthless', 'harmony', 'abstracted', 'button', 'dynamic', 'bite-sized', 'quilt', 'leather', 'busy', 'marry', 'festive', 'thirsty', 'voice', 'majestic', 'jumpy', 'immense', 'obscene', 'pine', 'determined', 'lavish', 'agreeable', 'savory', 'unsuitable', 'grandfather', 'fragile', 'settle', 'self', 'pig', 'utter', 'stove', 'cherries', 'different', 'efficient', 'secret', 'organic', 'sister', 'cake', 'fall', 'exuberant', 'opposite', 'well-to-do', 'caption', 'vacuous', 'alive', 'smash', 'zonked', 'black', 'religion', 'maddening', 'wet', 'collar', 'sleepy', 'horn', 'seed', 'punish', 'last', 'advice', 'well-made', 'hot', 'wish', 'horse', 'credit', 'blush', 'orange', 'use', 'quixotic', 'murky', 'reproduce', 'wave', 'hurry', 'bit', 'infamous', 'soup', 'flowery', 'pen', 'brake', 'pump', 'unaccountable', 'actor', 'building', 'yarn', 'gruesome', 'angle', 'place', 'cut', 'obsequious', 'texture', 'short', 'crowded', 'spotted', 'rabid', 'concentrate', 'please', 'thinkable', 'gusty', 'disillusioned', 'laugh', 'mindless', 'bloody', 'shame', 'zipper', 'knotty', 'frog', 'tempt', 'learned', 'spy', 'hill', 'utopian', 'modern', 'craven', 'idea', 'wrap', 'load', 'new', 'quarrelsome', 'cry', 'dazzling', 'vivacious', 'cloudy', 'ripe', 'mailbox', 'overwrought', 'bitter', 'spurious', 'crib', 'vein', 'play', 'comparison', 'distance', 'blushing', 'scorch', 'ludicrous', 'thing', 'spell', 'bomb', 'start', 'squirrel', 'overflow', 'near', 'pointless', 'order', 'listen', 'belief', 'needle', 'fog', 'heartbreaking', 'hideous', 'wide', 'ill', 'group', 'beginner', 'tangible', 'escape', 'abusive', 'scarce', 'basket', 'spade', 'tremendous', 'dinosaurs', 'whisper', 'view', 'aberrant', 'sky', 'switch', 'elbow', 'aunt', 'scarf', 'songs', 'uppity', 'existence', 'discreet', 'heavenly', 'pour', 'tire', 'matter', 'instinctive', 'time', 'demonic', 'scrape', 'placid', 'puffy', 'violent', 'wail', 'debonair', 'wrong', 'irate', 'private', 'giants', 'carry', 'right', 'daily', 'able', 'foamy', 'full', 'farm', 'dapper', 'rot', 'ethereal', 'icky', 'cause', 'girl', 'advertisement', 'mighty', 'marvelous', 'male', 'earsplitting', 'adaptable', 'familiar', 'sip', 'obeisant', 'prose', 'slimy', 'hug', 'youthful', 'suggestion', 'interest', 'cabbage', 'standing', 'territory', 'shrill', 'chubby', 'outstanding', 'bare', 'drop', 'grumpy', 'precious', 'sweater', 'nerve', 'unpack', 'drab', 'battle', 'goofy', 'naive', 'little', 'tin', 'hair', 'room', 'recondite', 'friends', 'tap', 'teaching', 'cars', 'furry', 'minister', 'sparkling', 'fair', 'clip', 'butter', 'expansion', 'eggnog', 'drain', 'front', 'reduce', 'pull', 'wistful', 'price', 'point', 'carpenter', 'snore', 'vast', 'complain', 'afraid', 'invincible', 'jam', 'kindly', 'help', 'curl', 'deserve', 'makeshift', 'office', 'mom', 'mint', 'rude', 'guess', 'decisive', 'nostalgic', 'sisters', 'rub', 'walk', 'hover', 'tasty', 'tested', 'vulgar', 'snobbish', 'haunt', 'true', 'reign', 'capricious', 'fancy', 'deafening', 'reading', 'church', 'basin', 'observe', 'stage', 'fallacious', 'introduce', 'bury', 'rebel', 'telephone', 'thought', 'yellow', 'wink', 'error', 'turkey', 'shrug', 'history', 'loose', 'shoes', 'appreciate', 'attend', 'foregoing', 'frame', 'pop', 'soggy', 'aboriginal', 'pedal', 'tiresome', 'distinct', 'baby', 'border', 'wide-eyed', 'teeny-tiny', 'quartz', 'carve', 'trashy', 'ladybug', 'baseball', 'berry', 'roll', 'slope', 'pushy', 'sniff', 'post', 'ready', 'park', 'pink', 'witty', 'acoustics', 'stale', 'guiltless', 'crook', 'keen', 'donkey', 'grab', 'swim', 'afford', 'compare', 'cycle', 'flavor', 'frail', 'memory', 'tease', 'float', 'frantic', 'ratty', 'paint', 'crack', 'store', 'whimsical', 'increase', 'rest', 'grape', 'yard', 'tiny', 'applaud', 'shade', 'jagged', 'thankful', 'acidic', 'wrist', 'silk', 'glorious', 'pancake', 'impartial', 'tedious', 'belong', 'invent', 'rush', 'like', 'push', 'brawny', 'reward', 'fantastic', 'grandiose', 'lowly', 'eight', 'fairies', 'shake', 'thrill', 'accurate', 'poor', 'lackadaisical', 'four', 'obey', 'preach', 'incredible', 'quill', 'womanly', 'soda', 'base', 'hissing', 'lucky', 'puny', 'stereotyped', 'murder', 'watery', 'heavy', 'painstaking', 'marble', 'knife', 'third', 'home', 'stick', 'curly', 'manage', 'fang', 'crazy', 'drown', 'green', 'salty', 'health', 'fetch', 'hulking', 'desire', 'blue-eyed', 'attach', 'knowledgeable', 'well-off', 'examine', 'year', 'flesh', 'judge', 'tub', 'bone', 'birth', 'broad', 'neat', 'board', 'plane', 'shaggy', 'coherent', 'letter', 'deep', 'detailed', 'enchanted', 'fold', 'improve', 'incompetent', 'spiders', 'gorgeous', 'suffer', 'remain', 'rich', 'salt', 'lamp', 'coat', 'swift', 'partner', 'honey', 'pan', 'animated', 'corn', 'normal', 'oafish', 'lewd', 'bake', 'medical', 'peck', 'hands', 'thumb', 'mask', 'hollow', 'alluring', 'curious', 'abounding', 'invite', 'puncture', 'straight', 'station', 'bed', 'save', 'behavior', 'guarantee', 'sour', 'stingy', 'delirious', 'forgetful', 'panoramic', 'crush', 'hypnotic', 'bashful', 'minute', 'dogs', 'concerned', 'unwieldy', 'tramp', 'driving', 'close', 'country', 'quicksand', 'perpetual', 'sincere', 'sofa', 'pie', 'fly', 'war', 'lumber', 'innate', 'spray', 'cemetery', 'prick', 'average', 'rifle', 'grass', 'happy', 'toy', 'supreme', 'search', 'aback', 'suggest', 'relation', 'tacky', 'animal', 'sail', 'enter', 'scold', 'scrawny', 'morning', 'smart', 'bruise', 'vagabond', 'ocean', 'horrible', 'sprout', 'juvenile', 'selfish', 'grade', 'request', 'brother', 'bleach', 'unadvised', 'eager', 'consist', 'jeans', 'charge']