text:
// Copyright (c) 2012-2018 The Elastos Open Source Project
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#define CATCH_CONFIG_MAIN

#include <catch.hpp>
#include <nlohmann/json.hpp>
#include <SDK/Account/Account.h>
#include <SDK/Common/Log.h>
#include <SDK/WalletCore/KeyStore/KeyStore.h>
#include <SDK/WalletCore/BIPs/BIP39.h>
#include <SDK/WalletCore/BIPs/Base58.h>
#include <SDK/WalletCore/KeyStore/CoinInfo.h>
#include <SDK/Plugin/Transaction/Asset.h>

#include <fstream>
#include <boost/filesystem.hpp>

using namespace Elastos::ElaWallet;

TEST_CASE("KeyStore test", "[KeyStore]") {
	Log::registerMultiLogger();

	SECTION("keystore from web wallet") {
		SECTION("with private key and without passphrase") {
			std::string passwd = "s12345678";
			const std::string mnemonic = "闲 齿 兰 丹 请 毛 训 胁 浇 摄 县 诉";
			const std::string passphrase = "";
			nlohmann::json keystoreJson = nlohmann::json::parse("{\"iv\":\"neksof2hUCccAvdcMeYOqg==\",\"v\":1,\"iter\":10000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"Sv8OvJi34Rs=\",\"ct\":\"qGWsLoHyWuZ/2ARas9G+qXfuIOj67oULVlIJOyK/n1HrhlVVUYukeh9Q3TWMdsKFFQdmLIrhd0dI9dic/VU/YulJrCQK7kvTeYTp9/Tp3INbU6XPygrt48addEYTQNmkcplHasuSGupYnXFGgfhda6F5ABRy41xHf4vbqDh9+7b+k/QyIyTqglVpIKJEiJ+J5/8lkVuIILm2OWNYdh/dMO+/s4gapLvGncBN8uZrpY2GlGDIVMhcH0B+hKsorDSSBssMn3E7JOXTlym2T+wC0Qy1FiUTmsCk7TQu4NpUa3OsPx1Lcp4Ow7AjkI7xmbR48mEBZbLD1GtkRwQiS5j4cp25iGHeeEM3jsdTnOSVa4sZpsnzq2jxEgHVeLmPwZdWwWfKrQo47gH2c95F1hPI7J4eGGLeN9l2YF0JBS9Zl6zQO/jge+PUZolibpKAU9OoGzZF+nCjoLZ9IJJrPIXxVycDquHd07WSGUoQgK+e+BybH5zlbIb3dtMjJFFXI6oXj1+G0ZgN5MKXjnyhcMHf1ojefK3A4BejoQ3dPav34PVJyWPUW3XdZLtfb/7KUk2jgx06kZgcIMFG2QIvK6uexH3KvIcPteVSSDVQ4oSj+HAKDX5qvfku7Yqb7FE04NjB4bRJIhvTngXbW6kuz2iowkc+V1Q2ssfV/js7/IIde1CGfy/fk0qbUNt5QYDgtmX4YmphcCdby67Uaba+YklqinHOJWfS+/pAJ2+FJ/WSS6bUvZljEATsbZdY25xexx1PZ0h5mhIC8tZHOQntcU37AH+DFj/fePqm+SNnXdiKC4ZX2Al8l6hU2yOYCswtWMd1uF2ZVZbZ1xoRkx9A/NOyQt3+/i/qrsa2or4hrg3Dob/sef8zPNlUoMBgvgQ/W0xczugmI0E+Ojh9k4ibzTQMzwicvgYYD2oLnFAlYIUUcRpgE1MQ5ETxeRJsg5tyEz7tEenaOBVbM/xkUDvcbzwKpa+7irBF+fKtyHZseJzTJxfNAfKdyy8smuFqwILrEBWJ4Ci0FYKG2CjlHPqsNK5U0v2YnyYs1nmuxnRhl7v/5QJcgtFZIDbGDuzILCde5NwAv2rsyb7FxFW/1Mic+YbZg3lMmSpa6yOQ89/5v+K3XyFzoad8LFxrjzxsChj1EFci6wc8MNoUlTbMYeO+jhbIRqYje6U4kUKMuglq1CAZEb0KQAK+FHF37Ap8Gd3RR2VE5bQ8K6G4+Tx71c+RLZYqPkDn8STCiGcrXLkrNZfSo1iLYqA0vIfpyZHmkTAjxdWMgBBBAKu503qwSamZYwN23SsAvg/qxj++NMsHKRek98j7jwrZWmNZDfe6W+RAEcrG2LWKcHVTyKWLKm5rE+bi1cKB5W9ZTPCLGkN/jw8tDa25BYd2rMA5mqeGShBqSbG/acw05rQFAgVKvfCZRyMjieGMoZ9Pk68xxABn5ZNFjaogFOylt3VOtL/Dz92wmin+6Q/1oRX8qCkavdVmJu230L2Z7JpVAeoRc2usm+bGRdbvdVC9j3NFW6Mu8+8AcN5ZPGm8yzG7yP2MaGPSGO/PiXc/CUtZNUGQW27BDaClj4EtKIRqp76lDam4e88pLgp67O5naUdcdB4ISmlkleDH4OMdtqSoSj/YDHZvg7rta9SSEDloNB0Y7X+JCaI7czLPZp4CfKHjzfmqflrxZ7YYZLhgkKHdU8xHhS4xg5punUGQHxYlS2xGAAdBSBJ8WaeTtdNXVXYa2b02zJNMSRaWyvnHh5IZezg2P5f4g8MTvInbDmO66vMOe12A/hpf/Zyolw==\"}");

			KeyStore ks;
			REQUIRE_NOTHROW(ks.Import(keystoreJson, passwd));

			const ElaNewWalletJson &walletData = ks.WalletJson();
			REQUIRE(!walletData.HasPassPhrase());
			REQUIRE(walletData.PassPhrase().empty());
			REQUIRE(walletData.Mnemonic() == mnemonic);
			REQUIRE(walletData.GetM() == 1);
			REQUIRE(walletData.GetN() == 1);

			uint512 seed = BIP39::DeriveSeed(mnemonic, passphrase);
			HDSeed hdseed(seed.bytes());
			HDKeychain rootprv(hdseed.getExtendedKey(true));

			HDKeychain requestKey = rootprv.getChild("1'/0");
			REQUIRE(walletData.RequestPubKey() == requestKey.pubkey().getHex());
			REQUIRE(walletData.RequestPrivKey() == requestKey.privkey().getHex());

			HDKeychain firstKey = rootprv.getChild("44'/0'/0'/0/0");
			Address addr(PrefixStandard, firstKey.pubkey());

			bytes_t xpubkey;
			Base58::CheckDecode(walletData.xPubKey(), xpubkey);
			HDKeychain mpk(xpubkey);
			Address addr1(PrefixStandard, mpk.getChild("0/0").pubkey());
			REQUIRE(addr1.String() == addr.String());

			std::string ownerPubKey = rootprv.getChild("44'/0'/1'/0/0").pubkey().getHex();
			REQUIRE(walletData.OwnerPubKey() == ownerPubKey);
			REQUIRE(walletData.GetCoinInfoList().size() == 0);
			REQUIRE(!walletData.SingleAddress());
		}

		SECTION("with private key and with passphrase") {
			const std::string passwd = "s12345678";
			const std::string mnemonic = "锭 援 迁 姻 角 孤 钙 肠 遵 腾 盗 困";
			const std::string passphrase = "heroopan";
			nlohmann::json keystoreJson = nlohmann::json::parse("{\"iv\":\"kgn/MQV6TYL0HPMqm51FqA==\",\"v\":1,\"iter\":10000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"4+aks85XBtI=\",\"ct\":\"iDuHXNSZMOjeTm6nZDYKPYrTObuILSmRSL9niqwHjfhx7fFXALVm2feOG04qj2HbfFMYTdrfPRmqmMUYbbx9O8Ti+5XRgI8uRTAbrbz0mvpalDcZrzVy6QWsVGGACpt3nllQhYlZ5vTyaKM0hhrYk/+xAEDc7V7SVStnVu3DJxF07VWYh4sQxv8g7y3UM2Jw1jlnLZCp2Y1guUUsih6rRnzGCNbM7bvtxDVyEgBrVDL/8sWGhIy/P0va3d7N+4LDTO2smQ5Rx+AumRTwa1oAxcy4H+kACP5TzHtJqAc2hB2w+fQ70Jf+XKET5CGaNTWCzi+hjosgEl6blD1bWf0oFMu1sl/27OGY2kgssvkxELuQHQcQU5aiy3jFzkaVLh/sM4I3rTeqxamQNTU/440Pv6SmRxOg6O6udlfanft8QgRfSFkzBi1bTgE2XOFh00qgumPks3Lz5lSLXSGpPxiN647XiWgUxCG60BSTd/c2KAx/0VdF3Q99v9tR3Z7lT2nLhoOj9VdXveVTspNFCxWbe4HoERvT3x4Fv0P+ie9QiqeJn3CEGgi6Vv34ZL0yOyisS5v8M73tsmY2QWBCw+eZ/M1RFEVitF+wnB/6c7LaEfNJjJXgi485qp9epF+rKpV6OyWacVbSImHRGY+TtLcn7O/RP1Uy4sVevQ2q5xin7NkAs8HjtpBP7kfbcKEZNwYvRTgeaqIzuA2o13mUlYceLN9NZdKuHTKYrwZ1nCROMBhifleGVhc55VRLcUgbhCNUDUPsjJRlFW2F4flY/GSGuOrLRGJvdt/ga4S68qiF8Ep82aC9x8ZXVkAVfoXmrX3VRTw7aVT8f8qD6nMONpsXxSgOGyx6U6wFrZcJdO+Zzw3e4J9axEx8+h+qb7cfsdjE+y6fKwA1Ke8SMmwiHXYDbDsX+vvZvFU4S97AweqlP4BQvw1hhAbYTbi75mEGMgDTE6oaQVRbbyy90jOBlNvHRVcR/BbBjlXs6VUiWlamy6FuQcONFGFxZ2cBVd1jJ+V9CiQcmqRRM3NC8H/icFzHhKOP8yy+FKeFbwSLq1p3sUuTEaQnynYNkCeR768zZMGxtc2ffqKtETsjjZuyNseKtUG7xRmv+X12x0E5NktYnPeBXMf/SHIMAed3RtjKxQuEjMcXmgfAFH70E1W1U9Q2T0sG4Qi6eEKFkJFZsh69KxMy8UpWr+yP+aqYCU+46oPz3OysNtCM27cQC+RylsuqhUkpkoVS1HZwPvIl6v4pnf7maCH1VfcrwCHa3KRymPLFaztUIF4OM0Z6WpSLBg2n1x/2YLpp9aBYrCubwvhhAREu10ZU6rRrdBHpQj8760//Z0UCVy+/uuUAClxG2oDC682bhSKv3KhfJMNGmcI5YnRTUdRJ6k3kGLvSNYQTnuhn1s8TSfWjqSneMJtxaQCOs9mNTtZH3JMwVdceVeCrxWL6WRhRaK6GrNsKKw3mgPmE9s7FWKOOgMY6SJW2GnkfY7ucdlxaGDOCubWM2hx/+y3EBHF/oYdfflAAAZ3ery35oQmRwXrLrvye7JwCQCFQph7mdouoZ3lxvhbkF87dh2Xf1jt2Ros9shAAR+/RoSJMJRLz3jggorfbWmiHjVv2OAxGRIRDCGo4dRdE0se0AFX9Aw2v9ga3wQR/jCMIQGUh3HUKgREosPaPWkDBXsKgQUf+uRyUgasgWVUllYnIpiPBjV9qj2h2lsztImupNAKm/ha3p9rzDNtKUZjdhyrTUtNpcbN/+VZfhBgrr5oYzl5m5JFB7JAzrgaVnA==\"}");

			KeyStore ks;
			REQUIRE_NOTHROW(ks.Import(keystoreJson, passwd));
			const ElaNewWalletJson &walletData = ks.WalletJson();
			REQUIRE(walletData.HasPassPhrase());
			REQUIRE(walletData.PassPhrase().empty());
			REQUIRE(walletData.Mnemonic() == mnemonic);
			REQUIRE(walletData.GetM() == 1);
			REQUIRE(walletData.GetN() == 1);

			uint512 seed = BIP39::DeriveSeed(mnemonic, passphrase);
			HDSeed hdseed(seed.bytes());
			HDKeychain rootprv(hdseed.getExtendedKey(true));

			HDKeychain requestKey = rootprv.getChild("1'/0");
			REQUIRE(walletData.RequestPubKey() == requestKey.pubkey().getHex());
			REQUIRE(walletData.RequestPrivKey() == requestKey.privkey().getHex());

			HDKeychain firstKey = rootprv.getChild("44'/0'/0'/0/0");
			Address addr(PrefixStandard, firstKey.pubkey());

			bytes_t xpubkey;
			Base58::CheckDecode(walletData.xPubKey(), xpubkey);
			HDKeychain mpk(xpubkey);
			Address addr1(PrefixStandard, mpk.getChild("0/0").pubkey());
			REQUIRE(addr1.String() == addr.String());

			std::string ownerPubKey = rootprv.getChild("44'/0'/1'/0/0").pubkey().getHex();
			REQUIRE(walletData.OwnerPubKey() == ownerPubKey);

			REQUIRE(walletData.GetCoinInfoList().size() == 0);
			REQUIRE(!walletData.SingleAddress());
		}

		SECTION("without private key") {
			const std::string passwd = "s12345678";
			nlohmann::json keystoreJson = nlohmann::json::parse("{\"iv\":\"1n/Rqe07AqCzE4l9I2bQ8g==\",\"v\":1,\"iter\":10000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"4+aks85XBtI=\",\"ct\":\"LtkQ9J95DJagndh0OaGujBsoS8NQ+oNEVy9YMfjo+Bpzi4Uy/iQhAkr3AyNmXA+3SpqMKRxAYusStehLPPgk35Z3Z6gBvbXqRKVzi1uOZ1hC3/SIN8sYx0Q14VEdr7eK6ah20KYEqgNIOZmLp9la0KkGXwgYDA3xFpXOFLnLpQpBYZJc7oHEfRApCtz8Bf8/k4S0oMZzMVcz0tpkhz40dURtOBWIGM9pgmVu/IlpsLeSeH7KjjkCp5R3APOAqB0OFG2OUE1zLQZR2oAE1mjEKy+Ynh2cPgR7kNSQwduY/PRK8pEoGiSp1g2VGGUbKoN/iokK/Vxj+rabXhAek7YLoyvbEON95hatEFVMVmPYbl4m+BApsLKXX82DeRLoa7gMRpYNXNJSLNS7cpgB79kYqhT8LqsQ2ewzhBXlBkMxdOjpKqII3D+Ll9FrDHp2AMQ3SpcS8BcRo39gKL4zFOR+EmVOnRA7d2H9RfBLtt+K7OAb4twtZcy6Kc/KE4kcNCzNFrvQ0KGbYhk1vU1j8Xv5zZIwFXnrG32TKecqezwBp3scbQMyBi7Q5Khe2Fj2x/593WHy9H1MgugvtkxBuqP9qwUyEY9+KaXrIHPDyEY01lRtMiqurOtQ7h6mT9q2vFwmFoKeFniMl1sv7wCDV7WxGkE6v8odreGV0IK/8bkTdTtvxYmOZ//YP+MCtLkrXgm9uAKCrGY36nL1HUG/sWJCpb4H19/atEgy7bWFJzeDpnIDR3noek553FUwciczMBWXmuDHTukrDOMKx8MHp74Tlz9y2I17E8enDXTDkp5efoa2JmG3Mn+Dqo34F6g6G8KFKbFz7AtgdBZZlz/87L1jQRQcrvtejn6a3l9Sge7uKgP/XKxKTv7C7bcL+/sPLpyO1uA/lBXk3q2uArw4RI8yqgLIW9+m6lJtUhOm+tvlh6gKT/jGJNuhJ+XcpRvR+2G8npFOcKz6An3e4AQ3p+e1A25ydiyN083atabe/J+V2ChFq3u15urFgE6tcDUhzRAOFK9YHo1VT1HGkm/BvSCXemiszfueJO7VGoNxx29GogmTQ70pZ4tEH7c7KbHkcdqnejKUssRnQjdUd4fS37fmTp8jP5RgxEXygBBs/UVVKKzVgywtIU9VPnPomzeIA6jeQoG26a/Wqup/eXQWdgrS/qZ2+Ux3NnxnySzW3hJpXUVS9letr6WrBzeDpDVLNaamGEo9WD5iKHRiQLkJjqnDtlYzZTSMtDV06ehwLqkRLwTpqj68ARhvQvhRqZWMfyN6veVLiE3pnXgsYOt385wgFBHZjHSuv505lXZ98i42AIVEpVHcC+hwIp6IjmdnUdf+RvFBHBEg1cxG00OBSPoR1NvQ1bZ370oOOlH1cJ2RL0HyAkIbUA7reVC64JNxKKp5okdH9cq4VuI9lU+yC0+oNB7dPXC+GdPWj6suZbXGKsqMRKm1ZgtYYwGsnjH9KhWw/dokZZVb5fLxiUZOm4UFSalEZIvUeAOCrFSoznRMMfUYQJEBEBaqHQuLHtdL5HkDfg==\"}");

			KeyStore ks;
			REQUIRE_NOTHROW(ks.Import(keystoreJson, passwd));
			const ElaNewWalletJson &walletData = ks.WalletJson();
			REQUIRE(walletData.HasPassPhrase());
			REQUIRE(walletData.Mnemonic().empty());
			REQUIRE(walletData.xPrivKey().empty());
			REQUIRE(walletData.PassPhrase().empty());
			REQUIRE(walletData.GetM() == 1);
			REQUIRE(walletData.GetN() == 1);

			REQUIRE(walletData.RequestPubKey() == "02644bccfc0abc15fe9b800ba41d323665c3935aacf761900320c036879e4db1ab");
			REQUIRE(walletData.RequestPrivKey() == "c56db84b2907d6364437b3ac77a6bf56467e3c8d47327053189253f8700b4cec");
			REQUIRE(walletData.xPubKey() == "xpub6D9d1MdbeSWkBMh9eMXHvdLNnXPLSZysWZBD4WDeRFF4W59qimVy29MF2Nnm2rGLVGRguFw4QF1DF7BTbS5fUZ91y354bJcJjn4FxvKywrv");
			REQUIRE(walletData.GetPublicKeyRing().size() == 1);
			REQUIRE(walletData.RequestPubKey() == walletData.GetPublicKeyRing()[0].GetRequestPubKey());
			REQUIRE(walletData.xPubKey() == walletData.GetPublicKeyRing()[0].GetxPubKey());

			// keystore from web wallet and without private do not support owner public key
			REQUIRE(walletData.OwnerPubKey().empty());
			REQUIRE(walletData.GetCoinInfoList().size() == 0);
			REQUIRE(!walletData.SingleAddress());
		}

		SECTION("multi sign keystore with private key and without passphrase") {
			const std::string passwd = "s12345678";
			const std::string addr = "8ZmSJ3Kz7nSmPBdXYmspYYHKqU11617ks5";
			const std::string &mnemonic = "令 厘 选 健 爱 轨 烯 距 握 译 控 礼";
			nlohmann::json keystoreJson = nlohmann::json::parse("{\"iv\":\"2ZKIcWrVmEPp3I2B/4WYcA==\",\"v\":1,\"iter\":10000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"4+aks85XBtI=\",\"ct\":\"twQzpVMf73LqRTO7QRETlSKInvVzTuG2LGC6RErO1irfOjdQm0zwEXQDcQ0YkENcQ1adoMeDWmw0iy7BxrUHL9/D9kudqeQ/3jkmjARRGtQRYK5FDUIK+T70+zgeWSm6J0zDuY/AlWbx4rwHTizzzpw+BeWBFnO9u7KYrzrTJ0zh/jI/O4itnCmUlkQd6bJOHqykFKlma3RulXtHqEpSz9x4UKXm0ynX5giHi/8F5/1u0YwqMEGQiF34p2J6+JT2NFVT8E8DePlt8eW69Ymny9hLENKQ1h9vti29ep2ECyo11C8DMS6IxmaA/HZSogxk9UxeExJz11vkSJ+cJ/VdRmHaqrjCQ0weiUr8p8C8l8lJg5MFPh4SYWXSL8voLoJPs14p/czA04SQqiqwILxmdLRcz1lThvbyFL0LtTGyPZx3ZYkRGOvfJLa3sJV7fs0kSvM+neFYYjI7fHufn083QFIWFVVKOFbV93WPolCAwZIXmd7OfWWE00qcNTPLEY4kHeElk06KjhQJMsCv6ddXbv4N54GKgmPp6EuxMpsGx5AxTtB8S6mM8Lik9/7wMpmfYiKHICoFuQDuPIroVhDw27G3PlDSxEzDDNwJdaYNCoXfwnC8IIkuOEni7LgTMpNUt03cyuUGPODeC2pkoRqbdWJSwhpLgCGrD/tcdrZYnVBc0+ru4Gl3F3VCAt04wzilMjJ4C2hGHdhNgxJc7frCU7yvFKOmlF55hDokWvQc7SmwcgFYkBwM6oQYRKfg5kEl5DzyYhzbaDuzVxmIQ6XgnwOWNqE5/xOp3GesxAFocta5OA4/+2FifGjWYgaDFlm9HGUsUukvzBxxHqUXy8EIUqy4cs0lqV6Hpl8WioYq+NYyc2jr3QpjjwYfV2tY9u2uGfDwHsQtsTznpufHRIP9GreLSgsbIf0OMsyutiXBmK9Yhs7JqM4KoG3VnqnflF4ymg+QehZbI89WPDVxbImG11qKgbAK4/38dUBxMWq8iiBnvi6Ykxbjo36vArLbg57hki53aaPU7+SuO/mijFY9jxi43e8BX/Tx62/l3c+vvYA5NaHAuyzpmxniNsND+wkb8OquHw8CRjgoOoFtszARSenChsmcRBNOcikNzOX3AK0aIGmAtOUSljre3UqdW1nTMTvHc01+AoCwGAQNfKxvOYZ2yGC5/0jmHA3mzVi/IigkKVVXi45E3Aw9QSQgXviEfwVtc+DFhwK+hYAP0YzJ0qeaIfjNFe7cbF9SFEUdkKn0+n4gb2bcx2PIenxzsLqUhCCc0UY25S5dbC0U3Zxz7TD5M6zvnzLLuJf1keH8vLYKJ0bUkcfKp/ZtE5D9zYRis0QYip5nnIaRksbI1UDl88BaQ8/Rtm38igIaAoKmOhNuGFdzViHxS5oQGHHNdq/lehBAF1hS3iEqBHrffDOgmlCKY7xldJ68kYLd0qktwuFRJne+POmrbGlOUcXylQUBwjdWyQM9Cj2C5VlRifa6xbeKT8CVnEhEbyyD9cCsEKwpGA741LqW2dGnIl080USa8Ipj6iS/jHcT8sg3Xpm7FxjAhN3xkSmuNZBfyR/AOKVNmiFv0/ROTUq1X4tqhnK6wTMnAX0t9zBCdUnJWj+EvCwf9ugRgavgIDTuNveUwid4dvJUstHCkbSPS/pBqqaKpBx52xgKOfKkdOyUbI89c/oKedzrNRYP/0Dvr2whtbCBTOqiwHCU7v19Sl79UEB26NGgbYljuhcyE7pxxVELN4Hh9dEQTj1EKReOQQZjOe+so+WFkUQ8mdxNe2fXuwR/rmyoF5r4t08yaR9uebtBQS0CMQYq/JwCwu90OyvSvJzWd4WUSokbJfH11zUHvus2xH5IAE0cUuUJf68v0MlOh8GxRDJZpAk8vsu9p4R0JyWt2mkebwEcURznVNL7bwa6dUN6lr+OFjFnezecxFlahc2jttvmbY0sWEgmjBhbiySeHJgj89wJj1XVMyCkwet+TdgoQPmMbdHWaEGJInxhv50LSzJ89lPJCxiEqIVi3kbzmRcccISrouG9nlBLgWvlReqR1OD6YV/R46Uym/wVVCBn3iA+W1s9RpOC+McuBv84MglEGU/5L4hhvIgeYhO4o42N7avYSihNOOIHztZuY3cCkpOQVtBLvfsG1TmumrTvJoxqyAnKIa3WQkI/BXh9mJLGTGXPmqzJtTIpEj72bF5MvITsR8t9JpCOq+ye6E7bimBTaZbtSfOm43LXfdRM6c9I74wxULA7IhNmeJZI+YRmKGbqfdxZmJpHLBdYjw5nyEyF2yPBK1hjwjxL4ZG3bOqAehdTcwGuaid/dzXEOxbIOvrpFBupl9f9t0Ov2IQRmW/NiHYlNyLXsvr/O4oBtU3ATt9s8MPqG66Tk+HJH8sAzWOt2KJUK7+unKw1eM2hbbeqZDQ=\"}");

			KeyStore ks;
			REQUIRE_NOTHROW(ks.Import(keystoreJson, passwd));
			const ElaNewWalletJson &walletData = ks.WalletJson();
			REQUIRE(!walletData.HasPassPhrase());
			REQUIRE(walletData.PassPhrase().empty());
			REQUIRE(walletData.Mnemonic() == mnemonic);

			REQUIRE(2 == walletData.GetM());
			REQUIRE(3 == walletData.GetN());

			uint512 seed = BIP39::DeriveSeed(mnemonic, "");
			HDSeed hdseed(seed.bytes());
			HDKeychain rootprv(hdseed.getExtendedKey(true));

			HDKeychain requestKey = rootprv.getChild("1'/0");
			REQUIRE(walletData.RequestPubKey() == requestKey.pubkey().getHex());
			REQUIRE(walletData.RequestPrivKey() == requestKey.privkey().getHex());

			REQUIRE(Base58::CheckEncode(rootprv.extkey()) == walletData.xPrivKey());
			REQUIRE(Base58::CheckEncode(rootprv.getChild("44'/0'/0'").getPublic().extkey()) == walletData.xPubKey());

			REQUIRE(walletData.GetN() == walletData.GetPublicKeyRing().size());
			std::vector<bytes_t> pubkeys;
			for (size_t i = 0; i < walletData.GetPublicKeyRing().size(); ++i) {
				bytes_t pubkey(walletData.GetPublicKeyRing()[i].GetRequestPubKey());
				pubkeys.push_back(pubkey);
			}
			Address addr1(PrefixMultiSign, pubkeys, walletData.GetM());
			REQUIRE(addr1.String() == addr);
			REQUIRE(walletData.SingleAddress());
		}
	}

	SECTION("old keystore from spv wallet") {
		SECTION("with private key and without passphrase") {
			const std::string passwd = "Abcd1234";
			const std::string mnemonic = "卢 口 宴 点 睛 量 旦 奥 沫 洪 鲁 距";
			const std::string passphrase = "";
			nlohmann::json keystoreJson = nlohmann::json::parse(
				"{\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"ugZV9gZlLiUiYZ0wCkxjP+/OFhoRF/bYtwEpBdilpIkhiNSvOgxjRX8R6wyT84gR809Sd/Stc3moH+zAEw1qgOKeLatz33r+H2K5OnpR1MIkflpYI2XFPZBt0+TckN/OsTq1v+QiFRlSLtdSJWdWS9o943nBxLc81x7HbcdPpGTaPAXUj6HNp1V5HW+qLdc9lUkL0EYPRZ//9VLJY9AdhuNhsOZxBokAVYy4qobQ1p4tWaj4v8/qYUMcokdKeDYZmsdY8r5wVPmHehACkBMAfPcNbzwS32M8wHq2WeNDie/oPmrKZLn74vYURweSBAUqF4x/QDbitJkc7aonSebx0qOvLhrJap5MtP8R19quGNqmT2Vb7aPsPTzdExrRPXJyZZ4s91iJYWYkq9+tSmWFGUdbMnJv8hlVKNfdxM/DzaBvwFyGgIR58VvESFOQwuPLCUEgUPQjjDhE31+CqQUwLH2SSDNE9lNgGGRzCKdyXFKIYbk3cF7/E1UzHXnt0gOBxneGCpqE+Qo4ChQ+4YNxdRdAwnrbYJbhxRvbEoCguGgiWnBmrJUODGBntM+OEQexNqJ9hoAPBezm4hrlAz274owPKhRnsjpZrJ43ChvVTiMUji6RpignlpV5mYcjD6dpU7tf3yeibD5tdhzb7vsBe3BLqW5x4z66H2JbLz542ts0qaMwpX0/SE3r7vTBsH5ZxrHYzObXbUqqKAUfyXhVfoDCC5Zm6CgaKhbuxZvtxaYnatNkwYCyS+ihy0By6LvNmFyZsDUGq3tiRAkpNRIxJSuzFbnYGNgvoGj9W6MNmbgwL5kUiHOTHd+jloVlmKkmGdTaxBAzgQoX8NyM35pR7zHc6dccuOQBT7IcEHA0QF4WXEwQR3lztX/zHYsWBSlnYn782suDBKtF2pmxW7vbLlylyn804Ni1HjaeRQYztZe5t2ld3Bum4rSIW7+tQux2jqNAitayEvxb4j2owPLbZwKEOwDik4JJG65smQ1FfwNkiusrWRNyjJgBlgZ5HchrPSwGY48Z8+J6oRRZOqJf4QyTfT7xwq0gYvUXBSQjj+ZtmZprToohO9N8RV928AEwHzUlFamS2a/DtZ03qMnXahKE4K5EophFIE0FbUXrxxbZYj2v3H7s7xoVoDAVIYwFyEhapmD69aglei0a8lcfuryB3a5OMirnW3BJblD8t8MfTncoeoRhT2qYhrDfU6/A+tL3ArKaVI9IM070gVTy24Co/gk1svfKdsnKDRq4x80aydpcuw9rilS+gbAHRcnts/vc9oqelLzChKFpGFRbkBgccKNFlO4bhPnwGFjfVc3seAw31zFOu3ixO42o8U1s1vlSyIMmroMnV4hE609FJlyQXK4I9htl8zrxx5J5u7DMjy3w0nWIckpz04+oSXujgaK9p/EYdIQDGQq0sQzoazeXxTxxTPNLWEf/chepgBnd3L+sxKRtQKTEksFSJqlasy4qMWo+IxEayFlRWNarUA60xWywCdEffNAqxxLRR/WoExXUuz2lZnpk3MbJ7ZkEP+CuiObfiJNt5dGkUkaEjyhakDv9Ug2iDulu1eDemMJipDflndNux2xXGKU2K3hOe2GfMxNcbWdq4ll44qx/uf+kqr/KYq4bxJ8+3uW8T85ORFKVU/RypRU884q+vGYlF9KpPlIxWUCA7pMtV3ClZ9YbWlfexKacL2VxZbVxsxAt4HS88PhPW4reafL3KY23Au9V07XZp9XKdu4EzM14OUzA0j8yYH52/X5iXEGD+sxrZogBtl3l4xAesO981spamZhjLdeBWUJnA6X1JY5MY++Vh4I7stvi0R0LQ4f3LjMXzCdCLDbrNchPtV8m/VlRXBxfw4OJ6FOxvkKEFqGBygOYH613cqa6HW/AlqwhTN42azUYBXMbUHPv3j23FiRrSzjg0OzEnkEVZ20yNn8B8JaKg+9UkWKOy9Gyqrz/LfMsIOs=\",\"iter\":10000,\"iv\":\"aknqq5k9rY7GfrIzSXLt1Q==\",\"ks\":128,\"mode\":\"ccm\",\"salt\":\"HoI5PkRT1Wc=\",\"ts\":64,\"v\":1}");

			KeyStore ks;
			REQUIRE_NOTHROW(ks.Import(keystoreJson, passwd));
			const ElaNewWalletJson &walletData = ks.WalletJson();
			REQUIRE(!walletData.HasPassPhrase());
			REQUIRE(walletData.PassPhrase().empty());
			REQUIRE(walletData.Mnemonic() == mnemonic);

			REQUIRE(1 == walletData.GetM());
			REQUIRE(1 == walletData.GetN());

			uint512 seed = BIP39::DeriveSeed(mnemonic, passphrase);
			HDSeed hdseed(seed.bytes());
			HDKeychain rootprv(hdseed.getExtendedKey(true));

			HDKeychain requestKey = rootprv.getChild("1'/0");
			REQUIRE(walletData.RequestPubKey() == requestKey.pubkey().getHex());
			REQUIRE(walletData.RequestPrivKey() == requestKey.privkey().getHex());

			HDKeychain firstKey = rootprv.getChild("44'/0'/0'/0/0");
			Address addr(PrefixStandard, firstKey.pubkey());
			REQUIRE("EPmxXC5orLrXd1FJK8Skz5tmf2gDreh8fd" == addr.String());

			std::string ownerPubKey = rootprv.getChild("44'/0'/1'/0/0").pubkey().getHex();
			REQUIRE(walletData.OwnerPubKey() == ownerPubKey);

			const std::vector<CoinInfoPtr> &coinInfos = walletData.GetCoinInfoList();
			REQUIRE(2 == coinInfos.size());
			REQUIRE(coinInfos[0]->GetChainID() == "ELA");
			REQUIRE(coinInfos[0]->GetEarliestPeerTime() != 0);
			REQUIRE(coinInfos[0]->GetVisibleAssets().size() == 1);
			REQUIRE(coinInfos[0]->GetVisibleAssets()[0] == Asset::GetELAAssetID());
			REQUIRE(coinInfos[1]->GetChainID() == "IDChain");
			REQUIRE(coinInfos[1]->GetEarliestPeerTime() != 0);
			REQUIRE(coinInfos[1]->GetVisibleAssets().size() == 1);
			REQUIRE(coinInfos[1]->GetVisibleAssets()[0] == Asset::GetELAAssetID());
			REQUIRE(!walletData.SingleAddress());
		}

		SECTION("with private key and with passphrase") {
			const std::string passwd = "Abcd1234";
			const std::string mnemonic = "symptom glove trigger fossil stage valid cherry bid dolphin kit promote vital";
			const std::string passphrase = "Abcd1234";
			nlohmann::json keystoreJson  = nlohmann::json::parse("{\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"W21+P8TWNznXpQlJlSqrbWMbZZof7rmDkwK0oTYPjqhVsUNf+PF3EhNPsLV7OSJOF7SwjCl0wB+6xEUjxXNcuqMBjQQJ4Mduv3APesxupdh2H43a56ZlAZV9wIT5Tk1YaUIlBsIqGgJ+ZTyHYohWj6V9+IH6mJSxRSwFhr9IHyL9Z6KQqc5AngLzMpRSC4zAnpDxZx/b4N5hqS0sm42MhgZxnXDC31100PR2Mzy/PFoAgcN7whT4eCsF3v2nJ/RG3BTrw9zz4sCuVwTH8vRs7qSB9KWKoNUsfG2qtGtidc8KPg5pwHZnZlqK69Yp9vifFlQEo4e9miMSzKRwce4sUe/lO1A7HCZt9Vq8lDJ1PerX8KMeLyy9fsqkjDpRhENs1Vopa6Xr08ksZn8YhcRWYc1p/K6tgI7gLoZpD+hG22HDdNHKY+HVskQsL5LhR5wQE609gPO2zs4JTK4TB6hwqD8zcfkDNJqIFs+3iqAmfyDH3TfXGDyrGkTIHUM5HjcVk4bECBv84lI4lX8y49yapwQdigXis93uSR4EAxBqHsmy9mCTIjUDjTcltJVwgsqt6zBrfJy0qR7/qzY9nR8dnOo6i2Ep3kfsf/MSHAhovtfRcXW6lwvDs3lKMDAnQqx1Py0+7irNYvdy4GfwPIopJgcoZEC5x2JhTnXaN0zk7b8XyGy9MImQQnzws6t0OEJLHLXYwZyMS/7+kx5XwsmSrEgFUVtSlx1Gcfj8RVzKjv8oADrh1obEo8eTjiHL1p2dE6dRH0hkgTsm/0sJz9FeNJqmOa8kTuYg6+8gEfmjiHLd/qFAb5sJg0WLThAIWNXGFoLSld/9e5tO1xBMjKrt1CtAUPq++Lo8q1uAmdZXnWkeUs2DTMGdPNdcDY88BpbKyYOlVh1Wq2n9hSjrBySANx+UVxJfyZXOwcmw23eKsNA31V0yhGvSO4ZgLsIgmgtbSWCRItX5XT6YG2GuCN8R3Jx5lE6rzoQrWzSZgGTooRTsw7T8qmVy6E8DLW1+OiZS5n5vyl0pI+dxDx75Yt23O34dh1nvJLmGN0oQEabA6qCXBxdgf/O0y319HgnjEusmoyqG4hs6GGQ+u8qxUviXMFreQAersgsNoF8Jo+24aRNHTq+fEcjOh5DFvSlYFh763Eu7GSqS2fLnIRrBW/4Y7WMdhdEbFxLueDAWdt0Nu4GXZsGLCUaAjGlzfEwgHxoBTca6/4HDeg7XbrbemKLU+KQFdfxU4zlLxJyR5zHRnRVMmFHQJf3nYkjYAuKkdgv9+nvqEfeX0Xn5/hdNgTAncDPfNKIt5NoJrMVyYGFjpnvRZZ4YxRW56+TK0OdTvVsc6Sp1gsnp4y3MkHNxvMktjZKPl2TPDi1diUUCoKqkP0pjADZSwxvo4vOWKLPKCivcREi0ID8TIVIJ+SM0jO6fcQ==\",\"iter\":10000,\"iv\":\"oD/5rjOJ8wQpv1jS8QH6kw==\",\"ks\":128,\"mode\":\"ccm\",\"salt\":\"iIjm5NrNxR0=\",\"ts\":64,\"v\":1}");

			KeyStore ks;
			REQUIRE_NOTHROW(ks.Import(keystoreJson, passwd));

			const ElaNewWalletJson &walletData = ks.WalletJson();

			REQUIRE(walletData.HasPassPhrase());
			REQUIRE(walletData.PassPhrase().empty());
			REQUIRE(walletData.Mnemonic() == mnemonic);

			REQUIRE(1 == walletData.GetM());
			REQUIRE(1 == walletData.GetN());

			uint512 seed = BIP39::DeriveSeed(mnemonic, passphrase);
			HDSeed hdseed(seed.bytes());
			HDKeychain rootprv(hdseed.getExtendedKey(true));

			HDKeychain requestKey = rootprv.getChild("1'/0");
			REQUIRE(walletData.RequestPubKey() == requestKey.pubkey().getHex());
			REQUIRE(walletData.RequestPrivKey() == requestKey.privkey().getHex());

			HDKeychain firstKey = rootprv.getChild("44'/0'/0'/0/0");
			Address addr(PrefixStandard, firstKey.pubkey());
			REQUIRE("EWkMVjB48znLGXCNNpLzf2rLmnX1iU91FP" == addr.String());

			std::string ownerPubKey = rootprv.getChild("44'/0'/1'/0/0").pubkey().getHex();
			REQUIRE(walletData.OwnerPubKey() == ownerPubKey);

			const std::vector<CoinInfoPtr> &coinInfos = walletData.GetCoinInfoList();
			REQUIRE(1 == coinInfos.size());
			REQUIRE(coinInfos[0]->GetChainID() == "ELA");
			REQUIRE(coinInfos[0]->GetEarliestPeerTime() != 0);
			REQUIRE(coinInfos[0]->GetVisibleAssets().size() == 1);
			REQUIRE(coinInfos[0]->GetVisibleAssets()[0] == Asset::GetELAAssetID());
			REQUIRE(!walletData.SingleAddress());
		}

		SECTION("with private key and with passphrase and single address") {
			const std::string passwd = "Abcd1234";
			const std::string mnemonic = "ride chalk song document stem want vocal win birth hotel pottery kitchen";
			const std::string passphrase = "Abcd1234";
			nlohmann::json keystoreJson = nlohmann::json::parse("{\"adata\":\"\",\"cipher\":\"aes\",\"ct\":\"/17CPdZfIS+iT7SWtt0x7f4vsokkOad9tVoOxt9gt0IWf0347BNqTC02NnDEftlE6AzqLxMQ7TY3QSTK/iOKx7gQdYm+1qU1jDCFjrwFvs27KHWtfFrW3XHYkzk2XQiZ2pf/EoY4w3K0Z8L4fudr24btJYP6M+/IJews1atdWDmCd3/FonsRJEaQfeDMMr4nBif/0+fcMwbcRprWX+qxQrw3A/VSZp6vLHROG7VA4mKCxHUOryCofgbJfo9z/6bQQuXL6R4cYEUnYqn7fQAiv7SNv54AlcgiVhnfYDBl6mqUMrmvsX6pYSzQvst6p3a2PCWln5YkLDFEsHaxqtDeLSUZz7Z0VUCXnLXM103CoIXMZbHE5M1ILuF+tOomORy1po4Xk32cyH78h0Dnuz5qGXXPFc/NsZ9v1oXERUnVPhyYTQE+GMaehMSXEttdlZRg4vgSAwro3kgdt8o39ou81s4KPjv9PIowIuFDmT0VKFvM+QKj8MTUSVEQPCdJjsrg960AycVo00/YZpD2AJbNjSulouiizZ61B2TRIGa0afbAl5D8Mz9xRbbfje2Nfc81il+X2dSgT3DX4E/ZbVCFVHuWgbDYlcftq6YfGd231WkgZlCEYJQG6Ab2jiVHLe8yyz0jrVtz5BPueqKrtDbvVHDQ7g79J7UJvG33vdbtsSNczA+oBcxaHiJ1qoUmJyO2W4x3u8vv6mkP2/AWaFaNBHEheBJ3j9DU898766K0ElGZJky4pucZmyWsvJqteKUlohfGpTYlVUaRrXxIhIx2cWelYKrQTni6iDzfOP1N6WvYQlvCq8uAdsQHt6H7wbM5QTuJp22vdiIpFkhYLII5acxNATMHpHZMIrelTsaHr/dqEtSmvZOb0v/qlaPYsDovoLBeW4G5vx53Mz6KFH6cdwPTZL+NKsWim8wcARD2VY7U7TvNoZqjsn9E32sapXCVjRtM5zzdhIYzqU1/UNFHxCUZ0o5vvW0P48Y1QUOIssD3h5hw1GiVg0A/VRW/PxB96wQjVeYcuAquDcn2sqpA6NVSkXsg+T7p+uHiqwx85wZ1zfRuZHhq3ZpmNAIzRMm9uyngFFVoSBewCzRvsMP/kjj5mqLx8igjdmUXa2hidDuAXVO7KdFvx5KFgpLM2fmhxcl1GxSY7OGpmU4gLncLKD1C5oBnCW+QW/BUl6e1++K7YrtpGiZrbkV4WLqHdPF1SNciATdyAMvQ21b/6MBOKFc3wh8CjfOyRhK8QnAmFauOqqFx5F4NXtx89wXQUMhf5tVnLp0NVxzmsU7BQSBVZAyJY/27tmbTR0Nv3UDbWscGAX7uaUJyvAxZJ3YXSLkZ8ITYb/w2Il/WvcTMU6v5guO8Xm+jkLk21JsrzZAG+pgjquWBaeqJ2/yF33TVMsBZT5tzLQonN6YX\",\"iter\":10000,\"iv\":\"vwYqwOxaqkGA9JQetCHtcg==\",\"ks\":128,\"mode\":\"ccm\",\"salt\":\"DVFsO/mhTm0=\",\"ts\":64,\"v\":1}");

			KeyStore ks;
			REQUIRE_NOTHROW(ks.Import(keystoreJson, passwd));

			const ElaNewWalletJson &walletData = ks.WalletJson();

			REQUIRE(walletData.HasPassPhrase());
			REQUIRE(walletData.PassPhrase().empty());
			REQUIRE(walletData.Mnemonic() == mnemonic);

			REQUIRE(1 == walletData.GetM());
			REQUIRE(1 == walletData.GetN());

			uint512 seed = BIP39::DeriveSeed(mnemonic, passphrase);
			HDSeed hdseed(seed.bytes());
			HDKeychain rootprv(hdseed.getExtendedKey(true));

			HDKeychain requestKey = rootprv.getChild("1'/0");
			REQUIRE(walletData.RequestPubKey() == requestKey.pubkey().getHex());
			REQUIRE(walletData.RequestPrivKey() == requestKey.privkey().getHex());

			HDKeychain firstKey = rootprv.getChild("44'/0'/0'/0/0");
			Address addr(PrefixStandard, firstKey.pubkey());
			REQUIRE("ENjw5MoRDuKRsuZ6WhhbzxmJ1yuSvecLSf" == addr.String());

			std::string ownerPubKey = rootprv.getChild("44'/0'/1'/0/0").pubkey().getHex();
			REQUIRE(walletData.OwnerPubKey() == ownerPubKey);

			const std::vector<CoinInfoPtr> &coinInfos = walletData.GetCoinInfoList();
			REQUIRE(1 == coinInfos.size());
			REQUIRE(coinInfos[0]->GetChainID() == "ELA");
			REQUIRE(coinInfos[0]->GetEarliestPeerTime() != 0);
			REQUIRE(coinInfos[0]->GetVisibleAssets().size() == 1);
			REQUIRE(coinInfos[0]->GetVisibleAssets()[0] == Asset::GetELAAssetID());
			REQUIRE(walletData.SingleAddress());
		}

	}
}


text:
/**
 * @copyright Copyright (c) 2017 B-com http://www.b-com.com/
 *
 * 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 <boost/log/core.hpp>
#include "xpcf/xpcf.h"
#include "core/Log.h"
#include "api/input/devices/IARDevice.h"
#include "api/solver/pose/IFiducialMarkerPose.h"
#include "api/display/IImageViewer.h"
#include "api/display/I3DOverlay.h"
#include "api/display/I3DPointsViewer.h"

using namespace SolAR;
using namespace SolAR::datastructure;
using namespace SolAR::api;
namespace xpcf = org::bcom::xpcf;

#define INDEX_USE_CAMERA 0

int main(int argc, char *argv[])
{

#if NDEBUG
	boost::log::core::get()->set_logging_enabled(false);
#endif

	LOG_ADD_LOG_TO_CONSOLE();

	try {
		/* instantiate component manager*/
		/* this is needed in dynamic mode */
		SRef<xpcf::IComponentManager> xpcfComponentManager = xpcf::getComponentManagerInstance();

        if (xpcfComponentManager->load("SolARTest_ModuleOpenCV_DevicePoseCorrection_conf.xml") != org::bcom::xpcf::_SUCCESS)
		{
            LOG_ERROR("Failed to load the configuration file SolARTest_ModuleOpenCV_DevicePoseCorrection_conf.xml")
				return -1;
		}

		// declare and create components
		LOG_INFO("Start creating components");
		auto arDevice = xpcfComponentManager->resolve<input::devices::IARDevice>();
		auto fiducialMarkerPoseEstimator = xpcfComponentManager->resolve<solver::pose::IFiducialMarkerPose>();
		int nbCameras = arDevice->getNbCameras();
		if (INDEX_USE_CAMERA >= nbCameras) {
			LOG_ERROR("Index of the used camera cannot be found");
			return 0;
		}
		auto viewer3D = xpcfComponentManager->resolve<display::I3DPointsViewer>();
		auto imageViewer = xpcfComponentManager->resolve<display::IImageViewer>();
		auto overlay3D = xpcfComponentManager->resolve<display::I3DOverlay>();
		LOG_INFO("Components created!");

		LOG_INFO("Start AR device loader");
		// Connect remotely to the HoloLens streaming app
		if (arDevice->start() == FrameworkReturnCode::_ERROR_)
		{
			LOG_ERROR("Cannot start loader");
			return -1;
		}
		LOG_INFO("Started!");

		// set calibration matrix for components
		CameraParameters camParams;
		camParams = arDevice->getParameters(INDEX_USE_CAMERA);
		overlay3D->setCameraParameters(camParams.intrinsic, camParams.distortion);
		fiducialMarkerPoseEstimator->setCameraParameters(camParams.intrinsic, camParams.distortion);

		// Display images and poses
		std::vector<SRef<CloudPoint>> pointCloud;
		pointCloud.push_back(xpcf::utils::make_shared<CloudPoint>(0, 0, 0));
		pointCloud.push_back(xpcf::utils::make_shared<CloudPoint>(1, 0, 0));
		pointCloud.push_back(xpcf::utils::make_shared<CloudPoint>(0, 1, 0));
		pointCloud.push_back(xpcf::utils::make_shared<CloudPoint>(0, 0, 1));
		std::vector<Transform3Df> keyframePoses;
		Transform3Df T_M_W = Transform3Df::Identity();
		bool isStop = false;
		bool isFoundTransform = false;
		while (!isStop)
		{
			// get data
			std::vector<SRef<Image>> images;
			std::vector<Transform3Df> poses;
			std::chrono::system_clock::time_point timestamp;
			if (arDevice->getData(images, poses, timestamp) != FrameworkReturnCode::_SUCCESS) {
				LOG_ERROR("Error during capture");
				break;
			}

			SRef<Image> image = images[INDEX_USE_CAMERA];
			Transform3Df pose = poses[INDEX_USE_CAMERA];

			// find T_W_M
			if (!isFoundTransform) {
				Transform3Df T_M_C;
				if (fiducialMarkerPoseEstimator->estimate(image, T_M_C) == FrameworkReturnCode::_SUCCESS) {
					T_M_W = T_M_C * pose.inverse();
					isFoundTransform = true;
				}
			}

			// correct pose
			pose = T_M_W * pose;

			// draw pose and display
			overlay3D->draw(pose, image);
			if (imageViewer->display(image) == SolAR::FrameworkReturnCode::_STOP)
				isStop = true;

			// display 3D camera poses of camera 0
			if (isFoundTransform) {
				keyframePoses.push_back(pose);
				if (viewer3D->display(pointCloud, pose, keyframePoses, {}, {}, {}) == SolAR::FrameworkReturnCode::_STOP)
					break;
			}
		}
	}

	catch (xpcf::Exception e)
	{
		LOG_ERROR("The following exception has been catch : {}", e.what());
		return -1;
	}
	return 0;
}

