{"action":"created","issue":{"url":"https://api.github.com/repos/monero-project/monero/issues/3281","repository_url":"https://api.github.com/repos/monero-project/monero","labels_url":"https://api.github.com/repos/monero-project/monero/issues/3281/labels{/name}","comments_url":"https://api.github.com/repos/monero-project/monero/issues/3281/comments","events_url":"https://api.github.com/repos/monero-project/monero/issues/3281/events","html_url":"https://github.com/monero-project/monero/pull/3281","id":297978197,"number":3281,"title":"Support for reading 16 byte / 13-word seeds","user":{"login":"paulshapiro","id":438826,"avatar_url":"https://avatars1.githubusercontent.com/u/438826?v=4","gravatar_id":"","url":"https://api.github.com/users/paulshapiro","html_url":"https://github.com/paulshapiro","followers_url":"https://api.github.com/users/paulshapiro/followers","following_url":"https://api.github.com/users/paulshapiro/following{/other_user}","gists_url":"https://api.github.com/users/paulshapiro/gists{/gist_id}","starred_url":"https://api.github.com/users/paulshapiro/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/paulshapiro/subscriptions","organizations_url":"https://api.github.com/users/paulshapiro/orgs","repos_url":"https://api.github.com/users/paulshapiro/repos","events_url":"https://api.github.com/users/paulshapiro/events{/privacy}","received_events_url":"https://api.github.com/users/paulshapiro/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":19,"created_at":"2018-02-17T04:27:23Z","updated_at":"2018-03-02T05:28:15Z","closed_at":null,"author_association":"NONE","pull_request":{"url":"https://api.github.com/repos/monero-project/monero/pulls/3281","html_url":"https://github.com/monero-project/monero/pull/3281","diff_url":"https://github.com/monero-project/monero/pull/3281.diff","patch_url":"https://github.com/monero-project/monero/pull/3281.patch"},"body":"Includes support for \r\n\r\n* account.cpp to properly generate view keys for a (32 byte) `secret_key` which was derived from legacy 16 byte key data, as well as necessary integration in wallet2 generate;\r\n\r\n* converting legacy 16 byte key data to suitable normal 32 byte `secret_key`s for usage in wallet2/account; and\r\n\r\n* mnemonic words<->bytes transforms to support 13-word seed mnemonics.\r\n\r\nThis PR does not include any specific integration with simplewallet (CLI) nor `src/wallet/api` (GUI).\r\n\r\nHere's an example of how one might decode mnemonic strings of varying lengths from user input...\r\n\r\n```\r\nbool decoded_seed(\r\n\tstd::string mnemonic_string,\r\n\tstd::string mnemonic_language,\r\n\tMnemonicDecodedSeed_RetVals &retVals\r\n) {\r\n\tretVals = {};\r\n\t//\r\n\t// sanitize inputs\r\n\tif (mnemonic_string.empty()) {\r\n\t\tretVals.did_error = true;\r\n\t\tretVals.err_string = \"Please enter a valid seed\";\r\n\t\t//\r\n\t\treturn false;\r\n\t}\r\n\tboost::algorithm::to_lower(mnemonic_string); // critical\r\n\t// TODO: strip wrapping whitespace? anything else?\r\n\t//\r\n\tstd::stringstream stream(mnemonic_string); // to count words…\r\n\tunsigned long word_count = std::distance(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>());\r\n\t//\tunsigned long word_count = boost::range::distance(boost::algorithm::make_split_iterator(mnemonic_string, boost::algorithm::is_space())); // TODO: get this workin\r\n\t//\r\n\tcrypto::secret_key sec_seed;\r\n\tstd::string sec_seed_string;\r\n\tbool from_legacy16B_lw_seed = false;\r\n\tif (word_count == crypto::ElectrumWords::stable_32B_seed_mnemonic_word_count) {\r\n\t\tfrom_legacy16B_lw_seed = false; // to be clear\r\n\t\tbool r = crypto::ElectrumWords::words_to_bytes(mnemonic_string, sec_seed, mnemonic_language);\r\n\t\tif (!r) {\r\n\t\t\tretVals.did_error = true;\r\n\t\t\tretVals.err_string = \"Invalid 25-word mnemonic\";\r\n\t\t\t//\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tsec_seed_string = string_tools::pod_to_hex(sec_seed);\r\n\t} else if (word_count == crypto::ElectrumWords::legacy_16B_seed_mnemonic_word_count) {\r\n\t\tfrom_legacy16B_lw_seed = true;\r\n\t\tcrypto::legacy16B_secret_key legacy16B_sec_seed;\r\n\t\tbool r = crypto::ElectrumWords::words_to_bytes(mnemonic_string, legacy16B_sec_seed, mnemonic_language); // special 16 byte function\r\n\t\tif (!r) {\r\n\t\t\tretVals.did_error = true;\r\n\t\t\tretVals.err_string = \"Invalid 13-word mnemonic\";\r\n\t\t\t//\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tcrypto::coerce_valid_sec_key_from(legacy16B_sec_seed, sec_seed);\r\n\t\tsec_seed_string = string_tools::pod_to_hex(legacy16B_sec_seed); // <- NOTE: we are returning the _legacy_ seed as the string… this is important so we don't lose the fact it was 16B/13-word originally!\r\n\t} else {\r\n\t\tretVals.did_error = true;\r\n\t\tretVals.err_string = \"Please enter a 25- or 13-word secret mnemonic.\";\r\n\t\t//\r\n\t\treturn false;\r\n\t}\r\n\tretVals.optl__sec_seed = sec_seed;\r\n\tretVals.optl__sec_seed_string = sec_seed_string;\r\n\tretVals.optl__mnemonic_string = mnemonic_string; \r\n\tretVals.from_legacy16B_lw_seed = from_legacy16B_lw_seed;\r\n\t//\r\n\treturn true;\r\n}\r\n```\r\n\r\nIn the following example, a secret key in hex string form is \r\n\r\n```\r\nunsigned long sec_seed_string_length = sec_seed_string.length();\r\ncrypto::secret_key sec_seed;\r\nbool from_legacy16B_lw_seed = false;\r\nif (sec_seed_string_length == crypto::sec_seed_hex_string_length) { // normal seed\r\n\tfrom_legacy16B_lw_seed = false; // to be clear\r\n\tbool r = string_tools::hex_to_pod(sec_seed_string, sec_seed);\r\n\tif (!r) {\r\n\t\toutputs.did_error = true;\r\n\t\toutputs.err_string = \"Invalid seed\";\r\n\t\t//\r\n\t\treturn false;\r\n\t}\r\n} else if (sec_seed_string_length == crypto::legacy16B__sec_seed_hex_string_length) {\r\n\tfrom_legacy16B_lw_seed = true;\r\n\tcrypto::legacy16B_secret_key legacy16B_sec_seed;\r\n\tbool r = string_tools::hex_to_pod(sec_seed_string, legacy16B_sec_seed);\r\n\tif (!r) {\r\n\t\toutputs.did_error = true;\r\n\t\toutputs.err_string = \"Invalid seed\";\r\n\t\t//\r\n\t\treturn false;\r\n\t}\r\n\tcrypto::coerce_valid_sec_key_from(legacy16B_sec_seed, sec_seed);\r\n}\r\ncryptonote::account_base expected_account{};\r\nexpected_account.generate(sec_seed, true/*recover*/, false/*two_random*/, from_legacy16B_lw_seed);\r\nconst cryptonote::account_keys& expected_account_keys = expected_account.get_keys();\r\n// .... etc\r\n```\r\n\r\nThere might be a slightly more rigorous method to detect the byte content of the seed and mnemonic than checking string length and word count. I've heard for example that luigi has done some work with 13-word seeds that encode 32 bytes of seed information rather than 16."},"comment":{"url":"https://api.github.com/repos/monero-project/monero/issues/comments/369824442","html_url":"https://github.com/monero-project/monero/pull/3281#issuecomment-369824442","issue_url":"https://api.github.com/repos/monero-project/monero/issues/3281","id":369824442,"user":{"login":"stoffu","id":26077532,"avatar_url":"https://avatars1.githubusercontent.com/u/26077532?v=4","gravatar_id":"","url":"https://api.github.com/users/stoffu","html_url":"https://github.com/stoffu","followers_url":"https://api.github.com/users/stoffu/followers","following_url":"https://api.github.com/users/stoffu/following{/other_user}","gists_url":"https://api.github.com/users/stoffu/gists{/gist_id}","starred_url":"https://api.github.com/users/stoffu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stoffu/subscriptions","organizations_url":"https://api.github.com/users/stoffu/orgs","repos_url":"https://api.github.com/users/stoffu/repos","events_url":"https://api.github.com/users/stoffu/events{/privacy}","received_events_url":"https://api.github.com/users/stoffu/received_events","type":"User","site_admin":false},"created_at":"2018-03-02T05:28:15Z","updated_at":"2018-03-02T05:28:15Z","author_association":"CONTRIBUTOR","body":"Yes."}}
{"actor":{"display_login":"stoffu"}}