{"action":"created","comment":{"url":"https://api.github.com/repos/randombit/botan/pulls/comments/62692900","id":62692900,"diff_hunk":"@@ -0,0 +1,385 @@\n+/*\n+* ECIES\n+* (C) 2016 Philipp Weber\n+*\n+* Botan is released under the Simplified BSD License (see license.txt)\n+*/\n+\n+#include <botan/ecies.h>\n+#include <botan/cipher_mode.h>\n+#include <botan/pipe.h>\n+\n+#include <botan/internal/ct_utils.h>\n+#include <botan/internal/pk_utils.h>\n+\n+namespace Botan {\n+\n+namespace {\n+\n+/**\n+* Private key type for ECIES_ECDH_KA_Operation\n+*/\n+class ECIES_PrivateKey : public EC_PrivateKey, public PK_Key_Agreement_Key\n+   {\n+   public:\n+      explicit ECIES_PrivateKey(const ECDH_PrivateKey& private_key) :\n+         EC_PublicKey(private_key),\n+         EC_PrivateKey(private_key),\n+         PK_Key_Agreement_Key(),\n+         m_key(private_key)\n+         {\n+         }\n+\n+      std::vector<byte> public_value() const override\n+         {\n+         return m_key.public_value();\n+         }\n+\n+      std::string algo_name() const override\n+         {\n+         return \"ECIES\";\n+         }\n+\n+      size_t max_input_bits() const override\n+         {\n+         return m_key.max_input_bits();\n+         }\n+\n+   private:\n+      ECDH_PrivateKey m_key;\n+   };\n+\n+/**\n+* Implements ECDH key agreement without using the cofactor mode\n+*/\n+class ECIES_ECDH_KA_Operation : public PK_Ops::Key_Agreement_with_KDF\n+   {\n+   public:\n+      typedef ECIES_PrivateKey Key_Type;\n+\n+      ECIES_ECDH_KA_Operation(const ECIES_PrivateKey& private_key, const std::string&) :\n+         PK_Ops::Key_Agreement_with_KDF(\"Raw\"),\n+         m_key(private_key)\n+         {\n+         }\n+\n+      secure_vector<byte> raw_agree(const byte w[], size_t w_len) override\n+         {\n+         const CurveGFp& curve = m_key.domain().get_curve();\n+         PointGFp point = OS2ECP(w, w_len, curve);\n+         PointGFp S = point * m_key.private_value();\n+         BOTAN_ASSERT(S.on_the_curve(), \"ECDH agreed value was on the curve\");\n+         return BigInt::encode_1363(S.get_affine_x(), curve.get_p().bytes());\n+         }\n+\n+   private:\n+      ECIES_PrivateKey m_key;\n+   };\n+\n+/**\n+* Creates a PK_Key_Agreement instance for the given key and ecies_params\n+* Returns either ECIES_ECDH_KA_Operation or the default implementation for the given key,\n+* depending on the key and ecies_params\n+* @param private_key the private key used for the key agreement\n+* @param ecies_params settings for ecies\n+* @param for_encryption disable cofactor mode if the secret will be used for encryption\n+* (according to ISO 18033 cofactor mode is only used during decryption)\n+*/\n+PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key, const ECIES_KA_Params& ecies_params,\n+                                      bool for_encryption)\n+   {\n+   const ECDH_PrivateKey* ecdh_key = dynamic_cast<const ECDH_PrivateKey*>(&private_key);\n+\n+   if(ecdh_key == nullptr && (ecies_params.cofactor_mode() || ecies_params.old_cofactor_mode()\n+                              || ecies_params.check_mode()))\n+      {\n+      // assume we have a private key from an external provider (e.g. pkcs#11):\n+      // there is no way to determine or control whether the provider uses cofactor mode or not.\n+      // ISO 18033 does not allow cofactor mode in combination with old cofactor mode or check mode\n+      // => disable cofactor mode, old cofactor mode and check mode for unknown keys/providers (as a precaution).\n+      throw Invalid_Argument(\"ECIES: cofactor, old cofactor and check mode are only supported for ECDH_PrivateKey\");\n+      }\n+\n+   if(ecdh_key && (for_encryption || !ecies_params.cofactor_mode()))\n+      {\n+      // ECDH_KA_Operation uses cofactor mode: use own key agreement method if cofactor should not be used.\n+      return PK_Key_Agreement(ECIES_PrivateKey(*ecdh_key), \"Raw\");\n+      }\n+\n+   return PK_Key_Agreement(private_key, \"Raw\");\t\t// use default implementation\n+   }\n+}\n+\n+BOTAN_REGISTER_PK_KEY_AGREE_OP(\"ECIES\", ECIES_ECDH_KA_Operation);\n+\n+ECIES_KA_Operation::ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key, const ECIES_KA_Params& ecies_params,\n+                                       bool for_encryption) :\n+   m_ka(create_key_agreement(private_key, ecies_params, for_encryption)),\n+   m_params(ecies_params)\n+   {\n+   }\n+\n+/**\n+* ECIES secret derivation according to ISO 18033-2\n+*/\n+SymmetricKey ECIES_KA_Operation::derive_secret(const std::vector<byte>& eph_public_key_bin,\n+      const PointGFp& other_public_key_point) const\n+   {\n+   if(other_public_key_point.is_zero())\n+      {\n+      throw Invalid_Argument(\"ECIES: other public key point is zero\");\n+      }\n+\n+   std::unique_ptr<KDF> kdf = m_params.create_kdf();\n+   BOTAN_ASSERT(kdf != nullptr, \"KDF is found\");\n+\n+   PointGFp other_point = other_public_key_point;\n+\n+   // ISO 18033: step b\n+   if(m_params.old_cofactor_mode())\n+      {\n+      other_point *= m_params.domain().get_cofactor();\n+      }\n+\n+   secure_vector<byte> derivation_input;\n+\n+   // ISO 18033: encryption step e / decryption step g\n+   if(!m_params.single_hash_mode())\n+      {\n+      derivation_input += eph_public_key_bin;\n+      }\n+\n+   // ISO 18033: encryption step f / decryption step h\n+   secure_vector<byte> other_public_key_bin = EC2OSP(other_point, static_cast<byte>(m_params.compression_type()));\n+    // Note: the argument `m_params.secret_length()` passed for `key_len` will only be used by providers because\n+   // \"Raw\" is passed to the `PK_Key_Agreement` if the implementation of botan is used.\n+   const SymmetricKey peh = m_ka.derive_key(m_params.domain().get_order().bytes(), other_public_key_bin.data(), other_public_key_bin.size());\n+   derivation_input.insert(derivation_input.end(), peh.begin(), peh.end());\n+\n+   // ISO 18033: encryption step g / decryption step i\n+   return kdf->derive_key(m_params.secret_length(), derivation_input);\n+   }\n+\n+\n+ECIES_KA_Params::ECIES_KA_Params(const EC_Group& domain, const std::string& kdf_spec, size_t length,\n+                                 PointGFp::Compression_Type compression_type, ECIES_Flags flags) :\n+   m_domain(domain),\n+   m_kdf_spec(kdf_spec),\n+   m_length(length),\n+   m_compression_mode(compression_type),\n+   m_flags(flags)\n+   {\n+   }\n+\n+std::unique_ptr<KDF> ECIES_KA_Params::create_kdf() const\n+   {\n+   std::unique_ptr<KDF> kdf = Botan::KDF::create(m_kdf_spec);\n+   if(kdf == nullptr)\n+      {\n+      throw Algorithm_Not_Found(m_kdf_spec);\n+      }\n+   return kdf;\n+   }\n+\n+\n+ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,\n+                                         const std::string& dem_algo_spec, size_t dem_key_len,\n+                                         const std::string& mac_spec, size_t mac_key_len,\n+                                         PointGFp::Compression_Type compression_type, ECIES_Flags flags) :\n+   ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags),\n+   m_dem_spec(dem_algo_spec),\n+   m_dem_keylen(dem_key_len),\n+   m_mac_spec(mac_spec),\n+   m_mac_keylen(mac_key_len)\n+   {\n+   // ISO 18033: \"At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1.\"\n+   if(cofactor_mode() + old_cofactor_mode() + check_mode() > 1)\n+      {\n+      throw Invalid_Argument(\"ECIES: only one of cofactor_mode, old_cofactor_mode and check_mode can be set\");\n+      }\n+   }\n+\n+ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,\n+                                         const std::string& dem_algo_spec, size_t dem_key_len,\n+                                         const std::string& mac_spec, size_t mac_key_len) :\n+   ECIES_System_Params(domain, kdf_spec, dem_algo_spec, dem_key_len, mac_spec, mac_key_len, PointGFp::UNCOMPRESSED,\n+                         ECIES_Flags::NONE)\n+   {\n+   }\n+\n+std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const\n+   {\n+   std::unique_ptr<MessageAuthenticationCode> mac = Botan::MessageAuthenticationCode::create(m_mac_spec);\n+   if(mac == nullptr)\n+      {\n+      throw Algorithm_Not_Found(m_mac_spec);\n+      }\n+   return mac;\n+   }\n+\n+std::unique_ptr<Keyed_Filter> ECIES_System_Params::create_cipher(Botan::Cipher_Dir direction) const\n+   {\n+   Keyed_Filter* cipher = get_cipher(m_dem_spec, direction);\n+   if(cipher == nullptr)\n+      {\n+      throw Algorithm_Not_Found(m_dem_spec);\n+      }\n+   return std::unique_ptr<Keyed_Filter>(cipher);\n+   }\n+\n+\n+/*\n+* ECIES_Encryptor Constructor\n+*/\n+ECIES_Encryptor::ECIES_Encryptor(const PK_Key_Agreement_Key& private_key, const ECIES_System_Params& ecies_params) :\n+   m_ka(private_key, ecies_params, true),\n+   m_params(ecies_params),\n+   m_eph_public_key_bin(private_key.public_value()),\t// returns the uncompressed public key, see conversion below\n+   m_iv(),\n+   m_other_point(),\n+   m_label()\n+   {\n+   if(ecies_params.compression_type() != PointGFp::UNCOMPRESSED)\n+      {\n+      // ISO 18033: step d \n+      // convert only if necessary; m_eph_public_key_bin has been initialized with the uncompressed format\n+      m_eph_public_key_bin = unlock(EC2OSP(OS2ECP(m_eph_public_key_bin, m_params.domain().get_curve()),\n+                                           static_cast<byte>(ecies_params.compression_type())));\n+      }\n+   }\n+\n+/*\n+* ECIES_Encryptor Constructor\n+*/\n+ECIES_Encryptor::ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_Params& ecies_params) :\n+   ECIES_Encryptor(ECDH_PrivateKey(rng, ecies_params.domain()), ecies_params)\n+   {\n+   }\n+\n+\n+/*\n+* ECIES Encryption according to ISO 18033-2\n+*/\n+std::vector<byte> ECIES_Encryptor::enc(const byte data[], size_t length, RandomNumberGenerator&) const\n+   {\n+   if(m_other_point.is_zero())\n+      {\n+      throw Invalid_State(\"ECIES: the other key is zero\");\n+      }\n+\n+   const SymmetricKey secret_key = m_ka.derive_secret(m_eph_public_key_bin, m_other_point);\n+\n+   // encryption\n+   std::unique_ptr<Keyed_Filter> cipher = m_params.create_cipher(ENCRYPTION);\n+   BOTAN_ASSERT(cipher != nullptr, \"Cipher is found\");\n+\n+   cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));\n+   if(m_iv.size() != 0)\n+      {\n+      cipher->set_iv(m_iv);\n+      }\n+   Pipe pipe(cipher.release());\n+   pipe.process_msg(data, length);\n+   const secure_vector<byte> encrypted_data = pipe.read_all(0);\n+\n+   // concat elements\n+   std::unique_ptr<MessageAuthenticationCode> mac = m_params.create_mac();\n+   BOTAN_ASSERT(mac != nullptr, \"MAC is found\");\n+\n+   secure_vector<byte> out(m_eph_public_key_bin.size() + encrypted_data.size() + mac->output_length());\n+   buffer_insert(out, 0, m_eph_public_key_bin);\n+   buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data);\n+\n+   // mac\n+   mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());\n+   mac->update(encrypted_data);\n+   if(!m_label.empty())\n+      {\n+      mac->update(m_label);\n+      }\n+   mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size());\n+\n+   return unlock(out);\n+   }\n+\n+\n+ECIES_Decryptor::ECIES_Decryptor(const PK_Key_Agreement_Key& key, const ECIES_System_Params& ecies_params) :\n+   m_ka(key, ecies_params, false),\n+   m_params(ecies_params),\n+   m_iv(),\n+   m_label()\n+   {\n+   // ISO 18033: \"If v > 1 and CheckMode = 0, then we must have gcd(�, v) = 1.\" (v = index, � = order)\n+   if(!ecies_params.check_mode())\n+      {\n+      Botan::BigInt cofactor = m_params.domain().get_cofactor();\n+      if(cofactor > 1 && Botan::gcd(cofactor, m_params.domain().get_order()) != 1)\n+         {\n+         throw Invalid_Argument(\"ECIES: gcd of cofactor and order must be 1 if check_mode is 0\");\n+         }\n+      }\n+   }\n+\n+/**\n+* ECIES Decryption according to ISO 18033-2\n+*/\n+secure_vector<byte> ECIES_Decryptor::do_decrypt(byte& valid_mask, const byte in[], size_t in_len) const\n+   {\n+   size_t point_size = m_params.domain().get_curve().get_p().bytes();\n+   if(m_params.compression_type() != PointGFp::COMPRESSED)\n+      {\n+      point_size *= 2;\t\t// uncompressed and hybrid contains x AND y\n+      }\n+   point_size += 1;\t\t\t// format byte\n+\n+   std::unique_ptr<MessageAuthenticationCode> mac = m_params.create_mac();\n+   BOTAN_ASSERT(mac != nullptr, \"MAC is found\");\n+\n+   if(in_len < point_size + mac->output_length())\n+      {\n+      throw Decoding_Error(\"ECIES decryption: ciphertext is too short\");\n+      }\n+\n+   // extract data\n+   const std::vector<byte> other_public_key_bin(in, in + point_size);\t// the received (ephemeral) public key\n+   const std::vector<byte> encrypted_data(in + point_size, in + in_len - mac->output_length());\n+   const std::vector<byte> mac_data(in + in_len - mac->output_length(), in + in_len);\n+\n+   // ISO 18033: step a\n+   PointGFp other_public_key = OS2ECP(other_public_key_bin, m_params.domain().get_curve());\n+\n+   // ISO 18033: step b\n+   if(m_params.check_mode() && !other_public_key.on_the_curve())\n+      {\n+      throw Decoding_Error(\"ECIES decryption: received public key is not on the curve\");\n+      }\n+\n+   // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree) \n+   // throws Illegal_Transformation if the point is zero)\n+   const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);\n+\n+   // validate mac\n+   mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());\n+   mac->update(encrypted_data);\n+   if(!m_label.empty())\n+      {\n+      mac->update(m_label);\n+      }\n+   const secure_vector<byte> calculated_mac = mac->final();\n+   valid_mask = CT::expand_mask<byte>(same_mem(mac_data.data(), calculated_mac.data(), mac_data.size()));\n+\n+   // decrypt data\n+   std::unique_ptr<Keyed_Filter> cipher = m_params.create_cipher(DECRYPTION);\n+   BOTAN_ASSERT(cipher != nullptr, \"Cipher is found\");\n+\n+   cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));\n+   if(m_iv.size() != 0)\n+      {\n+      cipher->set_iv(m_iv);\n+      }\n+   Pipe pipe(cipher.release());\n+   pipe.process_msg(encrypted_data);\n+   return pipe.read_all(0);","path":"src/lib/pubkey/ecies/ecies.cpp","position":382,"original_position":382,"commit_id":"2a58a43264c7994c19a1f05d807e40ffd95644c2","original_commit_id":"2a58a43264c7994c19a1f05d807e40ffd95644c2","user":{"login":"neusdan","id":5289290,"avatar_url":"https://avatars.githubusercontent.com/u/5289290?v=3","gravatar_id":"","url":"https://api.github.com/users/neusdan","html_url":"https://github.com/neusdan","followers_url":"https://api.github.com/users/neusdan/followers","following_url":"https://api.github.com/users/neusdan/following{/other_user}","gists_url":"https://api.github.com/users/neusdan/gists{/gist_id}","starred_url":"https://api.github.com/users/neusdan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/neusdan/subscriptions","organizations_url":"https://api.github.com/users/neusdan/orgs","repos_url":"https://api.github.com/users/neusdan/repos","events_url":"https://api.github.com/users/neusdan/events{/privacy}","received_events_url":"https://api.github.com/users/neusdan/received_events","type":"User","site_admin":false},"body":"But how should the attacker calculate a valid MAC without knowing the secret key / be able to derive the secret key?","created_at":"2016-05-10T15:23:14Z","updated_at":"2016-05-10T15:23:14Z","html_url":"https://github.com/randombit/botan/pull/483#discussion_r62692900","pull_request_url":"https://api.github.com/repos/randombit/botan/pulls/483","_links":{"self":{"href":"https://api.github.com/repos/randombit/botan/pulls/comments/62692900"},"html":{"href":"https://github.com/randombit/botan/pull/483#discussion_r62692900"},"pull_request":{"href":"https://api.github.com/repos/randombit/botan/pulls/483"}}},"pull_request":{"url":"https://api.github.com/repos/randombit/botan/pulls/483","id":68059247,"html_url":"https://github.com/randombit/botan/pull/483","diff_url":"https://github.com/randombit/botan/pull/483.diff","patch_url":"https://github.com/randombit/botan/pull/483.patch","issue_url":"https://api.github.com/repos/randombit/botan/issues/483","number":483,"state":"open","locked":false,"title":"Ecies","user":{"login":"weberph","id":246259,"avatar_url":"https://avatars.githubusercontent.com/u/246259?v=3","gravatar_id":"","url":"https://api.github.com/users/weberph","html_url":"https://github.com/weberph","followers_url":"https://api.github.com/users/weberph/followers","following_url":"https://api.github.com/users/weberph/following{/other_user}","gists_url":"https://api.github.com/users/weberph/gists{/gist_id}","starred_url":"https://api.github.com/users/weberph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weberph/subscriptions","organizations_url":"https://api.github.com/users/weberph/orgs","repos_url":"https://api.github.com/users/weberph/repos","events_url":"https://api.github.com/users/weberph/events{/privacy}","received_events_url":"https://api.github.com/users/weberph/received_events","type":"User","site_admin":false},"body":"This adds ECIES according to ISO-18033-2 (2006) (this standard is recommended by the german BSI (Federal Office for Information Security))\r\n- compatible with bouncy castle\r\n- ISO 18033 test vectors passed\r\n- test data created with bouncy castle\r\n- added kdf1 according to ISO 18033\r\n\r\n### Details\r\nThe support for ECIES has been implemented according to ISO 18033-2 (2006) [ISO180332] which is the standard recommended by TR-02102-1.\r\nThe ECIES algorithm outlined by TR-02102-1 is a combination of separate algorithms and mechanisms described by ISO 18033-2 and consists of a data encryption mechanism (DEM), a key encryption mechanism (KEM) and the conjunction of those two (HC – “hybrid cipher” [ISO180332, P. 25]).\r\nECIES has been implemented using DEM1 (data encryption method) [ISO180332, P. 26] and ECIES-KEM (key encryption method) [ISO180332, P. 32].\r\n\r\n### Implementation\r\nThe most important new classes in Botan to support ECIES are `ECIES_KA_Operation`, `ECIES_Encryptor` and `ECIES_Decryptor`.\r\n`ECIES_KA_Operation` implements ECIES-KEM which contains the key agreement using ECDH and the secret derivation using the provided KDF.\r\n`ECIES_Encryptor` and `ECIES_Decryptor` provide the implementation for DEM1 and HC, including the use of `ECIES_KA_Operation` for the secret generation, the encryption/decryption, the calculation/verification of the MAC and the composition/interpretation of the final message.\r\nTo supply the required arguments and system parameters to these operations, two classes have been added: `ECIES_KA_Params` and `ECIES_System_Params`.\r\n`ECIES_KA_Params` contains the system parameters required for the secret derivation (using `ECIES_KA_Operation`) and `ECIES_System_Params` contains the system parameters required for the `ECIES_Encryptor` / `ECIES_Decryptor` classes.\r\nBecause the ECDH key agreement operation of Botan uses cofactor mode, a new key type (`ECIES_PrivateKey`) and a key agreement operation (`ECIES_ECDH_KA_Operation`) for that key type have been added internally (not exposed via header files).\r\nThis operation is used whenever the cofactor mode is disabled by the system parameters.\r\n\r\n### Restriction\r\nAccording to ISO 18033 \"At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1\".\r\nThis is a problem when dealing with custom providers (e.g. PKCS#11) because the implementation of the ECDH key agreement is unknown and the interfaces/architecture of Botan does not allow to determine or control whether cofactor mode should be used or not.\r\nFor this reason cofactor mode, old cofactor mode and check mode are prohibited for private keys / `PK_Key_Agreement_Key` that are not derived from `ECDH_PrivateKey`.\r\n\r\n### Tests\r\nTo test the ECIES implementation two sets of test vectors have been added.\r\nOne set contains the test vectors from ISO 18033 for ECIES-KEM [ISO180332, P. 76].\r\nThese test vectors cover only the secret derivation of ECIES but not the content of the final message after encryption and calculation of the message authentication code.\r\nBecause the test vectors from ISO 18033 use a key derivation function that was not supported by botan, \"KDF1_18033\" has been added to test against these vectors.\r\nThe second set of test vectors covers the complete ECIES mechanism including the final message.\r\nThose test vectors have been created with the Java library Bouncy Castle [1].\r\n\r\n### Compatibility\r\nThe ECIES implementation has been successfully tested against the implementation of Bouncy Castle.\r\n\r\n\r\n[1] Note: ISO 18033 states \"If SingleHashMode = 1, then let Z be the null octet string; otherwise, let Z = C0.\" [ISO180332, P. 33], but Bouncy Castle sets Z = C0 if `SingleHashMode` is true.","created_at":"2016-04-27T14:07:26Z","updated_at":"2016-05-10T15:23:14Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b4538e5b7e457548ed24598a6248517e1943a70a","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/randombit/botan/pulls/483/commits","review_comments_url":"https://api.github.com/repos/randombit/botan/pulls/483/comments","review_comment_url":"https://api.github.com/repos/randombit/botan/pulls/comments{/number}","comments_url":"https://api.github.com/repos/randombit/botan/issues/483/comments","statuses_url":"https://api.github.com/repos/randombit/botan/statuses/2a58a43264c7994c19a1f05d807e40ffd95644c2","head":{"label":"weberph:ecies","ref":"ecies","sha":"2a58a43264c7994c19a1f05d807e40ffd95644c2","user":{"login":"weberph","id":246259,"avatar_url":"https://avatars.githubusercontent.com/u/246259?v=3","gravatar_id":"","url":"https://api.github.com/users/weberph","html_url":"https://github.com/weberph","followers_url":"https://api.github.com/users/weberph/followers","following_url":"https://api.github.com/users/weberph/following{/other_user}","gists_url":"https://api.github.com/users/weberph/gists{/gist_id}","starred_url":"https://api.github.com/users/weberph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weberph/subscriptions","organizations_url":"https://api.github.com/users/weberph/orgs","repos_url":"https://api.github.com/users/weberph/repos","events_url":"https://api.github.com/users/weberph/events{/privacy}","received_events_url":"https://api.github.com/users/weberph/received_events","type":"User","site_admin":false},"repo":{"id":57212556,"name":"botan","full_name":"weberph/botan","owner":{"login":"weberph","id":246259,"avatar_url":"https://avatars.githubusercontent.com/u/246259?v=3","gravatar_id":"","url":"https://api.github.com/users/weberph","html_url":"https://github.com/weberph","followers_url":"https://api.github.com/users/weberph/followers","following_url":"https://api.github.com/users/weberph/following{/other_user}","gists_url":"https://api.github.com/users/weberph/gists{/gist_id}","starred_url":"https://api.github.com/users/weberph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weberph/subscriptions","organizations_url":"https://api.github.com/users/weberph/orgs","repos_url":"https://api.github.com/users/weberph/repos","events_url":"https://api.github.com/users/weberph/events{/privacy}","received_events_url":"https://api.github.com/users/weberph/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/weberph/botan","description":"Crypto and TLS for C++11","fork":true,"url":"https://api.github.com/repos/weberph/botan","forks_url":"https://api.github.com/repos/weberph/botan/forks","keys_url":"https://api.github.com/repos/weberph/botan/keys{/key_id}","collaborators_url":"https://api.github.com/repos/weberph/botan/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/weberph/botan/teams","hooks_url":"https://api.github.com/repos/weberph/botan/hooks","issue_events_url":"https://api.github.com/repos/weberph/botan/issues/events{/number}","events_url":"https://api.github.com/repos/weberph/botan/events","assignees_url":"https://api.github.com/repos/weberph/botan/assignees{/user}","branches_url":"https://api.github.com/repos/weberph/botan/branches{/branch}","tags_url":"https://api.github.com/repos/weberph/botan/tags","blobs_url":"https://api.github.com/repos/weberph/botan/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/weberph/botan/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/weberph/botan/git/refs{/sha}","trees_url":"https://api.github.com/repos/weberph/botan/git/trees{/sha}","statuses_url":"https://api.github.com/repos/weberph/botan/statuses/{sha}","languages_url":"https://api.github.com/repos/weberph/botan/languages","stargazers_url":"https://api.github.com/repos/weberph/botan/stargazers","contributors_url":"https://api.github.com/repos/weberph/botan/contributors","subscribers_url":"https://api.github.com/repos/weberph/botan/subscribers","subscription_url":"https://api.github.com/repos/weberph/botan/subscription","commits_url":"https://api.github.com/repos/weberph/botan/commits{/sha}","git_commits_url":"https://api.github.com/repos/weberph/botan/git/commits{/sha}","comments_url":"https://api.github.com/repos/weberph/botan/comments{/number}","issue_comment_url":"https://api.github.com/repos/weberph/botan/issues/comments{/number}","contents_url":"https://api.github.com/repos/weberph/botan/contents/{+path}","compare_url":"https://api.github.com/repos/weberph/botan/compare/{base}...{head}","merges_url":"https://api.github.com/repos/weberph/botan/merges","archive_url":"https://api.github.com/repos/weberph/botan/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/weberph/botan/downloads","issues_url":"https://api.github.com/repos/weberph/botan/issues{/number}","pulls_url":"https://api.github.com/repos/weberph/botan/pulls{/number}","milestones_url":"https://api.github.com/repos/weberph/botan/milestones{/number}","notifications_url":"https://api.github.com/repos/weberph/botan/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/weberph/botan/labels{/name}","releases_url":"https://api.github.com/repos/weberph/botan/releases{/id}","deployments_url":"https://api.github.com/repos/weberph/botan/deployments","created_at":"2016-04-27T12:43:07Z","updated_at":"2016-04-27T12:43:11Z","pushed_at":"2016-04-27T13:50:54Z","git_url":"git://github.com/weberph/botan.git","ssh_url":"git@github.com:weberph/botan.git","clone_url":"https://github.com/weberph/botan.git","svn_url":"https://github.com/weberph/botan","homepage":"http://botan.randombit.net","size":50983,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"randombit:master","ref":"master","sha":"4ad555977b03cb92dfac0b87a00febe4d8e7ff5e","user":{"login":"randombit","id":469092,"avatar_url":"https://avatars.githubusercontent.com/u/469092?v=3","gravatar_id":"","url":"https://api.github.com/users/randombit","html_url":"https://github.com/randombit","followers_url":"https://api.github.com/users/randombit/followers","following_url":"https://api.github.com/users/randombit/following{/other_user}","gists_url":"https://api.github.com/users/randombit/gists{/gist_id}","starred_url":"https://api.github.com/users/randombit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/randombit/subscriptions","organizations_url":"https://api.github.com/users/randombit/orgs","repos_url":"https://api.github.com/users/randombit/repos","events_url":"https://api.github.com/users/randombit/events{/privacy}","received_events_url":"https://api.github.com/users/randombit/received_events","type":"User","site_admin":false},"repo":{"id":8608254,"name":"botan","full_name":"randombit/botan","owner":{"login":"randombit","id":469092,"avatar_url":"https://avatars.githubusercontent.com/u/469092?v=3","gravatar_id":"","url":"https://api.github.com/users/randombit","html_url":"https://github.com/randombit","followers_url":"https://api.github.com/users/randombit/followers","following_url":"https://api.github.com/users/randombit/following{/other_user}","gists_url":"https://api.github.com/users/randombit/gists{/gist_id}","starred_url":"https://api.github.com/users/randombit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/randombit/subscriptions","organizations_url":"https://api.github.com/users/randombit/orgs","repos_url":"https://api.github.com/users/randombit/repos","events_url":"https://api.github.com/users/randombit/events{/privacy}","received_events_url":"https://api.github.com/users/randombit/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/randombit/botan","description":"Crypto and TLS for C++11","fork":false,"url":"https://api.github.com/repos/randombit/botan","forks_url":"https://api.github.com/repos/randombit/botan/forks","keys_url":"https://api.github.com/repos/randombit/botan/keys{/key_id}","collaborators_url":"https://api.github.com/repos/randombit/botan/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/randombit/botan/teams","hooks_url":"https://api.github.com/repos/randombit/botan/hooks","issue_events_url":"https://api.github.com/repos/randombit/botan/issues/events{/number}","events_url":"https://api.github.com/repos/randombit/botan/events","assignees_url":"https://api.github.com/repos/randombit/botan/assignees{/user}","branches_url":"https://api.github.com/repos/randombit/botan/branches{/branch}","tags_url":"https://api.github.com/repos/randombit/botan/tags","blobs_url":"https://api.github.com/repos/randombit/botan/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/randombit/botan/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/randombit/botan/git/refs{/sha}","trees_url":"https://api.github.com/repos/randombit/botan/git/trees{/sha}","statuses_url":"https://api.github.com/repos/randombit/botan/statuses/{sha}","languages_url":"https://api.github.com/repos/randombit/botan/languages","stargazers_url":"https://api.github.com/repos/randombit/botan/stargazers","contributors_url":"https://api.github.com/repos/randombit/botan/contributors","subscribers_url":"https://api.github.com/repos/randombit/botan/subscribers","subscription_url":"https://api.github.com/repos/randombit/botan/subscription","commits_url":"https://api.github.com/repos/randombit/botan/commits{/sha}","git_commits_url":"https://api.github.com/repos/randombit/botan/git/commits{/sha}","comments_url":"https://api.github.com/repos/randombit/botan/comments{/number}","issue_comment_url":"https://api.github.com/repos/randombit/botan/issues/comments{/number}","contents_url":"https://api.github.com/repos/randombit/botan/contents/{+path}","compare_url":"https://api.github.com/repos/randombit/botan/compare/{base}...{head}","merges_url":"https://api.github.com/repos/randombit/botan/merges","archive_url":"https://api.github.com/repos/randombit/botan/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/randombit/botan/downloads","issues_url":"https://api.github.com/repos/randombit/botan/issues{/number}","pulls_url":"https://api.github.com/repos/randombit/botan/pulls{/number}","milestones_url":"https://api.github.com/repos/randombit/botan/milestones{/number}","notifications_url":"https://api.github.com/repos/randombit/botan/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/randombit/botan/labels{/name}","releases_url":"https://api.github.com/repos/randombit/botan/releases{/id}","deployments_url":"https://api.github.com/repos/randombit/botan/deployments","created_at":"2013-03-06T16:51:39Z","updated_at":"2016-05-09T04:02:38Z","pushed_at":"2016-05-10T13:31:56Z","git_url":"git://github.com/randombit/botan.git","ssh_url":"git@github.com:randombit/botan.git","clone_url":"https://github.com/randombit/botan.git","svn_url":"https://github.com/randombit/botan","homepage":"http://botan.randombit.net","size":62666,"stargazers_count":269,"watchers_count":269,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":81,"mirror_url":null,"open_issues_count":60,"forks":81,"open_issues":60,"watchers":269,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/randombit/botan/pulls/483"},"html":{"href":"https://github.com/randombit/botan/pull/483"},"issue":{"href":"https://api.github.com/repos/randombit/botan/issues/483"},"comments":{"href":"https://api.github.com/repos/randombit/botan/issues/483/comments"},"review_comments":{"href":"https://api.github.com/repos/randombit/botan/pulls/483/comments"},"review_comment":{"href":"https://api.github.com/repos/randombit/botan/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/randombit/botan/pulls/483/commits"},"statuses":{"href":"https://api.github.com/repos/randombit/botan/statuses/2a58a43264c7994c19a1f05d807e40ffd95644c2"}}}}