ext: cpp

max_stars_repo_name: ThibaudM/SolARModuleOpenCV

max_issues_repo_name: ThibaudM/SolARModuleOpenCV

max_forks_repo_name: ThibaudM/SolARModuleOpenCV


text:
#include <algorithm>
#include <boost/filesystem.hpp>
#include <iostream>
#include <kseq++/seqio.hpp>
#include <utility>

#include "amplitig.hpp"
#include "kmers.hpp"
#include "log.hpp"

using namespace klibpp;

// Amplitigger constructor.
artic::Amplitigger::Amplitigger(artic::PrimerScheme* primerScheme, const std::string& refFile, const std::vector<std::string> inputFiles, unsigned int kmerSize, float kmerMatch)
    : _primerScheme(primerScheme), _refFile(refFile), _inputFiles(inputFiles), _kmerSize(kmerSize), _minPrimerKmers(kmerMatch)
{

    // check the params
    if (_kmerSize > MAX_K_SIZE)
        throw std::runtime_error("requested k-mer size greater than maximum allowed size (" + std::to_string(MAX_K_SIZE) + ")");
    if (_kmerSize > _primerScheme->GetMinPrimerLen())
        throw std::runtime_error("requested k-mer size greater than the smallest primer in scheme (" + std::to_string(_primerScheme->GetMinPrimerLen()) + ")");
    if (_inputFiles.size() == 0)
        throw std::runtime_error("no FASTQ files provided");

    // TODO: collect thresholds from user?
    _minReadLength = 100;
    _maxReadLength = _primerScheme->GetMaxAmpliconSpan() + (0.10 * _primerScheme->GetMaxAmpliconSpan());

    // get the holders ready
    _readCounter = 0;
    _droppedLong = 0;
    _droppedShort = 0;
    _droppedUnbinned = 0;
    _multibinned = 0;

    // load the primer k-mers
    LOG_TRACE("collecting primer k-mers");
    LOG_TRACE("\tk-mer size used:\t{}", _kmerSize);
    LOG_TRACE("\tk-mer matches required:\t{}%", _minPrimerKmers);
    LOG_TRACE("\treference fasta file:\t{}", _refFile);
    _primerScheme->GetPrimerKmers(_refFile, _kmerSize, _primerKmerMap);
    LOG_TRACE("\ttotal distinct k-mers:\t{}", _primerKmerMap.size());

    // TODO: filter k-mers and retain only mutually exclusive k-mers
    size_t meCount = 0;
    for (auto i : _primerKmerMap)
    {
        if (i.second.size() > 1)
        {
            _primerKmerMap.erase(i.first);
        }
        else
        {
            meCount++;
        }
    }
    LOG_TRACE("\ttotal mutually exclusive k-mers:\t{}", meCount);
    if (meCount != _primerKmerMap.size())
        throw std::runtime_error("did not remove k-mers...");
}

