2
\$\begingroup\$

Refactored part of the original code:

c++ Mongo Interface

Implementation of ScramSha-256 for Mongo server to be used with the Authenticate class.

Example of Usage:

  Authenticate        authenticate;

  authenticate.addAuthenticator(
           "SCRAM-SHA-256",
           ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate
  );

  authenticate.authenticte(mongoConnection, "user", "password", "db", "MyApp", true);

AuthenticateScramSha.h

  #ifndef THORSANVIL_DB_MONGO_AUTHENTICATE_SCRAM_SHA_H
  #define THORSANVIL_DB_MONGO_AUTHENTICATE_SCRAM_SHA_H

  // https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst

  #include "ThorsMongoConfig.h"
  #include "MongoUtil.h"

  #include "ThorSerialize/Traits.h"
  #include "ThorSerialize/MongoUtility.h"

  #include <string>
  #include <cstdint>
  #include <cstddef>

  #if !defined(__WINNT__)
  #include <sys/utsname.h>
  #endif

  namespace ThorsAnvil::DB::Mongo::Auth::ScramSha
  {

  class Binary
  {
      private:
          int                 type;
          std::string         data;

      public:
          Binary(int type, std::string const& init)
              : type(type)
              , data(std::begin(init), std::end(init))
          {}
          Binary(int type)
              : type(type)
          {}
          std::string const&  getData() const             {return data;}
          int                 getType() const             {return type;}
          std::size_t         getSize() const             {return data.size();}
          void                resize(std::size_t size)    {data.resize(size);}
          char const*         getBuffer() const           {return &data[0];}
          char*               getBuffer()                 {return &data[0];}
  };

  class BinarySerializer: public ThorsAnvil::Serialize::MongoUtility::BinarySerializer<Binary, '\x00'>
  {
      public:
          virtual void writeJson(Serialize::JsonPrinter& printer, Binary const& object) const override
          {
              printer.addValue(object.getData());
          }
  };


  struct AuthInit
  {
      AuthInit(std::string const& db, std::string const& mechanism, std::string&& payload);

      std::int32_t        saslStart;
      std::string         mechanism;
      std::string         $db;
      Binary              payload;
  };

  struct AuthCont
  {
      AuthCont(std::int32_t convId, std::string const& db, std::string&& payload);

      std::int32_t        saslContinue;
      std::int32_t        conversationId;
      std::string         $db;
      Binary              payload;
  };

  struct AuthReply: public CmdReplyBase
  {
      AuthReply();

      std::int32_t        conversationId;     // Seen
      bool                done;               // Seen
      Binary              payload;            // Seen
  };

  void authenticate(std::iostream& stream, std::string const& username, std::string const& password, std::string const& database);

  }

  ThorsAnvil_MakeTraitCustomSerialize(ThorsAnvil::DB::Mongo::Auth::ScramSha::Binary, ThorsAnvil::DB::Mongo::Auth::ScramSha::BinarySerializer);

  ThorsAnvil_MakeTrait(ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthInit,           saslStart, mechanism, payload, $db);
  ThorsAnvil_MakeTrait(ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthCont,           saslContinue, payload, conversationId, $db);
  ThorsAnvil_ExpandTrait(ThorsAnvil::DB::Mongo::CmdReplyBase,
                       ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthReply,          conversationId, done, payload);

  #endif

AuthenticateScramSha.cpp

  #include "AuthenticateScramSha.h"
  #include "Op_Msg.h"
  #include "ThorsCrypto/scram.h"

  using namespace ThorsAnvil::DB::Mongo::Auth::ScramSha;
  namespace TC = ThorsAnvil::Crypto;

  AuthInit::AuthInit(std::string const& db, std::string const& mechanism, std::string&& payload)
      : saslStart(1)
      , mechanism(mechanism)
      , $db(db)
      , payload(0, std::move(payload))
  {}

  AuthCont::AuthCont(std::int32_t convId, std::string const& db, std::string&& payload)
      : saslContinue(1)
      , conversationId(convId)
      , $db(db)
      , payload(0, std::move(payload))
  {}

  AuthReply::AuthReply()
      : payload(0)
  {}

  namespace ThorsAnvil::DB::Mongo::Auth::ScramSha
  {
  void authenticate(std::iostream& stream, std::string const& username, std::string const& password, std::string const& database)
  {
      // Start Authorization
      TC::ScramClientSha256   client(username);
      MessageId               authInitId;
      AuthReply               authInitReply;
      bool                    authInitOK = false;
      if (stream << Op_Msg(AuthInit{database,  "SCRAM-SHA-256", client.getFirstMessage()}, authInitId))
      {
          if (stream >> Op_Msg(authInitReply, authInitId)) {
              authInitOK = true;
          }
      }

      if (!authInitOK)
      {
          ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
                                   "authenticate",
                                   "AuthInit request or response failed");
      }
      if (authInitReply.ok != 1)
      {
          stream.setstate(std::ios::failbit);
          ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
                                   "authenticate",
                                   "Handshake FirstMessage: ",
                                   "Code: ", authInitReply.code,
                                   "Name: ", authInitReply.codeName,
                                   "Msg:  ", authInitReply.errmsg);
      }

      MessageId               authContId;
      AuthReply               authContReply;
      bool                    authContOK = false;
      if (stream << Op_Msg(AuthCont{authInitReply.conversationId, database, client.getProofMessage(password, authInitReply.payload.getData())}, authContId))
      {
          if (stream >> Op_Msg(authContReply, authContId)) {
              authContOK = true;
          }
      }

      if (!authContOK)
      {
          ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
                                   "authenticate",
                                   "AuthCont request or response failed");
      }
      if (authContReply.ok != 1)
      {
          ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
                                   "authenticate",
                                   "Handshake Proof: ",
                                   "Code: ", authContReply.code,
                                   "Name: ", authContReply.codeName,
                                   "Msg:  ", authContReply.errmsg);
      }

      // Send Auth Cont 2: Send the DB Info
      MessageId               authContId2;
      AuthReply               authContReply2;
      bool                    authContOK2 = false;
      if (stream << Op_Msg(AuthCont{authContReply.conversationId, database, ""}, authContId2))
      {
          if (stream >> Op_Msg(authContReply2, authContId2)) {
              authContOK2 = true;
          }
      }

      if (!authContOK2)
      {
          ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
                                   "authenticate",
                                   "AuthCont2 request or response failed");
      }
      if (authContReply2.ok != 1)
      {
          ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
                                  "authenticate",
                                   "Handshake DB Connect: ",
                                   "Code: ", authContReply2.code,
                                   "Name: ", authContReply2.codeName,
                                   "Msg:  ", authContReply2.errmsg);
      }
      if (!authContReply2.done)
      {
          ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
                                  "authenticate",
                                   "Handshake DB Connect: ", "Expected handshake to be complete");
      }
  }

  }

