1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/webdata/encryptor/ie7_password.h"
6
7#include <wincrypt.h>
8#include <string>
9#include <vector>
10
11#include "base/memory/scoped_ptr.h"
12#include "base/sha1.h"
13#include "base/strings/string_util.h"
14#include "base/strings/stringprintf.h"
15
16namespace {
17
18// Structures that IE7/IE8 use to store a username/password.
19// Some of the fields might have been incorrectly reverse engineered.
20struct PreHeader {
21  DWORD pre_header_size;  // Size of this header structure. Always 12.
22  DWORD header_size;      // Size of the real Header: sizeof(Header) +
23                          // item_count * sizeof(Entry);
24  DWORD data_size;        // Size of the data referenced by the entries.
25};
26
27struct Header {
28  char wick[4];             // The string "WICK". I don't know what it means.
29  DWORD fixed_header_size;  // The size of this structure without the entries:
30                            // sizeof(Header).
31  DWORD item_count;         // Number of entries. It should always be 2. One for
32                            // the username, and one for the password.
33  wchar_t two_letters[2];   // Two unknown bytes.
34  DWORD unknown[2];         // Two unknown DWORDs.
35};
36
37struct Entry {
38  DWORD offset;         // Offset where the data referenced by this entry is
39                        // located.
40  FILETIME time_stamp;  // Timestamp when the password got added.
41  DWORD string_length;  // The length of the data string.
42};
43
44// Main data structure.
45struct PasswordEntry {
46  PreHeader pre_header;  // Contains the size of the different sections.
47  Header header;         // Contains the number of items.
48  Entry entry[1];        // List of entries containing a string. The first one
49                         // is the username, the second one if the password.
50};
51
52}  // namespace
53
54namespace ie7_password {
55
56bool GetUserPassFromData(const std::vector<unsigned char>& data,
57                         std::wstring* username,
58                         std::wstring* password) {
59  const PasswordEntry* information =
60      reinterpret_cast<const PasswordEntry*>(&data.front());
61
62  // Some expected values. If it's not what we expect we don't even try to
63  // understand the data.
64  if (information->pre_header.pre_header_size != sizeof(PreHeader))
65    return false;
66
67  if (information->header.item_count != 2)  // Username and Password
68    return false;
69
70  if (information->header.fixed_header_size != sizeof(Header))
71    return false;
72
73  const uint8* ptr = &data.front();
74  const uint8* offset_to_data = ptr + information->pre_header.header_size +
75                                information->pre_header.pre_header_size;
76
77  const Entry* user_entry = information->entry;
78  const Entry* pass_entry = user_entry+1;
79
80  *username = reinterpret_cast<const wchar_t*>(offset_to_data +
81                                               user_entry->offset);
82  *password = reinterpret_cast<const wchar_t*>(offset_to_data +
83                                               pass_entry->offset);
84  return true;
85}
86
87std::wstring GetUrlHash(const std::wstring& url) {
88  std::wstring lower_case_url = StringToLowerASCII(url);
89  // Get a data buffer out of our std::wstring to pass to SHA1HashString.
90  std::string url_buffer(
91      reinterpret_cast<const char*>(lower_case_url.c_str()),
92      (lower_case_url.size() + 1) * sizeof(wchar_t));
93  std::string hash_bin = base::SHA1HashString(url_buffer);
94
95  std::wstring url_hash;
96
97  // Transform the buffer to an hexadecimal string.
98  unsigned char checksum = 0;
99  for (size_t i = 0; i < hash_bin.size(); ++i) {
100    // std::string gives signed chars, which mess with StringPrintf and
101    // check_sum.
102    unsigned char hash_byte = static_cast<unsigned char>(hash_bin[i]);
103    checksum += hash_byte;
104    url_hash += base::StringPrintf(L"%2.2X", static_cast<unsigned>(hash_byte));
105  }
106  url_hash += base::StringPrintf(L"%2.2X", checksum);
107
108  return url_hash;
109}
110
111bool DecryptPassword(const std::wstring& url,
112                     const std::vector<unsigned char>& data,
113                     std::wstring* username, std::wstring* password) {
114  std::wstring lower_case_url = StringToLowerASCII(url);
115  DATA_BLOB input = {0};
116  DATA_BLOB output = {0};
117  DATA_BLOB url_key = {0};
118
119  input.pbData = const_cast<unsigned char*>(&data.front());
120  input.cbData = static_cast<DWORD>((data.size()) *
121                                    sizeof(std::string::value_type));
122
123  url_key.pbData = reinterpret_cast<unsigned char*>(
124                      const_cast<wchar_t*>(lower_case_url.data()));
125  url_key.cbData = static_cast<DWORD>((lower_case_url.size() + 1) *
126                                      sizeof(std::wstring::value_type));
127
128  if (CryptUnprotectData(&input, NULL, &url_key, NULL, NULL,
129                         CRYPTPROTECT_UI_FORBIDDEN, &output)) {
130    // Now that we have the decrypted information, we need to understand it.
131    std::vector<unsigned char> decrypted_data;
132    decrypted_data.resize(output.cbData);
133    memcpy(&decrypted_data.front(), output.pbData, output.cbData);
134
135    GetUserPassFromData(decrypted_data, username, password);
136
137    LocalFree(output.pbData);
138    return true;
139  }
140
141  return false;
142}
143
144}  // namespace ie7_password
145