// Run will perform the amplicon read binning on the open FASTQ file.
void artic::Amplitigger::Run()
{
    // get some holders ready
    artic::kmerset_t kmers;
    std::vector<unsigned int> ampliconIDs;
    KSeq record;
    int seqLen;

    LOG_TRACE("processing");
    for (auto file : _inputFiles)
    {
        if (!boost::filesystem::exists(file))
            throw std::runtime_error("supplied file does not exist:\t" + file);
        LOG_TRACE("\treading file:\t{}", file);

        SeqStreamIn iss(file.c_str());
        while (iss >> record)
        {
            _readCounter++;

            // check read length
            seqLen = record.seq.size();
            if (seqLen < _minReadLength)
            {
                _droppedShort++;
                continue;
            }
            if (seqLen > _maxReadLength)
            {
                _droppedLong++;
                continue;
            }

            // clear sets
            kmers.clear();
            ampliconIDs.clear();

            // get the read k-mers
            artic::GetEncodedKmers(record.seq.c_str(), seqLen, _kmerSize, kmers);

            // check read k-mers against primer scheme k-mers, keep amplicon IDs for all matches
            for (auto kmer : kmers)
            {

                // check if read k-mer is matched to a primer k-mer
                auto it = _primerKmerMap.find(kmer);
                if (it != _primerKmerMap.end())
                {
                    ampliconIDs.reserve(ampliconIDs.size() + it->second.size());
                    ampliconIDs.insert(ampliconIDs.end(), it->second.begin(), it->second.end());
                }
            }

            // sort the matching IDs and then find the best candidate amplicon ID for this read
            std::sort(ampliconIDs.begin(), ampliconIDs.end());
            std::vector<std::pair<unsigned int, int>> ampliconCandidates;
            int chain = 0;
            for (size_t i = 1; i < ampliconIDs.size(); i++)
            {
                // if successive match, continue to the next ID and keep building the chain
                if (ampliconIDs.at(i - 1) == ampliconIDs.at(i))
                {
                    chain++;
                    continue;
                }

                // ignore empty chains
                if (chain == 0)
                    continue;

                // if first chain, add it to candidates and continue
                if (ampliconCandidates.empty())
                {
                    ampliconCandidates.emplace_back(std::make_pair(ampliconIDs.at(i - 1), chain));
                    chain = 0;
                    continue;
                }

                // if new chain is < than existing max, continue
                if (chain < ampliconCandidates.back().second)
                {
                    chain = 0;
                    continue;
                }

                // if new chain > existing max, replace and continue
                if (chain > ampliconCandidates.back().second)
                {
                    ampliconCandidates.pop_back();
                    ampliconCandidates.emplace_back(std::make_pair(ampliconIDs.at(i - 1), chain));
                    chain = 0;
                    continue;
                }

                // final opt, same value as existing max so add it as well
                ampliconCandidates.emplace_back(std::make_pair(ampliconIDs.at(i - 1), chain));
                chain = 0;
            }

            // process the likely amplicons
            int binned = 0;
            for (auto candidate : ampliconCandidates)
            {
                auto amplicon = _primerScheme->GetAmplicon(candidate.first);
                auto ampliconKmers = (amplicon.GetForwardPrimer()->GetLen() + amplicon.GetReversePrimer()->GetLen()) - (_kmerSize * 2) + 2;
                auto propKmers = float(candidate.second) / float(ampliconKmers);
                if (propKmers >= _minPrimerKmers)
                {
                    std::cout << record.name << "\t" << amplicon.GetName() << "\t" << propKmers << std::endl;
                    binned++;
                }
            }

            // update some numbers
            if (binned > 1)
                _multibinned++;

            // this will catch reads which didn't get any primer k-mer hits AND those which had too small a match proportion
            if (binned == 0)
                _droppedUnbinned++;

            // compare biggest chain to max %k-mer content
            // remove chains with multiple identities
        }
    }

    // print some stats
    LOG_TRACE("finished processing reads")
    LOG_TRACE("\ttotal input reads:\t{}", _readCounter);
    LOG_TRACE("\ttotal dropped reads:\t{}", (_droppedLong + _droppedShort + _droppedUnbinned));
    LOG_TRACE("\t- short reads (<{}):\t{}", _minReadLength, _droppedShort);
    LOG_TRACE("\t- long reads (>{}):\t{}", _maxReadLength, _droppedLong);
    LOG_TRACE("\t- unbinned reads:\t{}", _droppedUnbinned);
    LOG_TRACE("\ttotal binned reads:\t{}", (_readCounter - (_droppedLong + _droppedShort + _droppedUnbinned)));
    LOG_TRACE("\t- multibinned reads:\t{}", _multibinned);
}

