diff --git a/.gitignore b/.gitignore index e43b0f9..f71399e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,32 @@ .DS_Store +build +Debug +xcode +cmake-build-debug/ +.idea +CMakeFiles +CMakeScripts +CMakeCache.txt +CTestTestfile.cmake +cmake_install.cmake + +TEST.build +DerivedData/ + + +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + + +*.moved-aside +*.xccheckout +*.xcscmblueprint + + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..713a912 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,91 @@ + +cmake_minimum_required(VERSION 3.4.1) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +project(TEST) +enable_testing() + +#Find openSSL +find_package(OpenSSL COMPONENTS + crypto REQUIRED + ssl REQUIRED +) + +#Prep ourselves for compiling boost +find_package(Boost COMPONENTS + unit_test_framework REQUIRED + atomic REQUIRED + chrono REQUIRED + context REQUIRED + date_time REQUIRED + exception REQUIRED + filesystem REQUIRED + graph REQUIRED + iostreams REQUIRED + math_c99 REQUIRED + math_c99f REQUIRED + math_c99l REQUIRED + math_tr1 REQUIRED + math_tr1f REQUIRED + math_tr1l REQUIRED + prg_exec_monitor REQUIRED + program_options REQUIRED + random REQUIRED + regex REQUIRED + serialization REQUIRED + signals REQUIRED + system REQUIRED + test_exec_monitor REQUIRED + thread REQUIRED + timer REQUIRED + wave REQUIRED + wserialization REQUIRED +) + +include_directories(${Boost_INCLUDE_DIRS}) +include_directories("src") +include_directories("test") + +# keeping test files in a separate source directory +file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test/test_*.cpp) + +set( + SRC_FILES + # + # src/monero_address_utils.hpp + # src/monero_address_utils.cpp + # src/monero_paymentID_utils.hpp + # src/monero_paymentID_utils.cpp + # src/monero_wallet_utils.hpp + # src/monero_wallet_utils.cpp + src/tools__ret_vals.hpp + src/tools__ret_vals.cpp +) + +#Run through each source +foreach(testSrc ${TEST_SRCS}) + # extract the filename without an extension (NAME_WE) + get_filename_component(testName ${testSrc} NAME_WE) + add_executable( + ${testName} + ${testSrc} + ${SRC_FILES} + ) + target_link_libraries( + ${testName} + # + OpenSSL::SSL + ${Boost_LIBRARIES} + ) + set_target_properties( + ${testName} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/build/products + ) + add_test( + NAME ${testName} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/build/products + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/products/${testName} --catch_system_error=yes + ) +endforeach(testSrc) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..fdc993b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bin/buildAndRun_tests__Mac b/bin/buildAndRun_tests__Mac new file mode 100755 index 0000000..26d34d2 --- /dev/null +++ b/bin/buildAndRun_tests__Mac @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +mkdir -p build; + +cd build && +cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl && +cmake --build . && +CTEST_OUTPUT_ON_FAILURE=1 ctest -VV diff --git a/bin/genXcodeProj b/bin/genXcodeProj new file mode 100755 index 0000000..626fdbc --- /dev/null +++ b/bin/genXcodeProj @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + + + +ESCAPEDSRCROOT="$(pwd | sed 's/\//\\\//g')"; +mkdir -p xcode; +cd xcode && +cmake -G Xcode .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl && +echo "$ 's/$ESCAPEDSRCROOT/\${SRCROOT}/g'" && #remove personally identifying info of proj generator +LC_CTYPE=C LANG=C find TEST.xcodeproj -type f -print0 -exec sed -i "" 's/'"$ESCAPEDSRCROOT"'/${SRCROOT}/g' {} + && + +echo "" #newline + diff --git a/src/monero_address_utils.cpp b/src/monero_address_utils.cpp new file mode 100644 index 0000000..61e966d --- /dev/null +++ b/src/monero_address_utils.cpp @@ -0,0 +1,127 @@ +// +// monero_address_utils.cpp +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +#import "index.hpp" +// +#include "cryptonote_basic_impl.h" +#include "string_tools.h" +#include "tools__ret_vals.hpp" +using namespace epee; +// +#include "monero_paymentID_utils.hpp" +// +// Accessors - Implementations +Monero_DecodedAddress_RetVals decodedAddress(string addressString, bool isTestnet) +{ + Monero_DecodedAddress_RetVals *retVals = [Monero_DecodedAddress_RetVals new]; + // + cryptonote::address_parse_info info; + bool didSucceed = cryptonote::get_account_address_from_str(info, isTestnet ? cryptonote::TESTNET : cryptonote::MAINNET, std::string(addressString.UTF8String)); + if (didSucceed == false) { + retVals.errStr_orNil = NSLocalizedString(@"Invalid address", nil); + // + return retVals; + } + cryptonote::account_public_address address = info.address; + std::string pub_viewKey_hexString = string_tools::pod_to_hex(address.m_view_public_key); + std::string pub_spendKey_hexString = string_tools::pod_to_hex(address.m_spend_public_key); + // + NSString *pub_viewKey_NSString = [NSString stringWithUTF8String:pub_viewKey_hexString.c_str()]; + NSString *pub_spendKey_NSString = [NSString stringWithUTF8String:pub_spendKey_hexString.c_str()]; + NSString *paymentID_NSString_orNil = nil; + if (info.has_payment_id == true) { + crypto::hash8 payment_id = info.payment_id; + std::string payment_id_hexString = string_tools::pod_to_hex(payment_id); + paymentID_NSString_orNil = [NSString stringWithUTF8String:payment_id_hexString.c_str()]; + } + { + retVals.pub_viewKey_NSString = pub_viewKey_NSString; + retVals.pub_spendKey_NSString = pub_spendKey_NSString; + retVals.paymentID_NSString_orNil = paymentID_NSString_orNil; + retVals.isSubaddress = info.is_subaddress; + } + return retVals; +} ++ (BOOL)isSubAddress:(NSString *)addressString isTestnet:(BOOL)isTestnet +{ + Monero_DecodedAddress_RetVals *retVals = [self decodedAddress:addressString isTestnet:isTestnet]; + // + return retVals.isSubaddress; +} ++ (BOOL)isIntegratedAddress:(NSString *)addressString isTestnet:(BOOL)isTestnet +{ + Monero_DecodedAddress_RetVals *retVals = [self decodedAddress:addressString isTestnet:isTestnet]; + // + return retVals.paymentID_NSString_orNil != nil; +} + ++ (NSString *)new_integratedAddrFromStdAddr:(NSString *)std_address_NSString andShortPID:(NSString *)short_paymentID isTestnet:(BOOL)isTestnet +{ + std::string payment_id__string = std::string(short_paymentID.UTF8String); + crypto::hash8 payment_id_short; + bool didParse = monero_paymentID_utils::parse_short_payment_id(payment_id__string, payment_id_short); + if (!didParse) { + return nil; + } + cryptonote::address_parse_info info; + bool didSucceed = cryptonote::get_account_address_from_str(info, isTestnet ? cryptonote::TESTNET : cryptonote::MAINNET, std::string(std_address_NSString.UTF8String)); + if (didSucceed == false) { + return nil; + } + if (info.is_subaddress) { + NSString *msg = [NSString stringWithFormat:@"%@ must not be called with a subaddress", NSStringFromSelector(_cmd)]; + NSAssert(false, msg); + [NSException raise:@"Illegal address value" format:@"%@", msg]; + // + return nil; + } + if (info.has_payment_id != false) { + // could even throw / fatalError here + return nil; // that was not a std_address! + } + std::string int_address_string = cryptonote::get_account_integrated_address_as_str( + isTestnet ? cryptonote::TESTNET : cryptonote::MAINNET, + info.address, + payment_id_short + ); + NSString *int_address_NSString = [NSString stringWithUTF8String:int_address_string.c_str()]; + // + return int_address_NSString; +} ++ (NSString *)new_integratedAddrFromStdAddr:(NSString *)std_address_NSString andShortPID:(NSString *)short_paymentID // mainnet +{ + return [self + new_integratedAddrFromStdAddr:std_address_NSString + andShortPID:short_paymentID + isTestnet:NO]; +} + +@end diff --git a/src/monero_address_utils.hpp b/src/monero_address_utils.hpp new file mode 100644 index 0000000..802b4a3 --- /dev/null +++ b/src/monero_address_utils.hpp @@ -0,0 +1,50 @@ +// +// monero_address_utils.hpp +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +#include "tools__ret_vals.hpp" +// +struct Monero_DecodedAddress_RetVals: tools::RetVals_base +{ // TODO: inherit from tools__ret_vals + optional errStr; + // + string pub_viewKey_string; + string pub_spendKey_string; + bool isSubaddress; + optional paymentID_string; +}; +// +// +Monero_DecodedAddress_RetVals decodedAddress(string addressString, bool isTestnet); ++ (BOOL)isSubAddress:(NSString *)addressString isTestnet:(BOOL)isTestnet; ++ (BOOL)isIntegratedAddress:(NSString *)addressString isTestnet:(BOOL)isTestnet; +// ++ (NSString *)new_integratedAddrFromStdAddr:(NSString *)std_address_NSString andShortPID:(NSString *)short_paymentID isTestnet:(BOOL)isTestnet; ++ (NSString *)new_integratedAddrFromStdAddr:(NSString *)std_address_NSString andShortPID:(NSString *)short_paymentID; // mainnet diff --git a/src/monero_paymentID_utils.cpp b/src/monero_paymentID_utils.cpp new file mode 100644 index 0000000..3dac12d --- /dev/null +++ b/src/monero_paymentID_utils.cpp @@ -0,0 +1,84 @@ +// +// monero_paymentID_utils.cpp +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// + +#include "monero_paymentID_utils.hpp" +#include "cryptonote_basic.h" +#include "cryptonote_basic/blobdatatype.h" +// +#include "string_tools.h" +using namespace epee; +// +// +crypto::hash8 monero_paymentID_utils::new_short_plain_paymentID() +{ + return crypto::rand(); +} +// +bool monero_paymentID_utils::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) +{ + cryptonote::blobdata payment_id_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) { + return false; + } + if (sizeof(crypto::hash) != payment_id_data.size()) { + return false; + } + payment_id = *reinterpret_cast(payment_id_data.data()); + // + return true; +} +bool monero_paymentID_utils::parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id) +{ + cryptonote::blobdata payment_id_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) { + return false; + } + if (sizeof(crypto::hash8) != payment_id_data.size()) { + return false; + } + payment_id = *reinterpret_cast(payment_id_data.data()); + // + return true; +} +bool monero_paymentID_utils::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) +{ + if (parse_long_payment_id(payment_id_str, payment_id)) { + return true; + } + crypto::hash8 payment_id8; + if (parse_short_payment_id(payment_id_str, payment_id8)) { + memcpy(payment_id.data, payment_id8.data, 8); + memset(payment_id.data + 8, 0, 24); + return true; + } + return false; +} diff --git a/src/monero_paymentID_utils.hpp b/src/monero_paymentID_utils.hpp new file mode 100644 index 0000000..b1136a2 --- /dev/null +++ b/src/monero_paymentID_utils.hpp @@ -0,0 +1,51 @@ +// +// monero_paymentID_utils.hpp +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// + +#ifndef monero_paymentID_utils_hpp +#define monero_paymentID_utils_hpp + +#include +#include "crypto.h" + +namespace monero_paymentID_utils +{ + // + // Generating Payment IDs + crypto::hash8 new_short_plain_paymentID(); // This is favored - its length will be detected and encrypted automatically on send + // + // Parsing and Detecting Payment IDs + bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); + bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); + bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); +} + +#endif /* monero_paymentID_utils_hpp */ diff --git a/src/monero_wallet_utils.cpp b/src/monero_wallet_utils.cpp new file mode 100644 index 0000000..7f42d27 --- /dev/null +++ b/src/monero_wallet_utils.cpp @@ -0,0 +1,328 @@ +// +// monero_wallet_utils.cpp +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// +#include "monero_wallet_utils.hpp" +#include +#include "cryptonote_basic/account.h" +// +#include "string_tools.h" +using namespace epee; +// +extern "C" { + #include "crypto-ops.h" +} +// +using namespace monero_wallet_utils; +using namespace crypto; // for extension +// +bool monero_wallet_utils::new_wallet( + std::string mnemonic_language, + WalletDescriptionRetVals &retVals, + bool isTestnet +) +{ + retVals = {}; + // + cryptonote::account_base account{}; // this initializes the wallet and should call the default constructor + crypto::secret_key nonLegacy32B_sec_seed = account.generate(); + // + const cryptonote::account_keys& keys = account.get_keys(); + std::string address_string = account.get_public_address_str(isTestnet); // getting the string here instead of leaving it to the consumer b/c get_public_address_str could potentially change in implementation (see TODO) so it's not right to duplicate that here + // + std::string mnemonic_string; + bool r = crypto::ElectrumWords::bytes_to_words(nonLegacy32B_sec_seed, mnemonic_string, std::move(mnemonic_language)); + // ^-- it's OK to directly call ElectrumWords w/ crypto::secret_key as we are generating new wallet, not reading + if (!r) { + retVals.did_error = true; + retVals.err_string = "Unable to create new wallet"; + // TODO: return code of unable to convert seed to mnemonic + // + return false; + } + retVals.optl__desc = + { + string_tools::pod_to_hex(nonLegacy32B_sec_seed), + // + address_string, + // + keys.m_spend_secret_key, + keys.m_view_secret_key, + keys.m_account_address.m_spend_public_key, + keys.m_account_address.m_view_public_key, + // + mnemonic_string + }; + return true; +} +// +bool monero_wallet_utils::decoded_seed( + std::string mnemonic_string, + std::string mnemonic_language, + MnemonicDecodedSeed_RetVals &retVals +) { + retVals = {}; + // + // sanitize inputs + if (mnemonic_string.empty()) { + retVals.did_error = true; + retVals.err_string = "Please enter a valid seed"; + // + return false; + } + boost::algorithm::to_lower(mnemonic_string); // critical + // TODO: strip wrapping whitespace? anything else? + // + std::stringstream stream(mnemonic_string); // to count words… + unsigned long word_count = std::distance(std::istream_iterator(stream), std::istream_iterator()); + // unsigned long word_count = boost::range::distance(boost::algorithm::make_split_iterator(mnemonic_string, boost::algorithm::is_space())); // TODO: get this workin + // + crypto::secret_key sec_seed; + std::string sec_seed_string; // TODO/FIXME: needed this for shared ref outside of if branch below… not intending extra default constructor call but not sure how to get around it yet + bool from_legacy16B_lw_seed = false; + if (word_count == crypto::ElectrumWords::stable_32B_seed_mnemonic_word_count) { + from_legacy16B_lw_seed = false; // to be clear + bool r = crypto::ElectrumWords::words_to_bytes(mnemonic_string, sec_seed, mnemonic_language); + if (!r) { + retVals.did_error = true; + retVals.err_string = "Invalid 25-word mnemonic"; + // + return false; + } + sec_seed_string = string_tools::pod_to_hex(sec_seed); + } else if (word_count == crypto::ElectrumWords::legacy_16B_seed_mnemonic_word_count) { + from_legacy16B_lw_seed = true; + crypto::legacy16B_secret_key legacy16B_sec_seed; + bool r = crypto::ElectrumWords::words_to_bytes(mnemonic_string, legacy16B_sec_seed, mnemonic_language); // special 16 byte function + if (!r) { + retVals.did_error = true; + retVals.err_string = "Invalid 13-word mnemonic"; + // + return false; + } + crypto::coerce_valid_sec_key_from(legacy16B_sec_seed, sec_seed); + sec_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! + } else { + retVals.did_error = true; + retVals.err_string = "Please enter a 25- or 13-word secret mnemonic."; + // + return false; + } + retVals.optl__sec_seed = sec_seed; + retVals.optl__sec_seed_string = sec_seed_string; + retVals.optl__mnemonic_string = mnemonic_string; + retVals.from_legacy16B_lw_seed = from_legacy16B_lw_seed; + // + return true; +} +// +bool monero_wallet_utils::wallet_with( + std::string mnemonic_string, + std::string mnemonic_language, + WalletDescriptionRetVals &retVals, + bool isTestnet +) { + retVals = {}; + // + MnemonicDecodedSeed_RetVals decodedSeed_retVals; + bool r = decoded_seed(std::move(mnemonic_string), std::move(mnemonic_language), decodedSeed_retVals); + if (!r) { + retVals.did_error = true; + retVals.err_string = *decodedSeed_retVals.err_string; // TODO: assert? + return false; + } + cryptonote::account_base account{}; // this initializes the wallet and should call the default constructor + account.generate( + *decodedSeed_retVals.optl__sec_seed, // is this an extra copy? maybe have consumer pass ref as arg instead + true/*recover*/, + false/*two_random*/, + decodedSeed_retVals.from_legacy16B_lw_seed // assumed set if r + ); + const cryptonote::account_keys& keys = account.get_keys(); + retVals.optl__desc = + { + *decodedSeed_retVals.optl__sec_seed_string, // assumed non nil if r + // + account.get_public_address_str(isTestnet), + // + keys.m_spend_secret_key, + keys.m_view_secret_key, + keys.m_account_address.m_spend_public_key, + keys.m_account_address.m_view_public_key, + // + *decodedSeed_retVals.optl__mnemonic_string, // assumed non nil if r; copied for return + }; + return true; +} + +bool monero_wallet_utils::validate_wallet_components_with( + const WalletComponentsToValidate &inputs, + WalletComponentsValidationResults &outputs +) +{ // TODO: how can the err_strings be prepared for localization? + outputs = {}; + bool r = false; + // + // Address + cryptonote::address_parse_info decoded_address_info; + r = cryptonote::get_account_address_from_str( + decoded_address_info, + inputs.isTestnet, + inputs.address_string + ); + if (r == false) { + outputs.did_error = true; + outputs.err_string = "Invalid address"; + // + return false; + } + // + // View key: + crypto::secret_key sec_viewKey; + r = string_tools::hex_to_pod(inputs.sec_viewKey_string, sec_viewKey); + if (r == false) { + outputs.did_error = true; + outputs.err_string = "Invalid view key"; + // + return false; + } + // Validate pub key derived from sec view key matches decoded_address-cached pub key + crypto::public_key expected_pub_viewKey; + r = crypto::secret_key_to_public_key(sec_viewKey, expected_pub_viewKey); + if (r == false) { + outputs.did_error = true; + outputs.err_string = "Invalid view key"; + // + return false; + } + if (decoded_address_info.address.m_view_public_key != expected_pub_viewKey) { + outputs.did_error = true; + outputs.err_string = "View key does not match address"; + // + return false; + } + // + // View-only vs spend-key/seed + outputs.isInViewOnlyMode = true; // setting the ground state + // + crypto::secret_key sec_spendKey; // may be initialized + if (inputs.optl__sec_spendKey_string) { + // First check if spend key content actually exists before passing to valid_sec_key_from - so that a spend key decode error can be treated as a failure instead of detecting empty spend keys too + if ((*inputs.optl__sec_spendKey_string).empty() == false) { + r = string_tools::hex_to_pod(*inputs.optl__sec_spendKey_string, sec_spendKey); + if (r == false) { // this is an actual parse error exit condition + outputs.did_error = true; + outputs.err_string = "Invalid spend key"; + // + return false; + } + // Validate pub key derived from sec spend key matches decoded_address_info-cached pub key + crypto::public_key expected_pub_spendKey; + r = crypto::secret_key_to_public_key(sec_spendKey, expected_pub_spendKey); + if (r == false) { + outputs.did_error = true; + outputs.err_string = "Invalid spend key"; + // + return false; + } + if (decoded_address_info.address.m_spend_public_key != expected_pub_spendKey) { + outputs.did_error = true; + outputs.err_string = "Spend key does not match address"; + // + return false; + } + outputs.isInViewOnlyMode = false; + } + } + if (inputs.optl__sec_seed_string) { + if ((*inputs.optl__sec_seed_string).empty() == false) { + unsigned long sec_seed_string_length = (*inputs.optl__sec_seed_string).length(); + crypto::secret_key sec_seed; + bool from_legacy16B_lw_seed = false; + if (sec_seed_string_length == crypto::sec_seed_hex_string_length) { // normal seed + from_legacy16B_lw_seed = false; // to be clear + bool r = string_tools::hex_to_pod((*inputs.optl__sec_seed_string), sec_seed); + if (!r) { + outputs.did_error = true; + outputs.err_string = "Invalid seed"; + // + return false; + } + } else if (sec_seed_string_length == crypto::legacy16B__sec_seed_hex_string_length) { + from_legacy16B_lw_seed = true; + crypto::legacy16B_secret_key legacy16B_sec_seed; + bool r = string_tools::hex_to_pod((*inputs.optl__sec_seed_string), legacy16B_sec_seed); + if (!r) { + outputs.did_error = true; + outputs.err_string = "Invalid seed"; + // + return false; + } + crypto::coerce_valid_sec_key_from(legacy16B_sec_seed, sec_seed); + } + cryptonote::account_base expected_account{}; // this initializes the wallet and should call the default constructor + expected_account.generate(sec_seed, true/*recover*/, false/*two_random*/, from_legacy16B_lw_seed); + const cryptonote::account_keys& expected_account_keys = expected_account.get_keys(); + // TODO: assert sec_spendKey initialized? + if (expected_account_keys.m_view_secret_key != sec_viewKey) { + outputs.did_error = true; + outputs.err_string = "Private view key does not match generated key"; + // + return false; + } + if (expected_account_keys.m_spend_secret_key != sec_spendKey) { + outputs.did_error = true; + outputs.err_string = "Private spend key does not match generated key"; + // + return false; + } + if (expected_account_keys.m_account_address.m_view_public_key != decoded_address_info.address.m_view_public_key) { + outputs.did_error = true; + outputs.err_string = "Public view key does not match generated key"; + // + return false; + } + if (expected_account_keys.m_account_address.m_spend_public_key != decoded_address_info.address.m_spend_public_key) { + outputs.did_error = true; + outputs.err_string = "Public spend key does not match generated key"; + // + return false; + } + // + outputs.isInViewOnlyMode = false; // TODO: should this ensure that sec_spendKey is not nil? spendKey should always be available if the seed is… + } + } + outputs.pub_viewKey_string = string_tools::pod_to_hex(decoded_address_info.address.m_view_public_key); + outputs.pub_spendKey_string = string_tools::pod_to_hex(decoded_address_info.address.m_spend_public_key); + outputs.isValid = true; + // + return true; +} diff --git a/src/monero_wallet_utils.hpp b/src/monero_wallet_utils.hpp new file mode 100644 index 0000000..fb57e42 --- /dev/null +++ b/src/monero_wallet_utils.hpp @@ -0,0 +1,116 @@ +// +// monero_wallet_utils.hpp +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// + +#ifndef monero_wallet_utils_hpp +#define monero_wallet_utils_hpp + +#include +#include "crypto.h" +#include "cryptonote_basic.h" +#include "cryptonote_basic_impl.h" +#include "electrum-words.h" +#include "mnemonics/singleton.h" +#include "mnemonics/english.h" +// +using namespace tools; +#include "tools__ret_vals.hpp" +// +namespace monero_wallet_utils +{ + // + // Accounts + struct MnemonicDecodedSeed_RetVals: RetVals_base + { + boost::optional optl__sec_seed = boost::none; + boost::optional optl__sec_seed_string = boost::none; + boost::optional optl__mnemonic_string = boost::none; + bool from_legacy16B_lw_seed = false; + }; + bool decoded_seed( + std::string mnemonic_string, + std::string mnemonic_language_string, + // + MnemonicDecodedSeed_RetVals &retVals + ); + // + // Convenience functions - Wallets + struct WalletDescription + { + std::string sec_seed_string; // as string bc it might by legacy 16B style aside from crypto::secret_key + // + std::string address_string; + // + crypto::secret_key sec_spendKey; + crypto::secret_key sec_viewKey; + crypto::public_key pub_spendKey; + crypto::public_key pub_viewKey; + // + std::string mnemonic_string; // mnemonic_language is not returned because it must be provided to all functions which can return a WalletDescription + }; + struct WalletDescriptionRetVals: RetVals_base + { + boost::optional optl__desc = boost::none; + }; + bool new_wallet( + std::string mnemonic_language, + WalletDescriptionRetVals &retVals, + bool isTestnet = false + ); + bool wallet_with( + std::string mnemonic_string, + std::string mnemonic_language, + WalletDescriptionRetVals &retVals, + bool isTestnet = false + ); + // + struct WalletComponentsToValidate + { + std::string address_string; // Required + std::string sec_viewKey_string; // Required + const std::string *optl__sec_spendKey_string; + const std::string *optl__sec_seed_string; + bool isTestnet; + }; + struct WalletComponentsValidationResults: RetVals_base + { + bool isValid; // this will naturally remain false if did_error=true + std::string pub_spendKey_string; + std::string pub_viewKey_string; + bool isInViewOnlyMode; // !sec_seed && !sec_spendKey + }; + bool validate_wallet_components_with( // returns !did_error + const WalletComponentsToValidate &inputs, + WalletComponentsValidationResults &outputs + ); +} + +#endif /* monero_wallet_utils_hpp */ diff --git a/src/tools__ret_vals.cpp b/src/tools__ret_vals.cpp new file mode 100644 index 0000000..867dde9 --- /dev/null +++ b/src/tools__ret_vals.cpp @@ -0,0 +1,9 @@ +// +// tools__ret_vals.cpp +// Copyright © 2018 MyMonero. All rights reserved. +// +// +#include "tools__ret_vals.hpp" +// + + diff --git a/src/tools__ret_vals.hpp b/src/tools__ret_vals.hpp new file mode 100644 index 0000000..e478723 --- /dev/null +++ b/src/tools__ret_vals.hpp @@ -0,0 +1,48 @@ +// +// tools__ret_vals.hpp +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#ifndef tools__ret_vals_hpp +#define tools__ret_vals_hpp +// +#include +#include +// +namespace tools +{ + struct RetVals_base + { + bool did_error = false; + boost::optional err_string = boost::none; + // + // derive *_RetVals structs from this type and add your own members + }; +} + +#endif /* tools__ret_vals_hpp */ diff --git a/test/test_all.cpp b/test/test_all.cpp new file mode 100644 index 0000000..c3316f2 --- /dev/null +++ b/test/test_all.cpp @@ -0,0 +1,54 @@ +// +// test_all.cpp +// MyMonero +// +// Copyright (c) 2014-2018, MyMonero.com +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// Test module setup +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE LibMoneroTests +#include // last +// +// Includes & namespaces +#include +#include +#include +using namespace std; +// using namespace boost; +// +// Shared code +// +// Test suites +// #include "../src/monero_address_utils.hpp" +BOOST_AUTO_TEST_CASE(mockedPlainStringDoc_insert) +{ + BOOST_REQUIRE(true); + std::cout << "Testing...." << std::endl; +}