test/AuthenticateScramShaTest.cpp

  #include "gtest/gtest.h"

  #include "AuthenticateScramSha.h"
  #include "Op_Msg.h"

  using ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthInit;
  using ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthCont;
  using ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthReply;
  using ThorsAnvil::DB::Mongo::MessageId;
  using ThorsAnvil::DB::Mongo::Internal::getMessageId;

  TEST(AuthenticateScramShaTest, AuthInit)
  {
      AuthInit        authInit("db", "mechanism", "DLDLLDDLDLDLDLDLDL");

      EXPECT_EQ(1, authInit.saslStart);
      EXPECT_EQ("mechanism", authInit.mechanism);
      EXPECT_EQ("db", authInit.$db);
      EXPECT_EQ(0, authInit.payload.getType());
      EXPECT_EQ("DLDLLDDLDLDLDLDLDL", authInit.payload.getData());
  }

  TEST(AuthenticateScramShaTest, AuthCont)
  {
      AuthCont          authCont(21, "db", "GHHFHHFHFHFFHHF");

      EXPECT_EQ(1, authCont.saslContinue);
      EXPECT_EQ(21, authCont.conversationId);
      EXPECT_EQ("db", authCont.$db);
      EXPECT_EQ(0, authCont.payload.getType());
      EXPECT_EQ("GHHFHHFHFHFFHHF", authCont.payload.getData());
  }

  TEST(AuthenticateScramShaTest, AuthReply)
  {
      AuthReply         authReply;

      EXPECT_EQ(0, authReply.payload.getType());
  }

  TEST(AuthenticateScramShaTest, AuthInitFailBadReply)
  {
      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                        // Remove CodeName to cause Error
                        //   "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);

      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_THROW(
          action(),
          std::runtime_error
      );
  }

  TEST(AuthenticateScramShaTest, AuthInitFail)
  {
      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                      // Set the OK to false.
                      // This will force an exception to be thrown.
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\x00\x00"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);


      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_THROW(
          action(),
          std::runtime_error
      );
  }

  TEST(AuthenticateScramShaTest, AuthContFailBadmessage)
  {
      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"
                             // Response 2 to AuthCont
                             "\xa3\x00\x00\x00"           // Size
                             "\x04\x00\x00\x00"           // Id
                             "\x01\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flags
                             "\x00"                       // Kind 0
                             "\x8e\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                      // Remove "payload" section to cause
                      // parsing to fail
                      //     "\x05"   "payload\x00"               "\x2e\x00\x00\x00"  "\x00"  // Binary
                                  "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38"  // 1.0
                                  "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c"  // 2.0
                                  "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);

      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_THROW(
          action(),
          std::runtime_error
      );
  }
  TEST(AuthenticateScramShaTest, AuthContFail)
  {
      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 2 to AuthCont
                             "\xa3\x00\x00\x00"           // Size
                             "\x04\x00\x00\x00"           // Id
                             "\x01\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flags
                             "\x00"                       // Kind 0
                             "\x8e\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x2e\x00\x00\x00"  "\x00"  // Binary
                                  "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38"  // 1.0
                                  "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c"  // 2.0
                                  "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
                  // Set OK to false.
                  // This will force an exception.
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\x00\x00"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);

      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_THROW(
          action(),
          std::runtime_error
      );
  }

  TEST(AuthenticateScramShaTest, AuthCont2FailBadMessage)
  {
      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 2 to AuthCont
                             "\xa3\x00\x00\x00"           // Size
                             "\x04\x00\x00\x00"           // Id
                             "\x01\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flags
                             "\x00"                       // Kind 0
                             "\x8e\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x2e\x00\x00\x00"  "\x00"  // Binary
                                  "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38"  // 1.0
                                  "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c"  // 2.0
                                  "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 3 to AuthCont2
                             "\x75\x00\x00\x00"           // Size
                             "\x06\x00\x00\x00"           // Id
                             "\x02\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                      // Remove the BSON SIZE
                      // This will cause parsing to fail
                      //     "\x60\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x01"
                             "\x05"   "payload\x00"               "\x00\x00\x00\x00"      "\x00"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"      "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"      "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);


      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_THROW(
          action(),
          std::runtime_error
      );
  }

  TEST(AuthenticateScramShaTest, AuthCont2Fail)
  {
      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 2 to AuthCont
                             "\xa3\x00\x00\x00"           // Size
                             "\x04\x00\x00\x00"           // Id
                             "\x01\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flags
                             "\x00"                       // Kind 0
                             "\x8e\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x2e\x00\x00\x00"  "\x00"  // Binary
                                  "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38"  // 1.0
                                  "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c"  // 2.0
                                  "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 3 to AuthCont2
                             "\x75\x00\x00\x00"           // Size
                             "\x06\x00\x00\x00"           // Id
                             "\x02\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\x60\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x01"
                             "\x05"   "payload\x00"               "\x00\x00\x00\x00"      "\x00"
                      // Set OK to false
                      // This will force an exception.
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\x00\x00"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"      "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"      "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);

      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_THROW(
          action(),
          std::runtime_error
      );
  }

  TEST(AuthenticateScramShaTest, AuthCont2OKNotDone)
  {
      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 2 to AuthCont
                             "\xa3\x00\x00\x00"           // Size
                             "\x04\x00\x00\x00"           // Id
                             "\x01\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flags
                             "\x00"                       // Kind 0
                             "\x8e\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x2e\x00\x00\x00"  "\x00"  // Binary
                                  "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38"  // 1.0
                                  "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c"  // 2.0
                                  "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 3 to AuthCont2
                             "\x75\x00\x00\x00"           // Size
                             "\x06\x00\x00\x00"           // Id
                             "\x02\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\x60\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                  // Set Done to false
                  // This will generate an exception.
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x00\x00\x00\x00"      "\x00"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"      "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"      "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);

      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_THROW(
          action(),
          std::runtime_error
      );
  }

  TEST(AuthenticateScramShaTest, AuthCont2OK)
  {
      //std::int32_t        conversationId;     // Seen
      //bool                done;               // Seen
      //Binary              payload;            // Seen

      using namespace std::string_literals;
      // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
      std::string mongoData(
                             // Response 1 to AuthInit
                             "\xe2\x00\x00\x00"           // Size
                             "\x02\x00\x00\x00"           // Id
                             "\x00\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\xcd\x00\x00\x00"           // BSON Size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x6d\x00\x00\x00"  "\x00"  // Binary
                                  "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e"  // 1.0
                                  "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64"  // 2.0
                                  "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72"  // 3.0
                                  "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71"  // 4.0
                                  "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69"  // 5.0
                                  "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45"  // 6.0
                                  "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 2 to AuthCont
                             "\xa3\x00\x00\x00"           // Size
                             "\x04\x00\x00\x00"           // Id
                             "\x01\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flags
                             "\x00"                       // Kind 0
                             "\x8e\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x00"
                             "\x05"   "payload\x00"               "\x2e\x00\x00\x00"  "\x00"  // Binary
                                  "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38"  // 1.0
                                  "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c"  // 2.0
                                  "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"  "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"  "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"

                             // Response 3 to AuthCont2
                             "\x75\x00\x00\x00"           // Size
                             "\x06\x00\x00\x00"           // Id
                             "\x02\x00\x00\x00"           // Response To
                             "\xdd\x07\x00\x00"           // OP_MSG
                             "\x00\x00\x00\x00"           // Flag
                             "\x00"                       // Kind 0
                             "\x60\x00\x00\x00"           // BSON size
                             "\x10"   "conversationId\x00"        "\x01\x00\x00\x00"
                             "\x08"   "done\x00"                  "\x01"
                             "\x05"   "payload\x00"               "\x00\x00\x00\x00"      "\x00"
                             "\x01"   "ok\x00"                    "\x00\x00\x00\x00\x00\x00\xf0\x3f"
                             "\x02"   "errmsg\x00"                "\x01\x00\x00\x00"      "\x00"
                             "\x02"   "codeName\x00"              "\x01\x00\x00\x00"      "\x00"
                             "\x10"   "code\x00"                  "\x00\x00\x00\x00"
                             "\x00"s);


      std::stringstream mongoStream;
      mongoStream << mongoData;

      getMessageId()  = 0;
      auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};

      EXPECT_NO_THROW(
          action()
      );
  }
\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.