/*
collect primer k-mers
remove any k-mers shared between primers
check each primer has some primers left / select a minimizer?

grab a read
get vector of k-mers
loop through the vector and check against primer k-mers
add vector of k-mers to k-mer freq map of matched amplicon

requires 3x loops:
once to get k-mers from sequence
once to check k-mers against primer k-mers
once to add k-mers to correct amplicon map

improvements for effiency:
heuristic to skip regions during primer check (i.e, found one primer so can skip ~100 k-mers to next likley place a primer is found)
check k-mer against map as it is generated - might be a better way

additions:
bloom filter to each amplicon to remove unique k-mers
remove/exclude primer k-mers (but how to clip reads before/after primer sequence to within amplicons?)

*/

max_stars_repo_name: BioWilko/artic-tools

max_issues_repo_name: BioWilko/artic-tools

max_forks_repo_name: BioWilko/artic-tools


text:
#ifndef COMMUNICATIONSERVICE_HPP_
#define COMMUNICATIONSERVICE_HPP_

#include "Config.hpp"

#include <boost/asio.hpp>

#include "Thread.hpp"
#include "MessageHandler.hpp"

namespace Messaging
{
	/*
	 *
	 */
	class CommunicationService
	{
		public:
			/**
			 *
			 */
			static CommunicationService& getCommunicationService();
			/**
			 * This function is public because otherwise it the classes Session, Server and Client
			 * have to be friends
			 */
			boost::asio::io_service& getIOService();
			/**
			 * Runs the given aRequestHandler at the given port until boost::asio::io_service::io_service.run()
			 * returns. In the limited context of RobotWorld this is done by sending a "stop"-message.
			 * @see ServerSession::handleMessageRead( Message& aMessage) for the implementation.
			 */
			void runRequestHandler(	RequestHandlerPtr aRequestHandler,
									short aPort = 12345);
			/**
			 * Uses std::stoi for string to *int* conversion. Throws the exceptions that std::stoi may throw.
			 * If int > max short you lose...
			 */
			//cppcheck-suppress passedByValue
			void runRequestHandler(	RequestHandlerPtr aRequestHandler,
									const std::string& aPort)
			{
				runRequestHandler(aRequestHandler,std::stoi(aPort));
			}
			void stop();
			/**
			 *
			 */
		private:
			/**
			 *
			 */
			CommunicationService();
			/**
			 *
			 */
			virtual ~CommunicationService();
			/**
			 *
			 */
			void runRequestHandlerWorker( 	RequestHandlerPtr aRequestHandler,
											short aPort);
			/**
			 *
			 */
			std::thread requestHandlerThread;
			/**
			 *
			 */
			boost::asio::io_service io_service;
	};
	// class CommunicationService
} // namespace Messaging

#endif // COMMUNICATIONSERVICE_HPP_

ext: hpp

max_stars_repo_name: Dibido/OSM-productiestraat

max_issues_repo_name: Dibido/OSM-productiestraat

max_forks_repo_name: Dibido/OSM-productiestraat


text: #include <boost/spirit/home/support/char_set/range_run_impl.hpp>

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
/*
 * Copyright 2016-2021 Internet Corporation for Assigned Names and Numbers.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
 */

/*
 * Developed by Sinodun IT (www.sinodun.com)
 */

