// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 Eduardo Aguiar
//
// This program 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 2, or (at your option) any later
// version.
//
// This program 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 <http://www.gnu.org/licenses/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "pssp_data2.h"
#include <mobius/crypt/hash_sha1.h>
#include <mobius/crypt/cipher_des.h>
#include <mobius/crypt/cipher_block_mode.h>
#include <mobius/io/bytearray_io.h>
#include <mobius/decoder/data_decoder.h>
#include <mobius/charset.h>
#include <map>

namespace mobius
{
namespace forensics
{
namespace registry
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief pssp_data2 implementation class
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class pssp_data2::impl
{
public:

  // function prototypes
  void set_key (const std::string&, mobius::bytearray&);
  const mobius::bytearray get_key (const std::string&) const;

private:
  //! \brief key map
  std::map <std::string, mobius::bytearray> keys_;
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief set data2 key
//! \param name key name
//! \param value key value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
pssp_data2::impl::set_key (const std::string& name, mobius::bytearray& value)
{
  keys_[name] = value;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get data2 key
//! \param name key name
//! \return None
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const mobius::bytearray
pssp_data2::impl::get_key (const std::string& name) const
{
  mobius::bytearray key_value;

  auto iter = keys_.find (name);

  if (iter != keys_.end ())
    key_value = iter->second;

  return key_value;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief constructor
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
pssp_data2::pssp_data2 ()
  : impl_ (std::make_shared <impl> ())
{
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief set data2 key
//! \param name key name
//! \param value key value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
pssp_data2::set_key (const std::string& name, mobius::bytearray& value)
{
  impl_->set_key (name, value);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get data2 key
//! \param name key name
//! \return key value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const mobius::bytearray
pssp_data2::get_key (const std::string& name) const
{
  return impl_->get_key (name);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief retrieve pssp cryptographic keys from SID key
//! \param sid_key SID key
//! \return PSSP key collection
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const mobius::forensics::registry::pssp_data2
get_data2 (mobius::forensics::registry::registry_key sid_key)
{
  pssp_data2 data2;

  // get "Data 2" key
  auto data2_key = sid_key.get_key_by_name ("Data 2");

  if (data2_key)
    {
      // evaluate SID based hash value
      mobius::crypt::hash_sha1 sid_hash;
      sid_hash.update ({0x66, 0x41, 0xa3, 0x29});
      sid_hash.update (mobius::conv_charset (sid_key.get_name (), "UTF-8", "UTF-16LE"));

      if (sid_key.get_name ().length () % 2)
        sid_hash.update ({0x14, 0x9a});

      auto sid_hash_value = sid_hash.get_digest ();

      // decrypt all cryptographic keys in "Data 2"
      //! \todo usar flag (2 ou 3)
      for (auto subkey : data2_key)
        {
          // decode "Value" data
          auto data = subkey.get_data_by_name ("Value").get_data ();
          auto reader = mobius::io::new_bytearray_reader (data);
          auto decoder = mobius::decoder::data_decoder (reader);

          decoder.skip (8);
          auto flag = decoder.get_uint32_le ();				//  8 - 11
          auto encrypted_key = decoder.get_bytearray_by_size (24);	// 12 - 35
          decoder.skip (4);
          auto salt = decoder.get_bytearray_by_size (16);		// 40 - 55

          // build DES key
          mobius::crypt::hash_sha1 data_hash;

          data_hash.update (salt);

          if (flag == 2)
            data_hash.update (sid_hash_value.slice (0, 3));

          else if (flag == 3)
            data_hash.update (sid_hash_value);

          auto des_key = data_hash.get_digest ().slice (0, 7);

          // decrypt cryptographic key
          mobius::crypt::cipher_des des (des_key, mobius::crypt::new_cipher_block_mode_cbc ());
          auto key_name = subkey.get_name ();
          auto key_value = des.decrypt (encrypted_key).slice (0, 7);

          // add cryptographic key to collection
          data2.set_key (key_name, key_value);
        }
    }

  return data2;
}

} // namespace registry
} // namespace forensics
} // namespace mobius