#ifndef BLOCKEDCBORREADER_HPP
#define BLOCKEDCBORREADER_HPP

#include <chrono>
#include <memory>
#include <unordered_map>
#include <vector>

#include <boost/optional.hpp>
#include <boost/functional/hash.hpp>

#include "addressevent.hpp"
#include "cbordecoder.hpp"
#include "blockcbor.hpp"
#include "blockcbordata.hpp"
#include "configuration.hpp"
#include "pseudoanonymise.hpp"
#include "queryresponse.hpp"

/**
 * \class QueryResponseData
 * \brief Data describing on a single query/response.
 *
 * Collect all the underlying information about a single query/reponse
 * into a single structure suitable for passing to an output
 * backend for further processing.
 *
 * Most items may or may not be present. If not present in the C-DNS file,
 * and a default is present, the value here will be the default.
 */
class QueryResponseData
{
public:
    /**
     * \struct Question
     * \brief Info on a single Question.
     */
    struct Question
    {
        /**
         * \brief the query name in the Question.
         */
        boost::optional<byte_string> qname;

        /**
         * \brief Question class.
         */
        boost::optional<CaptureDNS::QueryClass> qclass;

        /**
         * \brief Question type.
         */
        boost::optional<CaptureDNS::QueryType> qtype;
    };

    /**
     * \struct RR
     * \brief Info on a single RR.
     */
    struct RR
    {
        /**
         * \brief the name in the RR.
         */
        boost::optional<byte_string> name;

        /**
         * \brief RR class.
         */
        boost::optional<CaptureDNS::QueryClass> rclass;

        /**
         * \brief RR type.
         */
        boost::optional<CaptureDNS::QueryType> rtype;

        /**
         * \brief RR TTL.
         */
        boost::optional<uint32_t> ttl;

        /**
         * \brief RR RDATA.
         */
        boost::optional<byte_string> rdata;
    };

    /**
     * \brief the timestamp.
     */
    boost::optional<std::chrono::system_clock::time_point> timestamp;

    /**
     * \brief the client address.
     */
    boost::optional<IPAddress> client_address;

    /**
     * \brief client port.
     */
    boost::optional<uint16_t> client_port;

    /**
     * \brief client hop limit.
     */
    boost::optional<uint8_t> client_hoplimit;

    /**
     * \brief the server address.
     */
    boost::optional<IPAddress> server_address;

    /**
     * \brief server port.
     */
    boost::optional<uint16_t> server_port;

    /**
     * \brief server hop limit.
     */
    boost::optional<uint8_t> server_hoplimit;

    /**
     * \brief the transaction ID.
     */
    boost::optional<uint16_t> id;

    /**
     * \brief the query name in the first Question.
     */
    boost::optional<byte_string> qname;

    /**
     * \brief query/response flags.
     */
    uint8_t qr_flags;

    /**
     * \brief transport flags.
     */
    boost::optional<uint8_t> qr_transport_flags;

    /**
     * \brief transaction type.
     */
    boost::optional<uint8_t> qr_type;

    /*
     * Query-specific items.
     */

    /**
     * \brief DNS flags.
     */
    boost::optional<uint16_t> dns_flags;

    /**
     * \brief class of first Question.
     */
    boost::optional<CaptureDNS::QueryClass> query_class;

    /**
     * \brief type of first Question.
     */
    boost::optional<CaptureDNS::QueryType> query_type;

    /**
     * \brief Query second and subsequent Questions.
     */
    boost::optional<std::vector<Question>> query_questions;

    /**
     * \brief Query Answer sections.
     */
    boost::optional<std::vector<RR>> query_answers;

    /**
     * \brief Query Authority sections.
     */
    boost::optional<std::vector<RR>> query_authorities;

    /**
     * \brief Query Additional sections.
     */
    boost::optional<std::vector<RR>> query_additionals;

    /**
     * \brief query or response QDCOUNT.
     */
    boost::optional<uint16_t> query_qdcount;

    /**
     * \brief query ANCOUNT.
     */
    boost::optional<uint16_t> query_ancount;

    /**
     * \brief query NSCOUNT.
     */
    boost::optional<uint16_t> query_nscount;

    /**
     * \brief query ARCOUNT.
     */
    boost::optional<uint16_t> query_arcount;

    /**
     * \brief query OPCODE.
     */
    boost::optional<CaptureDNS::Opcode> query_opcode;

    /**
     * \brief query EDNS version.
     */
    boost::optional<uint8_t> query_edns_version;

    /**
     * \brief query EDNS UDP size
     */
    boost::optional<uint16_t> query_edns_payload_size;

    /**
     * \brief query OPT RDATA.
     */
    boost::optional<byte_string> query_opt_rdata;

    /**
     * \brief query RCODE, incorporating extended RCODE.
     */
    boost::optional<uint16_t> query_rcode;

    /**
     * \brief the size of the DNS query message.
     */
    boost::optional<uint32_t> query_size;

    /*
     * Response-specific items.
     */

    /**
     * \brief the response delay.
     */
    boost::optional<std::chrono::nanoseconds> response_delay;

    /**
     * \brief response RCODE, incorporating extended RCODE.
     */
    boost::optional<uint16_t> response_rcode;

    /**
     * \brief the size of the DNS response message.
     */
    boost::optional<uint32_t> response_size;

    /**
     * \brief Response second and subsequent Questions.
     */
    boost::optional<std::vector<Question>> response_questions;

    /**
     * \brief Response Answer sections.
     */
    boost::optional<std::vector<RR>> response_answers;

    /**
     * \brief Response Authority sections.
     */
    boost::optional<std::vector<RR>> response_authorities;

    /**
     * \brief Response Additional sections.
     */
    boost::optional<std::vector<RR>> response_additionals;

    /**
     * \brief Write basic information on the query/response to the output stream.
     *
     * \param output the output stream.
     * \param qr     the query/response.
     * \return the output stream.
     */
    friend std::ostream& operator<<(std::ostream& output, const QueryResponseData& qr);
};

/**
 * \class BlockCborReader
 * \brief Read input in the block CBOR format.
 *
 * The block [CBOR] format consists of blocks, or groups, of query/response
 * pairs. Each block has a header, comprising tables of information likely
 * to the repeated in individual query/response pairs.
 *
 * A formal description of the file format is given in a CDDL
 * specification in the documentation.
 *
 * [cbor]: http://cbor.io "CBOR website"
 */
class BlockCborReader
{
public:
    /**
     * \brief Constructor.
     *
     * \param dec               the decoder to use.
     * \param config            the configuration.
     * \param defaults          default values.
     * \param pseudo_anon       pseudo-anonymisation, if to use.
     */
    BlockCborReader(CborBaseDecoder& dec,
                    Configuration& config,
                    const Defaults& defaults,
                    boost::optional<PseudoAnonymise> pseudo_anon ={});

    /**
     * \brief Return the data for the next Query/Response pair.
     *
     * Retrieving the Query/Response pairs should probably be done
     * via an iterator. This fills in for now.
     *
     * \param eof true if data supplied, false
     * on EOF.
     * \returns data for the next Query/Response.
     */
    QueryResponseData readQRData(bool& eof);

    /**
     * \brief Dump the statistics for the block to the stream provided
     *
     * \param os output stream.
     */
    void dump_stats(std::ostream& os) const {
        block_->last_packet_statistics.dump_stats(os);
    }

    /**
     * \brief Dump information on the collector to the stream provided.
     *
     * \param os output stream.
     */
    void dump_collector(std::ostream& os) const;

    /**
     * \brief Dump info on file earliest/latest/end times to the stream provided
     *
     * \param os output stream.
     */
    void dump_times(std::ostream& os) const;

    /**
     * \brief Dump address event info to the stream provided.
     *
     * \param os output stream.
     */
    void dump_address_events(std::ostream& os) const;

protected:
    /**
     * \brief Read the file header.
     *
     * Read the file header and extract the configuration information
     * from the preamble.
     *
     * \param config extracted configuration information.
     * \throws cbor_decode_error if the CBOR is invalid.
     * \throws cbor_file_format_error on unexpected CBOR content.
     */
    void readFileHeader(Configuration& config);

    /**
     * \brief Read the file preamble.
     *
     * Read the file preamble and extract the configuration information
     * therein.
     *
     * \param config extracted configuration information.
     * \param ver    the file format version. Either format 1.0 or format 0.2.
     * \throws cbor_decode_error if the CBOR is invalid.
     * \throws std::logic_error on unexpected CBOR content.
     * \throws cbor_file_format_error on unexpected CBOR content.
     */
    void readFilePreamble(Configuration& config, block_cbor::FileFormatVersion ver);

    /**
     * \brief Read the configuration in pre-format 1.0 files.
     *
     * Read the configuration information.
     *
     * \param config extracted configuration information.
     * \throws cbor_decode_error if the CBOR is invalid.
     * \throws std::logic_error on unexpected CBOR content.
     * \throws cbor_file_format_error on unexpected CBOR content.
     */
    void readConfiguration(Configuration& config);

    /**
     * \brief Read the block parameters in format 1.0 files.
     *
     * Read the block parameter information vector. Set configuration
     * information from parameters.
     *
     * \param config extracted configuration information.
     * \throws cbor_decode_error if the CBOR is invalid.
     * \throws std::logic_error on unexpected CBOR content.
     * \throws cbor_file_format_error on unexpected CBOR content.
     */
    void readBlockParameters(Configuration& config);

    /**
     * \brief Convert byte string to address.
     *
     * Pseudo-anonymise the address if that's enabled.
     *
     * \param str       the byte string.
     * \param is_ipv6   is the address IPv6?
     * \returns the address.
     */
    IPAddress string_to_addr(const byte_string& str, bool is_ipv6);

private:
    /**
     * \brief Read the info for the next block.
     *
     * \return `false` if no more blocks in file.
     */
    bool readBlock();

    /**
     * \brief Get a client address from the address table.
     *
     * If the prefixes are such that we can infer whether the address is
     * IPv4 or IPv6, we don't try to touch the transport flags. If not,
     * we will try to dereference them.
     *
     * \param index           the table index.
     * \param transport_flags the transport flags.
     * \returns the address.
     */
    IPAddress get_client_address(std::size_t index, boost::optional<uint8_t> transport_flags);

    /**
     * \brief Get a server address from the address table.
     *
     * If the prefixes are such that we can infer whether the address is
     * IPv4 or IPv6, we don't try to touch the transport flags. If not,
     * we will try to dereference them.
     *
     * \param index           the table index.
     * \param transport_flags the transport flags.
     * \returns the address.
     */
    IPAddress get_server_address(std::size_t index, boost::optional<uint8_t> transport_flags);

    /**
     * \brief Determine if client prefix means a full IPv4 address.
     *
     * \param b byte string with address.
     * \returns true if full IPv4 address present.
     */
    bool is_ipv4_client_full_address(const byte_string& b) const;

    /**
     * \brief Determine if client prefix means a full IPv6 address.
     *
     * \param b byte string with address.
     * \returns true if full IPv6 address present.
     */
    bool is_ipv6_client_full_address(const byte_string& b) const;

    /**
     * \brief Determine if server prefix means a full IPv4 address.
     *
     * \param b byte string with address.
     * \returns true if full IPv4 address present.
     */
    bool is_ipv4_server_full_address(const byte_string& b) const;

    /**
     * \brief Determine if server prefix means a full IPv6 address.
     *
     * \param b byte string with address.
     * \returns true if full IPv6 address present.
     */
    bool is_ipv6_server_full_address(const byte_string& b) const;

    /**
     * \brief Decode block cbor extra info to our RRs.
     *
     * \param index index of the block RR.
     * \param res   output RR vector.
     */
    void read_extra_info(const std::unique_ptr<block_cbor::QueryResponseExtraInfo>& extra_info,
                         boost::optional<std::vector<QueryResponseData::Question>>& questions,
                         boost::optional<std::vector<QueryResponseData::RR>>& answers,
                         boost::optional<std::vector<QueryResponseData::RR>>& authorities,
                         boost::optional<std::vector<QueryResponseData::RR>>& additionals);

    /**
     * \brief Decode a block cbor RR to our RR.
     *
     * \param index index of the block RR.
     * \param res   output RR vector.
     */
    void read_rr(block_cbor::index_t index, boost::optional<std::vector<QueryResponseData::RR>>& res);

    /**
     * \brief Synthesise Q/R flags from other fields.
     *
     * \param qri query response item.
     * \param sig query response signature.
     * \returns synthesised signature.
     */
    uint8_t synthesise_qr_flags(const block_cbor::QueryResponseItem& qri,
                                const block_cbor::QueryResponseSignature& sig);

    /**
     * \brief the decoder to read from.
     */
    CborBaseDecoder& dec_;

    /**
     * \brief the default values to use when reading.
     */
    const Defaults& defaults_;

    /**
     * \brief index of the next item to read from the current block.
     */
    unsigned next_item_;

    /**
     * \brief `true` if we need to read a new block.
     */
    bool need_block_;

    /**
     * \brief is the block size indefinite?
     */
    bool blocks_indef_;

    /**
     * \brief the number of blocks in the file, if definite.
     */
    uint64_t nblocks_;

    /**
     * \brief the file format version.
     */
    block_cbor::FileFormatVersion file_format_version_;

    /**
     * \brief the current block.
     */
    std::unique_ptr<block_cbor::BlockData> block_;

    /**
     * \brief the number of the current block
     */
    uint64_t current_block_num_;

    /**
     * \brief ID of the capturing program.
     */
    std::string generator_id_;

    /**
     * \brief ID of the capturing host.
     */
    std::string host_id_;

    /**
     * \brief Pointer to the field translation object.
     */
    std::unique_ptr<block_cbor::FileVersionFields> fields_;

    /**
     * \brief pseudo-anonymisation, if to use.
     */
    boost::optional<PseudoAnonymise> pseudo_anon_;

    /**
     * \brief accumulated address events from the file.
     */
    std::unordered_map<AddressEvent, unsigned, boost::hash<AddressEvent>> address_events_read_;

    /**
     * \brief vector of block parameters.
     */
    std::vector<block_cbor::BlockParameters> block_parameters_;

    /**
     * \brief earliest time of data in file.
     */
    boost::optional<std::chrono::system_clock::time_point> earliest_time_;

    /**
     * \brief latest time of data in file.
     */
    boost::optional<std::chrono::system_clock::time_point> latest_time_;

    /**
     * \brief end time of file.
     */
    boost::optional<std::chrono::system_clock::time_point> end_time_;

    /**
     * \brief start time of file.
     */
    boost::optional<std::chrono::system_clock::time_point> start_time_;
};

#endif

max_stars_repo_name: dns-stats/dns-stats-compactor


text:
/* misc.c - miscellaneous functions
 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
 *               2008, 2009, 2010 Free Software Foundation, Inc.
 * Copyright (C) 2014 Werner Koch
 *
 * This file is part of GnuPG.
 *
 * GnuPG is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * GnuPG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#include <config.h>

#include <botan/hex.h>
#include <boost/format.hpp>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
#include <asm/sysinfo.h>
#include <asm/unistd.h>
#endif
#ifdef HAVE_SETRLIMIT
#include <sys/resource.h>
#include <sys/time.h>
#include <time.h>
#endif
#ifdef ENABLE_SELINUX_HACKS
#include <sys/stat.h>
#endif

#ifdef HAVE_W32_SYSTEM
#include <process.h>
#include <time.h>
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#include <shlobj.h>
#include <windows.h>
#ifndef CSIDL_APPDATA
#define CSIDL_APPDATA 0x001a
#endif
#ifndef CSIDL_LOCAL_APPDATA
#define CSIDL_LOCAL_APPDATA 0x001c
#endif
#ifndef CSIDL_FLAG_CREATE
#define CSIDL_FLAG_CREATE 0x8000
#endif
#endif /*HAVE_W32_SYSTEM*/

#include "gpg.h"
#ifdef HAVE_W32_SYSTEM
#include "../common/status.h"
#endif /*HAVE_W32_SYSTEM*/
#include "../common/util.h"
#include "../common/zb32.h"
#include "call-agent.h"
#include "main.h"
#include "options.h"

#ifdef ENABLE_SELINUX_HACKS
/* A object and a global variable to keep track of files marked as
   secured. */
struct secured_file_item {
  struct secured_file_item *next;
  ino_t ino;
  dev_t dev;
};
static struct secured_file_item *secured_files;
#endif /*ENABLE_SELINUX_HACKS*/

/* For the sake of SELinux we want to restrict access through gpg to
   certain files we keep under our own control.  This function
   registers such a file and is_secured_file may then be used to
   check whether a file has ben registered as secured. */
void register_secured_file(const char *fname) {
#ifdef ENABLE_SELINUX_HACKS
  struct stat buf;
  struct secured_file_item *sf;

  /* Note that we stop immediately if something goes wrong here. */
  if (stat(fname, &buf))
    log_fatal(_("fstat of '%s' failed in %s: %s\n"), fname,
              "register_secured_file", strerror(errno));
  /*   log_debug ("registering '%s' i=%lu.%lu\n", fname, */
  /*              (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
  for (sf = secured_files; sf; sf = sf->next) {
    if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
      return; /* Already registered.  */
  }

  sf = xmalloc(sizeof *sf);
  sf->ino = buf.st_ino;
  sf->dev = buf.st_dev;
  sf->next = secured_files;
  secured_files = sf;
#else  /*!ENABLE_SELINUX_HACKS*/
  (void)fname;
#endif /*!ENABLE_SELINUX_HACKS*/
}

/* Remove a file registered as secure. */
void unregister_secured_file(const char *fname) {
#ifdef ENABLE_SELINUX_HACKS
  struct stat buf;
  struct secured_file_item *sf, *sfprev;

  if (stat(fname, &buf)) {
    log_error(_("fstat of '%s' failed in %s: %s\n"), fname,
              "unregister_secured_file", strerror(errno));
    return;
  }
  /*   log_debug ("unregistering '%s' i=%lu.%lu\n", fname,  */
  /*              (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
  for (sfprev = NULL, sf = secured_files; sf; sfprev = sf, sf = sf->next) {
    if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) {
      if (sfprev)
        sfprev->next = sf->next;
      else
        secured_files = sf->next;
      xfree(sf);
      return;
    }
  }
#else  /*!ENABLE_SELINUX_HACKS*/
  (void)fname;
#endif /*!ENABLE_SELINUX_HACKS*/
}

/* Return true if FD is corresponds to a secured file.  Using -1 for
   FS is allowed and will return false. */
int is_secured_file(int fd) {
#ifdef ENABLE_SELINUX_HACKS
  struct stat buf;
  struct secured_file_item *sf;

  if (fd == -1)
    return 0; /* No file descriptor so it can't be secured either.  */

  /* Note that we print out a error here and claim that a file is
     secure if something went wrong. */
  if (fstat(fd, &buf)) {
    log_error(_("fstat(%d) failed in %s: %s\n"), fd, "is_secured_file",
              strerror(errno));
    return 1;
  }
  /*   log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */
  /*              (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
  for (sf = secured_files; sf; sf = sf->next) {
    if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes.  */
  }
#else       /*!ENABLE_SELINUX_HACKS*/
  (void)fd;
#endif      /*!ENABLE_SELINUX_HACKS*/
  return 0; /* No. */
}

/* Return true if FNAME is corresponds to a secured file.  Using NULL,
   "" or "-" for FS is allowed and will return false. This function is
   used before creating a file, thus it won't fail if the file does
   not exist. */
int is_secured_filename(const char *fname) {
#ifdef ENABLE_SELINUX_HACKS
  struct stat buf;
  struct secured_file_item *sf;

  if (iobuf_is_pipe_filename(fname) || !*fname) return 0;

  /* Note that we print out a error here and claim that a file is
     secure if something went wrong. */
  if (stat(fname, &buf)) {
    if (errno == ENOENT || errno == EPERM || errno == EACCES) return 0;
    log_error(_("fstat of '%s' failed in %s: %s\n"), fname,
              "is_secured_filename", strerror(errno));
    return 1;
  }
  /*   log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */
  /*              (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
  for (sf = secured_files; sf; sf = sf->next) {
    if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes.  */
  }
#else       /*!ENABLE_SELINUX_HACKS*/
  (void)fname;
#endif      /*!ENABLE_SELINUX_HACKS*/
  return 0; /* No. */
}

u16 checksum_u16(unsigned n) {
  u16 a;

  a = (n >> 8) & 0xff;
  a += n & 0xff;
  return a;
}

u16 checksum(byte *p, unsigned n) {
  u16 a;

  for (a = 0; n; n--) a += *p++;
  return a;
}

u16 checksum_mpi(gcry_mpi_t a) {
  u16 csum;
  byte *buffer;
  size_t nbytes;
  int secure = gcry_is_secure(a);

  if (gcry_mpi_print(GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a)) BUG();
  /* Fixme: For numbers not in secure memory we should use a stack
   * based buffer and only allocate a larger one if mpi_print returns
   * an error. */
  if (secure)
    buffer = (byte *)Botan::allocate_memory(1, nbytes);
  else
    buffer = (byte *)gcry_xmalloc(nbytes);

  if (gcry_mpi_print(GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a)) BUG();
  csum = checksum(buffer, nbytes);
  if (secure)
    Botan::deallocate_memory(buffer, 1, nbytes);
  else
    xfree(buffer);
  return csum;
}

void print_pubkey_algo_note(pubkey_algo_t algo) {
  if (algo >= 100 && algo <= 110) {
    static int warn = 0;
    if (!warn) {
      warn = 1;
      es_fflush(es_stdout);
      log_info(_("WARNING: using experimental public key algorithm %s\n"),
               openpgp_pk_algo_name(algo));
    }
  } else if (algo == PUBKEY_ALGO_ELGAMAL) {
    es_fflush(es_stdout);
    log_info(_("WARNING: Elgamal sign+encrypt keys are deprecated\n"));
  }
}

void print_cipher_algo_note(cipher_algo_t algo) {
  if (algo >= 100 && algo <= 110) {
    static int warn = 0;
    if (!warn) {
      warn = 1;
      es_fflush(es_stdout);
      log_info(_("WARNING: using experimental cipher algorithm %s\n"),
               openpgp_cipher_algo_name(algo));
    }
  }
}

void print_digest_algo_note(digest_algo_t algo) {
  const enum gcry_md_algos galgo = map_md_openpgp_to_gcry(algo);

  if (algo >= 100 && algo <= 110) {
    if (!glo_ctrl.shown_experimental_digest_warning) {
      glo_ctrl.shown_experimental_digest_warning = true;
      es_fflush(es_stdout);
      log_info(_("WARNING: using experimental digest algorithm %s\n"),
               gcry_md_algo_name(galgo));
    }
  } else if (opt.weak_digests.count(galgo)) {
    es_fflush(es_stdout);
    log_info(_("WARNING: digest algorithm %s is deprecated\n"),
             gcry_md_algo_name(galgo));
  }
}

void print_digest_rejected_note(enum gcry_md_algos algo) {
  if (glo_ctrl.shown_rejection_notice.count(algo)) return;

  glo_ctrl.shown_rejection_notice.emplace(algo);
  es_fflush(es_stdout);
  log_info(_("Note: signatures using the %s algorithm are rejected\n"),
           gcry_md_algo_name(algo));
}

/* Print a message
 *  "(reported error: %s)\n
 * in verbose mode to further explain an error.  If the error code has
 * the value IGNORE_EC no message is printed.  A message is also not
 * printed if ERR is 0.  */
void print_reported_error(gpg_error_t err, gpg_error_t ignore_ec) {
  if (!opt.verbose) return;

  if (!err)
    ;
  else if (err == ignore_ec)
    ;
  else
    log_info(_("(reported error: %s)\n"), gpg_strerror(err));
}

/* Print a message
 *   "(further info: %s)\n
 * in verbose mode to further explain an error.  That message is
 * intended to help debug a problem and should not be translated.
 */
void print_further_info(const char *format, ...) {
  va_list arg_ptr;

  if (!opt.verbose) return;

  log_info(_("(further info: "));
  va_start(arg_ptr, format);
  log_logv(GPGRT_LOG_CONT, format, arg_ptr);
  va_end(arg_ptr);
  log_printf(")\n");
}

/* Map OpenPGP algo numbers to those used by Libgcrypt.  We need to do
   this for algorithms we implemented in Libgcrypt after they become
   part of OpenPGP.  */
enum gcry_cipher_algos map_cipher_openpgp_to_gcry(cipher_algo_t algo) {
  switch (algo) {
    case CIPHER_ALGO_NONE:
      return GCRY_CIPHER_NONE;

#ifdef GPG_USE_IDEA
    case CIPHER_ALGO_IDEA:
      return GCRY_CIPHER_IDEA;
#else
    case CIPHER_ALGO_IDEA:
      return (gcry_cipher_algos)0;
#endif

    case CIPHER_ALGO_3DES:
      return GCRY_CIPHER_3DES;

#ifdef GPG_USE_CAST5
    case CIPHER_ALGO_CAST5:
      return GCRY_CIPHER_CAST5;
#else
    case CIPHER_ALGO_CAST5:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_BLOWFISH
    case CIPHER_ALGO_BLOWFISH:
      return GCRY_CIPHER_BLOWFISH;
#else
    case CIPHER_ALGO_BLOWFISH:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_AES128
    case CIPHER_ALGO_AES:
      return GCRY_CIPHER_AES;
#else
    case CIPHER_ALGO_AES:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_AES192
    case CIPHER_ALGO_AES192:
      return GCRY_CIPHER_AES192;
#else
    case CIPHER_ALGO_AES192:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_AES256
    case CIPHER_ALGO_AES256:
      return GCRY_CIPHER_AES256;
#else
    case CIPHER_ALGO_AES256:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_TWOFISH
    case CIPHER_ALGO_TWOFISH:
      return GCRY_CIPHER_TWOFISH;
#else
    case CIPHER_ALGO_TWOFISH:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_CAMELLIA128
    case CIPHER_ALGO_CAMELLIA128:
      return GCRY_CIPHER_CAMELLIA128;
#else
    case CIPHER_ALGO_CAMELLIA128:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_CAMELLIA192
    case CIPHER_ALGO_CAMELLIA192:
      return GCRY_CIPHER_CAMELLIA192;
#else
    case CIPHER_ALGO_CAMELLIA192:
      return (gcry_cipher_algos)0;
#endif

#ifdef GPG_USE_CAMELLIA256
    case CIPHER_ALGO_CAMELLIA256:
      return GCRY_CIPHER_CAMELLIA256;
#else
    case CIPHER_ALGO_CAMELLIA256:
      return (gcry_cipher_algos)0;
#endif
    default:
      return (gcry_cipher_algos)0;
  }
}

/* The inverse function of above.  */
static cipher_algo_t map_cipher_gcry_to_openpgp(enum gcry_cipher_algos algo) {
  switch (algo) {
    case GCRY_CIPHER_NONE:
      return CIPHER_ALGO_NONE;
    case GCRY_CIPHER_IDEA:
      return CIPHER_ALGO_IDEA;
    case GCRY_CIPHER_3DES:
      return CIPHER_ALGO_3DES;
    case GCRY_CIPHER_CAST5:
      return CIPHER_ALGO_CAST5;
    case GCRY_CIPHER_BLOWFISH:
      return CIPHER_ALGO_BLOWFISH;
    case GCRY_CIPHER_AES:
      return CIPHER_ALGO_AES;
    case GCRY_CIPHER_AES192:
      return CIPHER_ALGO_AES192;
    case GCRY_CIPHER_AES256:
      return CIPHER_ALGO_AES256;
    case GCRY_CIPHER_TWOFISH:
      return CIPHER_ALGO_TWOFISH;
    case GCRY_CIPHER_CAMELLIA128:
      return CIPHER_ALGO_CAMELLIA128;
    case GCRY_CIPHER_CAMELLIA192:
      return CIPHER_ALGO_CAMELLIA192;
    case GCRY_CIPHER_CAMELLIA256:
      return CIPHER_ALGO_CAMELLIA256;
    default:
      return (cipher_algo_t)0;
  }
}

/* Map Gcrypt public key algorithm numbers to those used by OpenPGP.
   FIXME: This mapping is used at only two places - we should get rid
   of it.  */
pubkey_algo_t map_pk_gcry_to_openpgp(enum gcry_pk_algos algo) {
  switch (algo) {
    case GCRY_PK_ECDSA:
      return PUBKEY_ALGO_ECDSA;
    case GCRY_PK_ECDH:
      return PUBKEY_ALGO_ECDH;
    default:
      return (pubkey_algo_t)(algo < 110 ? algo : 0);
  }
}

/* Return the block length of an OpenPGP cipher algorithm.  */
int openpgp_cipher_blocklen(cipher_algo_t algo) {
  /* We use the numbers from OpenPGP to be sure that we get the right
     block length.  This is so that the packet parsing code works even
     for unknown algorithms (for which we assume 8 due to tradition).

     NOTE: If you change the returned blocklen above 16, check
     the callers because they may use a fixed size buffer of that
     size. */
  switch (algo) {
    case CIPHER_ALGO_AES:
    case CIPHER_ALGO_AES192:
    case CIPHER_ALGO_AES256:
    case CIPHER_ALGO_TWOFISH:
    case CIPHER_ALGO_CAMELLIA128:
    case CIPHER_ALGO_CAMELLIA192:
    case CIPHER_ALGO_CAMELLIA256:
      return 16;

    default:
      return 8;
  }
}

/****************
 * Wrapper around the libgcrypt function with additional checks on
 * the OpenPGP contraints for the algo ID.
 */
int openpgp_cipher_test_algo(cipher_algo_t algo) {
  enum gcry_cipher_algos ga;

  ga = map_cipher_openpgp_to_gcry(algo);
  if (!ga) return GPG_ERR_CIPHER_ALGO;

  return gcry_cipher_test_algo(ga);
}

/* Map the OpenPGP cipher algorithm whose ID is contained in ALGORITHM to a
   string representation of the algorithm name.  For unknown algorithm
   IDs this function returns "?".  */
const char *openpgp_cipher_algo_name(cipher_algo_t algo) {
  switch (algo) {
    case CIPHER_ALGO_IDEA:
      return "IDEA";
    case CIPHER_ALGO_3DES:
      return "3DES";
    case CIPHER_ALGO_CAST5:
      return "CAST5";
    case CIPHER_ALGO_BLOWFISH:
      return "BLOWFISH";
    case CIPHER_ALGO_AES:
      return "AES";
    case CIPHER_ALGO_AES192:
      return "AES192";
    case CIPHER_ALGO_AES256:
      return "AES256";
    case CIPHER_ALGO_TWOFISH:
      return "TWOFISH";
    case CIPHER_ALGO_CAMELLIA128:
      return "CAMELLIA128";
    case CIPHER_ALGO_CAMELLIA192:
      return "CAMELLIA192";
    case CIPHER_ALGO_CAMELLIA256:
      return "CAMELLIA256";
    case CIPHER_ALGO_NONE:
    default:
      return "?";
  }
}

/* Return 0 if ALGO is a supported OpenPGP public key algorithm.  */
int openpgp_pk_test_algo(pubkey_algo_t algo) {
  return openpgp_pk_test_algo2(algo, 0);
}

/* Return 0 if ALGO is a supported OpenPGP public key algorithm and
   allows the usage USE.  */
int openpgp_pk_test_algo2(pubkey_algo_t algo, unsigned int use) {
  enum gcry_pk_algos ga = (gcry_pk_algos)0;
  size_t use_buf = use;

  switch (algo) {
#ifdef GPG_USE_RSA
    case PUBKEY_ALGO_RSA:
      ga = GCRY_PK_RSA;
      break;
    case PUBKEY_ALGO_RSA_E:
      ga = GCRY_PK_RSA_E;
      break;
    case PUBKEY_ALGO_RSA_S:
      ga = GCRY_PK_RSA_S;
      break;
#else
    case PUBKEY_ALGO_RSA:
      break;
    case PUBKEY_ALGO_RSA_E:
      break;
    case PUBKEY_ALGO_RSA_S:
      break;
#endif

    case PUBKEY_ALGO_ELGAMAL_E:
      ga = GCRY_PK_ELG;
      break;
    case PUBKEY_ALGO_DSA:
      ga = GCRY_PK_DSA;
      break;

#ifdef GPG_USE_ECDH
    case PUBKEY_ALGO_ECDH:
      ga = GCRY_PK_ECC;
      break;
#else
    case PUBKEY_ALGO_ECDH:
      break;
#endif

#ifdef GPG_USE_ECDSA
    case PUBKEY_ALGO_ECDSA:
      ga = GCRY_PK_ECC;
      break;
#else
    case PUBKEY_ALGO_ECDSA:
      break;
#endif

#ifdef GPG_USE_EDDSA
    case PUBKEY_ALGO_EDDSA:
      ga = GCRY_PK_ECC;
      break;
#else
    case PUBKEY_ALGO_EDDSA:
      break;
#endif

    case PUBKEY_ALGO_ELGAMAL:
      /* Dont't allow type 20 keys unless in rfc2440 mode.  */
      break;

    default:
      break;
  }
  if (!ga) return GPG_ERR_PUBKEY_ALGO;

  /* Now check whether Libgcrypt has support for the algorithm.  */
  return gcry_pk_algo_info(ga, GCRYCTL_TEST_ALGO, NULL, &use_buf);
}

int openpgp_pk_algo_usage(int algo) {
  int use = 0;

  /* They are hardwired in gpg 1.0. */
  switch (algo) {
    case PUBKEY_ALGO_RSA:
      use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC |
             PUBKEY_USAGE_AUTH);
      break;
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_ECDH:
      use = PUBKEY_USAGE_ENC;
      break;
    case PUBKEY_ALGO_RSA_S:
      use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG;
      break;
    case PUBKEY_ALGO_ELGAMAL:
      break;
    case PUBKEY_ALGO_ELGAMAL_E:
      use = PUBKEY_USAGE_ENC;
      break;
    case PUBKEY_ALGO_DSA:
      use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
      break;
    case PUBKEY_ALGO_ECDSA:
    case PUBKEY_ALGO_EDDSA:
      use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
    default:
      break;
  }
  return use;
}

/* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a
   string representation of the algorithm name.  For unknown algorithm
   IDs this function returns "?".  */
const char *openpgp_pk_algo_name(pubkey_algo_t algo) {
  switch (algo) {
    case PUBKEY_ALGO_RSA:
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_RSA_S:
      return "RSA";
    case PUBKEY_ALGO_ELGAMAL:
    case PUBKEY_ALGO_ELGAMAL_E:
      return "ELG";
    case PUBKEY_ALGO_DSA:
      return "DSA";
    case PUBKEY_ALGO_ECDH:
      return "ECDH";
    case PUBKEY_ALGO_ECDSA:
      return "ECDSA";
    case PUBKEY_ALGO_EDDSA:
      return "EDDSA";
    default:
      return "?";
  }
}

/* Explicit mapping of OpenPGP digest algos to Libgcrypt.  */
/* FIXME: We do not yes use it everywhere.  */
enum gcry_md_algos map_md_openpgp_to_gcry(digest_algo_t algo) {
  switch (algo) {
#ifdef GPG_USE_MD5
    case DIGEST_ALGO_MD5:
      return GCRY_MD_MD5;
#else
    case DIGEST_ALGO_MD5:
      return 0;
#endif

    case DIGEST_ALGO_SHA1:
      return GCRY_MD_SHA1;

#ifdef GPG_USE_RMD160
    case DIGEST_ALGO_RMD160:
      return GCRY_MD_RMD160;
#else
    case DIGEST_ALGO_RMD160:
      return 0;
#endif

#ifdef GPG_USE_SHA224
    case DIGEST_ALGO_SHA224:
      return GCRY_MD_SHA224;
#else
    case DIGEST_ALGO_SHA224:
      return 0;
#endif

    case DIGEST_ALGO_SHA256:
      return GCRY_MD_SHA256;

#ifdef GPG_USE_SHA384
    case DIGEST_ALGO_SHA384:
      return GCRY_MD_SHA384;
#else
    case DIGEST_ALGO_SHA384:
      return 0;
#endif

#ifdef GPG_USE_SHA512
    case DIGEST_ALGO_SHA512:
      return GCRY_MD_SHA512;
#else
    case DIGEST_ALGO_SHA512:
      return 0;
#endif
    default:
      return (gcry_md_algos)0;
  }
}

/* Return 0 if ALGO is suitable and implemented OpenPGP hash
   algorithm.  */
int openpgp_md_test_algo(digest_algo_t algo) {
  enum gcry_md_algos ga;

  ga = map_md_openpgp_to_gcry(algo);
  if (!ga) return GPG_ERR_DIGEST_ALGO;

  return gcry_md_test_algo(ga);
}

/* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a
   string representation of the algorithm name.  For unknown algorithm
   IDs this function returns "?".  */
const char *openpgp_md_algo_name(int algo) {
  switch (algo) {
    case DIGEST_ALGO_MD5:
      return "MD5";
    case DIGEST_ALGO_SHA1:
      return "SHA1";
    case DIGEST_ALGO_RMD160:
      return "RIPEMD160";
    case DIGEST_ALGO_SHA256:
      return "SHA256";
    case DIGEST_ALGO_SHA384:
      return "SHA384";
    case DIGEST_ALGO_SHA512:
      return "SHA512";
    case DIGEST_ALGO_SHA224:
      return "SHA224";
  }
  return "?";
}

static unsigned long get_signature_count(PKT_public_key *pk) {
#ifdef ENABLE_CARD_SUPPORT
  struct agent_card_info_s info;

  (void)pk;
  if (!agent_scd_getattr("SIG-COUNTER", &info))
    return info.sig_counter;
  else
    return 0;
#else
  (void)pk;
  return 0;
#endif
}

/* Expand %-strings.  Returns a string which must be xfreed.  Returns
   NULL if the string cannot be expanded (too large). */
char *pct_expando(const char *string, struct expando_args *args) {
  const char *ch = string;
  int idx = 0, maxlen = 0, done = 0;
  u32 pk_keyid[2] = {0, 0}, sk_keyid[2] = {0, 0};
  char *ret = NULL;

  if (args->pk) keyid_from_pk(args->pk, pk_keyid);

  if (args->pksk) keyid_from_pk(args->pksk, sk_keyid);

  /* This is used so that %k works in photoid command strings in
     --list-secret-keys (which of course has a sk, but no pk). */
  if (!args->pk && args->pksk) keyid_from_pk(args->pksk, pk_keyid);

  while (*ch != '\0') {
    if (!done) {
      /* 8192 is way bigger than we'll need here */
      if (maxlen >= 8192) goto fail;

      maxlen += 1024;
      ret = (char *)xrealloc(ret, maxlen);
    }

    done = 0;

    if (*ch == '%') {
      switch (*(ch + 1)) {
        case 's': /* short key id */
          if (idx + 8 < maxlen) {
            sprintf(&ret[idx], "%08lX", (unsigned long)sk_keyid[1]);
            idx += 8;
            done = 1;
          }
          break;

        case 'S': /* long key id */
          if (idx + 16 < maxlen) {
            sprintf(&ret[idx], "%08lX%08lX", (unsigned long)sk_keyid[0],
                    (unsigned long)sk_keyid[1]);
            idx += 16;
            done = 1;
          }
          break;

        case 'k': /* short key id */
          if (idx + 8 < maxlen) {
            sprintf(&ret[idx], "%08lX", (unsigned long)pk_keyid[1]);
            idx += 8;
            done = 1;
          }
          break;

        case 'K': /* long key id */
          if (idx + 16 < maxlen) {
            sprintf(&ret[idx], "%08lX%08lX", (unsigned long)pk_keyid[0],
                    (unsigned long)pk_keyid[1]);
            idx += 16;
            done = 1;
          }
          break;

        case 'U': /* z-base-32 encoded user id hash. */
          if (args->namehash) {
            char *tmp = zb32_encode(args->namehash, 8 * 20);
            if (tmp) {
              if (idx + strlen(tmp) < maxlen) {
                strcpy(ret + idx, tmp);
                idx += strlen(tmp);
              }
              xfree(tmp);
              done = 1;
            }
          }
          break;

        case 'c': /* signature count from card, if any. */
          if (idx + 10 < maxlen) {
            sprintf(&ret[idx], "%lu", get_signature_count(args->pksk));
            idx += strlen(&ret[idx]);
            done = 1;
          }
          break;

        case 'f': /* Fingerprint of key being signed */
        case 'p': /* Fingerprint of the primary key making the signature. */
        case 'g': /* Fingerprint of the key making the signature.  */
        {
          byte array[MAX_FINGERPRINT_LEN];
          size_t len;
          int i;

          if ((*(ch + 1)) == 'f' && args->pk)
            fingerprint_from_pk(args->pk, array, &len);
          else if ((*(ch + 1)) == 'p' && args->pksk) {
            if (args->pksk->flags.primary)
              fingerprint_from_pk(args->pksk, array, &len);
            else if (args->pksk->main_keyid[0] || args->pksk->main_keyid[1]) {
              /* Not the primary key: Find the fingerprint
                 of the primary key.  */
              PKT_public_key *pk =
                  (PKT_public_key *)xmalloc_clear(sizeof(PKT_public_key));

              if (!get_pubkey_fast(pk, args->pksk->main_keyid))
                fingerprint_from_pk(pk, array, &len);
              else
                memset(array, 0, (len = MAX_FINGERPRINT_LEN));
              free_public_key(pk);
            } else /* Oops: info about the primary key missing.  */
              memset(array, 0, (len = MAX_FINGERPRINT_LEN));
          } else if ((*(ch + 1)) == 'g' && args->pksk)
            fingerprint_from_pk(args->pksk, array, &len);
          else
            memset(array, 0, (len = MAX_FINGERPRINT_LEN));

          if (idx + (len * 2) < maxlen) {
            for (i = 0; i < len; i++) {
              sprintf(&ret[idx], "%02X", array[i]);
              idx += 2;
            }
            done = 1;
          }
        } break;

        case 'v': /* validity letters */
          if (args->validity_info && idx + 1 < maxlen) {
            ret[idx++] = args->validity_info;
            ret[idx] = '\0';
            done = 1;
          }
          break;

        /* The text string types e.g. "full", "expired", etc. */
        case 'V': {
          const char *str = NULL;

          str = args->validity_string;
          if (str && idx + strlen(str) < maxlen) {
            strcpy(&ret[idx], str);
            idx += strlen(str);
            done = 1;
          }
        } break;

        case '%':
          if (idx + 1 < maxlen) {
            ret[idx++] = '%';
            ret[idx] = '\0';
            done = 1;
          }
          break;

        /* Any unknown %-keys (like %i, %o, %I, and %O) are
           passed through for later expansion.  Note this also
           handles the case where the last character in the
           string is a '%' - the terminating \0 will end up here
           and properly terminate the string. */
        default:
          if (idx + 2 < maxlen) {
            ret[idx++] = '%';
            ret[idx++] = *(ch + 1);
            ret[idx] = '\0';
            done = 1;
          }
          break;
      }

      if (done) ch++;
    } else {
      if (idx + 1 < maxlen) {
        ret[idx++] = *ch;
        ret[idx] = '\0';
        done = 1;
      }
    }

    if (done) ch++;
  }

  return ret;

fail:
  xfree(ret);
  return NULL;
}

void deprecated_warning(const char *configname, unsigned int configlineno,
                        const char *option, const char *repl1,
                        const char *repl2) {
  if (configname) {
    if (strncmp("--", option, 2) == 0) option += 2;

    if (strncmp("--", repl1, 2) == 0) repl1 += 2;

    log_info(_("%s:%d: deprecated option \"%s\"\n"), configname, configlineno,
             option);
  } else
    log_info(_("WARNING: \"%s\" is a deprecated option\n"), option);

  log_info(_("please use \"%s%s\" instead\n"), repl1, repl2);
}

void deprecated_command(const char *name) {
  log_info(_("WARNING: \"%s\" is a deprecated command - do not use it\n"),
           name);
}

void obsolete_scdaemon_option(const char *configname, unsigned int configlineno,
                              const char *name) {
  if (configname)
    log_info(_("%s:%u: \"%s\" is obsolete in this file"
               " - it only has effect in %s\n"),
             configname, configlineno, name, SCDAEMON_NAME EXTSEP_S "conf");
  else
    log_info(_("WARNING: \"%s%s\" is an obsolete option"
               " - it has no effect except on %s\n"),
             "--", name, SCDAEMON_NAME);
}

/*
 * Wrapper around gcry_cipher_map_name to provide a fallback using the
 * "Sn" syntax as used by the preference strings.
 */
int string_to_cipher_algo(const char *string) {
  int val;

  val = map_cipher_gcry_to_openpgp(
      (gcry_cipher_algos)(gcry_cipher_map_name(string)));
  if (!val && string && (string[0] == 'S' || string[0] == 's')) {
    char *endptr;

    string++;
    val = strtol(string, &endptr, 10);
    if (!*string || *endptr || openpgp_cipher_test_algo((cipher_algo_t)(val)))
      val = 0;
  }

  return val;
}

/*
 * Wrapper around gcry_md_map_name to provide a fallback using the
 * "Hn" syntax as used by the preference strings.
 */
int string_to_digest_algo(const char *string) {
  int val;

  /* FIXME: We should make use of our wrapper function and not assume
     that there is a 1 to 1 mapping between OpenPGP and Libgcrypt.  */
  val = gcry_md_map_name(string);
  if (!val && string && (string[0] == 'H' || string[0] == 'h')) {
    char *endptr;

    string++;
    val = strtol(string, &endptr, 10);
    if (!*string || *endptr || openpgp_md_test_algo((digest_algo_t)(val)))
      val = 0;
  }

  return val;
}

const char *compress_algo_to_string(int algo) {
  const char *s = NULL;

  switch (algo) {
    case COMPRESS_ALGO_NONE:
      s = _("Uncompressed");
      break;

    case COMPRESS_ALGO_ZIP:
      s = "ZIP";
      break;

    case COMPRESS_ALGO_ZLIB:
      s = "ZLIB";
      break;

    case COMPRESS_ALGO_BZIP2:
      s = "BZIP2";
      break;
  }

  return s;
}

int string_to_compress_algo(const char *string) {
  /* TRANSLATORS: See doc/TRANSLATE about this string. */
  if (match_multistr(_("uncompressed|none"), string))
    return 0;
  else if (ascii_strcasecmp(string, "uncompressed") == 0)
    return 0;
  else if (ascii_strcasecmp(string, "none") == 0)
    return 0;
  else if (ascii_strcasecmp(string, "zip") == 0)
    return 1;
  else if (ascii_strcasecmp(string, "zlib") == 0)
    return 2;
  else if (ascii_strcasecmp(string, "bzip2") == 0)
    return 3;
  else if (ascii_strcasecmp(string, "z0") == 0)
    return 0;
  else if (ascii_strcasecmp(string, "z1") == 0)
    return 1;
  else if (ascii_strcasecmp(string, "z2") == 0)
    return 2;
  else if (ascii_strcasecmp(string, "z3") == 0)
    return 3;
  else
    return -1;
}

int check_compress_algo(int algo) {
  switch (algo) {
    case 0:
      return 0;
#ifdef BOTAN_HAS_ZLIB
    case 1:
    case 2:
      return 0;
#endif
#ifdef BOTAN_HAS_BZIP2
    case 3:
      return 0;
#endif
    default:
      return GPG_ERR_COMPR_ALGO;
  }
}

int default_cipher_algo(void) {
  if (opt.def_cipher_algo)
    return opt.def_cipher_algo;
  else if (!opt.personal_cipher_prefs.empty())
    return opt.personal_cipher_prefs[0].value;
  else
    return opt.s2k_cipher_algo;
}

/* There is no default_digest_algo function, but see
   sign.c:hash_for() */

int default_compress_algo(void) {
  if (opt.compress_algo != -1)
    return opt.compress_algo;
  else if (!opt.personal_compress_prefs.empty())
    return opt.personal_compress_prefs[0].value;
  else
    return DEFAULT_COMPRESS_ALGO;
}

void compliance_failure(void) {
  const char *ver = "???";

  switch (opt.compliance) {
    case CO_GNUPG:
      ver = "GnuPG";
      break;

    case CO_RFC4880:
      ver = "OpenPGP";
      break;

    case CO_PGP6:
      ver = "PGP 6.x";
      break;

    case CO_PGP7:
      ver = "PGP 7.x";
      break;

    case CO_PGP8:
      ver = "PGP 8.x";
      break;

    case CO_DE_VS:
      ver = "DE-VS applications";
      break;
  }

  log_info(_("this message may not be usable by %s\n"), ver);
  opt.compliance = CO_GNUPG;
}

/* Break a string into successive option pieces.  Accepts single word
   options and key=value argument options. */
char *optsep(char **stringp) {
  char *tok, *end;

  tok = *stringp;
  if (tok) {
    end = strpbrk(tok, " ,=");
    if (end) {
      int sawequals = 0;
      char *ptr = end;

      /* what we need to do now is scan along starting with *end,
         If the next character we see (ignoring spaces) is an =
         sign, then there is an argument. */

      while (*ptr) {
        if (*ptr == '=')
          sawequals = 1;
        else if (*ptr != ' ')
          break;
        ptr++;
      }

      /* There is an argument, so grab that too.  At this point,
         ptr points to the first character of the argument. */
      if (sawequals) {
        /* Is it a quoted argument? */
        if (*ptr == '"') {
          ptr++;
          end = strchr(ptr, '"');
          if (end) end++;
        } else
          end = strpbrk(ptr, " ,");
      }

      if (end && *end) {
        *end = '\0';
        *stringp = end + 1;
      } else
        *stringp = NULL;
    } else
      *stringp = NULL;
  }

  return tok;
}

/* Breaks an option value into key and value.  Returns NULL if there
   is no value.  Note that "string" is modified to remove the =value
   part. */
char *argsplit(char *string) {
  char *equals, *arg = NULL;

  equals = strchr(string, '=');
  if (equals) {
    char *quote, *space;

    *equals = '\0';
    arg = equals + 1;

    /* Quoted arg? */
    quote = strchr(arg, '"');
    if (quote) {
      arg = quote + 1;

      quote = strchr(arg, '"');
      if (quote) *quote = '\0';
    } else {
      size_t spaces;

      /* Trim leading spaces off of the arg */
      spaces = strspn(arg, " ");
      arg += spaces;
    }

    /* Trim tailing spaces off of the tag */
    space = strchr(string, ' ');
    if (space) *space = '\0';
  }

  return arg;
}

/* Return the length of the initial token, leaving off any
   argument. */
static size_t optlen(const char *s) {
  char *end = (char *)strpbrk(s, " =");

  if (end)
    return end - s;
  else
    return strlen(s);
}

int parse_options(char *str, unsigned int *options, struct parse_options *opts,
                  int noisy) {
  char *tok;

  if (str && !strcmp(str, "help")) {
    int i, maxlen = 0;

    /* Figure out the longest option name so we can line these up
       neatly. */
    for (i = 0; opts[i].name; i++)
      if (opts[i].help && maxlen < strlen(opts[i].name))
        maxlen = strlen(opts[i].name);

    for (i = 0; opts[i].name; i++)
      if (opts[i].help)
        es_printf("%s%*s%s\n", opts[i].name,
                  maxlen + 2 - (int)strlen(opts[i].name), "", _(opts[i].help));

    g10_exit(0);
  }

  while ((tok = optsep(&str))) {
    int i, rev = 0;
    char *otok = tok;

    if (tok[0] == '\0') continue;

    if (ascii_strncasecmp("no-", tok, 3) == 0) {
      rev = 1;
      tok += 3;
    }

    for (i = 0; opts[i].name; i++) {
      size_t toklen = optlen(tok);

      if (ascii_strncasecmp(opts[i].name, tok, toklen) == 0) {
        /* We have a match, but it might be incomplete */
        if (toklen != strlen(opts[i].name)) {
          int j;

          for (j = i + 1; opts[j].name; j++) {
            if (ascii_strncasecmp(opts[j].name, tok, toklen) == 0) {
              if (noisy) log_info(_("ambiguous option '%s'\n"), otok);
              return 0;
            }
          }
        }

        if (rev) {
          *options &= ~opts[i].bit;
          if (opts[i].value) *opts[i].value = NULL;
        } else {
          *options |= opts[i].bit;
          if (opts[i].value) *opts[i].value = argsplit(tok);
        }
        break;
      }
    }

    if (!opts[i].name) {
      if (noisy) log_info(_("unknown option '%s'\n"), otok);
      return 0;
    }
  }

  return 1;
}

/* Similar to access(2), but uses PATH to find the file. */
int path_access(const char *file, int mode) {
  char *envpath;
  int ret = -1;

  envpath = getenv("PATH");

  if (!envpath
#ifdef HAVE_DRIVE_LETTERS
      || (((file[0] >= 'A' && file[0] <= 'Z') ||
           (file[0] >= 'a' && file[0] <= 'z')) &&
          file[1] == ':')
#else
      || file[0] == '/'
#endif
  )
    return access(file, mode);
  else {
    /* At least as large as, but most often larger than we need. */
    char *buffer = (char *)xmalloc(strlen(envpath) + 1 + strlen(file) + 1);
    char *split, *item, *path = xstrdup(envpath);

    split = path;

    while ((item = gpg_strsep(&split, PATHSEP_S))) {
      strcpy(buffer, item);
      strcat(buffer, "/");
      strcat(buffer, file);
      ret = access(buffer, mode);
      if (ret == 0) break;
    }

    xfree(path);
    xfree(buffer);
  }

  return ret;
}

/* Return the number of public key parameters as used by OpenPGP.  */
int pubkey_get_npkey(pubkey_algo_t algo) {
  switch (algo) {
    case PUBKEY_ALGO_RSA:
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_RSA_S:
      return 2;
    case PUBKEY_ALGO_ELGAMAL_E:
      return 3;
    case PUBKEY_ALGO_DSA:
      return 4;
    case PUBKEY_ALGO_ECDH:
      return 3;
    case PUBKEY_ALGO_ECDSA:
      return 2;
    case PUBKEY_ALGO_ELGAMAL:
      return 3;
    case PUBKEY_ALGO_EDDSA:
      return 2;
    default:
      return 0;
  }
}

/* Return the number of secret key parameters as used by OpenPGP.  */
int pubkey_get_nskey(pubkey_algo_t algo) {
  switch (algo) {
    case PUBKEY_ALGO_RSA:
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_RSA_S:
      return 6;
    case PUBKEY_ALGO_ELGAMAL_E:
      return 4;
    case PUBKEY_ALGO_DSA:
      return 5;
    case PUBKEY_ALGO_ECDH:
      return 4;
    case PUBKEY_ALGO_ECDSA:
      return 3;
    case PUBKEY_ALGO_ELGAMAL:
      return 4;
    case PUBKEY_ALGO_EDDSA:
      return 3;
    default:
      return 0;
  }
}

/* Temporary helper. */
int pubkey_get_nsig(pubkey_algo_t algo) {
  switch (algo) {
    case PUBKEY_ALGO_RSA:
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_RSA_S:
      return 1;
    case PUBKEY_ALGO_ELGAMAL_E:
      return 0;
    case PUBKEY_ALGO_DSA:
      return 2;
    case PUBKEY_ALGO_ECDH:
      return 0;
    case PUBKEY_ALGO_ECDSA:
      return 2;
    case PUBKEY_ALGO_ELGAMAL:
      return 2;
    case PUBKEY_ALGO_EDDSA:
      return 2;
    default:
      return 0;
  }
}

/* Temporary helper. */
int pubkey_get_nenc(pubkey_algo_t algo) {
  switch (algo) {
    case PUBKEY_ALGO_RSA:
    case PUBKEY_ALGO_RSA_E:
    case PUBKEY_ALGO_RSA_S:
      return 1;
    case PUBKEY_ALGO_ELGAMAL_E:
      return 2;
    case PUBKEY_ALGO_DSA:
      return 0;
    case PUBKEY_ALGO_ECDH:
      return 2;
    case PUBKEY_ALGO_ECDSA:
      return 0;
    case PUBKEY_ALGO_ELGAMAL:
      return 2;
    case PUBKEY_ALGO_EDDSA:
      return 0;
    default:
      return 0;
  }
}

/* Temporary helper. */
unsigned int pubkey_nbits(int algo, gcry_mpi_t *key) {
  int rc, nbits;
  gcry_sexp_t sexp;

  if (algo == PUBKEY_ALGO_DSA && key[0] && key[1] && key[2] && key[3]) {
    rc = gcry_sexp_build(&sexp, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
                         key[0], key[1], key[2], key[3]);
  } else if ((algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) &&
             key[0] && key[1] && key[2]) {
    rc = gcry_sexp_build(&sexp, NULL, "(public-key(elg(p%m)(g%m)(y%m)))",
                         key[0], key[1], key[2]);
  } else if (is_RSA(algo) && key[0] && key[1]) {
    rc = gcry_sexp_build(&sexp, NULL, "(public-key(rsa(n%m)(e%m)))", key[0],
                         key[1]);
  } else if ((algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ||
              algo == PUBKEY_ALGO_EDDSA) &&
             key[0] && key[1]) {
    std::string curve = openpgp_oid_to_str(key[0]);
    if (curve.length() == 0)
      rc = gpg_error_from_syserror();
    else {
      rc = gcry_sexp_build(&sexp, NULL, "(public-key(ecc(curve%s)(q%m)))",
                           curve.c_str(), key[1]);
    }
  } else
    return 0;

  if (rc) BUG();

  nbits = gcry_pk_get_nbits(sexp);
  gcry_sexp_release(sexp);
  return nbits;
}

int mpi_print(estream_t fp, gcry_mpi_t a, int mode) {
  int n = 0;
  size_t nwritten;

  if (!a) return es_fprintf(fp, "[MPI_NULL]");
  if (!mode) {
    unsigned int n1;
    n1 = gcry_mpi_get_nbits(a);
    n += es_fprintf(fp, "[%u bits]", n1);
  } else if (gcry_mpi_get_flag(a, GCRYMPI_FLAG_OPAQUE)) {
    unsigned int nbits;
    unsigned char *p = (unsigned char *)gcry_mpi_get_opaque(a, &nbits);
    if (!p)
      n += es_fprintf(fp, "[invalid opaque value]");
    else {
      std::string str = Botan::hex_encode(p, (nbits + 7) / 8);
      if (!es_fputs(str.c_str(), fp)) n += str.size();
    }
  } else {
    unsigned char *buffer;
    size_t buflen;

    if (gcry_mpi_aprint(GCRYMPI_FMT_USG, &buffer, &buflen, a)) BUG();
    std::string str = Botan::hex_encode(buffer, buflen);
    if (!es_fputs(str.c_str(), fp)) n += str.size();
    gcry_free(buffer);
  }
  return n;
}

void mpi_print_stream(std::ostream &fp, gcry_mpi_t a, int mode) {
  int n = 0;
  size_t nwritten;

  if (!a) {
    fp << "[MPI_NULL]";
    return;
  }
  if (!mode) {
    unsigned int n1;
    n1 = gcry_mpi_get_nbits(a);
    fp << boost::format("[%u bits]") % n1;
  } else if (gcry_mpi_get_flag(a, GCRYMPI_FLAG_OPAQUE)) {
    unsigned int nbits;
    unsigned char *p = (unsigned char *)gcry_mpi_get_opaque(a, &nbits);
    if (!p)
      fp << "[invalid opaque value]";
    else {
      fp << Botan::hex_encode(p, (nbits + 7) / 8);
    }
  } else {
    unsigned char *buffer;
    size_t buflen;

    if (gcry_mpi_aprint(GCRYMPI_FMT_USG, &buffer, &buflen, a)) BUG();
    fp << Botan::hex_encode(buffer, buflen);
    gcry_free(buffer);
  }
}

/* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point,
   i.e.  04 <x> <y> */
unsigned int ecdsa_qbits_from_Q(unsigned int qbits) {
  if ((qbits % 8) > 3) {
    log_error(
        _("ECDSA public key is expected to be in SEC encoding "
          "multiple of 8 bits\n"));
    return 0;
  }
  qbits -= qbits % 8;
  qbits /= 2;
  return qbits;
}

/* Ignore signatures and certifications made over certain digest
 * algorithms by default, MD5 is considered weak.  This allows users
 * to deprecate support for other algorithms as well.
 */
void additional_weak_digest(const std::string &digestname) {
  const enum gcry_md_algos algo =
      (gcry_md_algos)string_to_digest_algo(digestname.c_str());

  if (algo == GCRY_MD_NONE) {
    log_error(_("unknown weak digest '%s'\n"), digestname.c_str());
    return;
  }

  opt.weak_digests.emplace(algo);
}

max_stars_repo_name: mehrdad-shokri/neopg

max_issues_repo_name: mehrdad-shokri/neopg

max_forks_repo_name: mehrdad-shokri/neopg