blob: 46e227576bd02863eb9454fed55d38537b17d484 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/ie7_password.h"
6
7#include <wincrypt.h>
8#include <string>
9#include <vector>
10
11#include "base/logging.h"
12#include "base/scoped_ptr.h"
13#include "base/string_util.h"
14
15namespace {
16
17// Structures that IE7/IE8 use to store a username/password.
18// Some of the fields might have been incorrectly reverse engineered.
19struct PreHeader {
20 DWORD pre_header_size; // Size of this header structure. Always 12.
21 DWORD header_size; // Size of the real Header: sizeof(Header) +
22 // item_count * sizeof(Entry);
23 DWORD data_size; // Size of the data referenced by the entries.
24};
25
26struct Header {
27 char wick[4]; // The string "WICK". I don't know what it means.
28 DWORD fixed_header_size; // The size of this structure without the entries:
29 // sizeof(Header).
30 DWORD item_count; // Number of entries. It should always be 2. One for
31 // the username, and one for the password.
32 wchar_t two_letters[2]; // Two unknown bytes.
33 DWORD unknown[2]; // Two unknown DWORDs.
34};
35
36struct Entry {
37 DWORD offset; // Offset where the data referenced by this entry is
38 // located.
39 FILETIME time_stamp; // Timestamp when the password got added.
40 DWORD string_length; // The length of the data string.
41};
42
43// Main data structure.
44struct PasswordEntry {
45 PreHeader pre_header; // Contains the size of the different sections.
46 Header header; // Contains the number of items.
47 Entry entry[1]; // List of entries containing a string. The first one
48 // is the username, the second one if the password.
49};
50
51// Cleans up a crypt prov and a crypt hash.
52void CleanupHashContext(HCRYPTPROV prov, HCRYPTHASH hash) {
53 if (hash)
54 CryptDestroyHash(hash);
55 if (prov)
56 CryptReleaseContext(prov, 0);
57}
58
59} // namespace
60
61namespace ie7_password {
62
63bool GetUserPassFromData(const std::vector<unsigned char>& data,
64 std::wstring* username,
65 std::wstring* password) {
66 const PasswordEntry* information =
67 reinterpret_cast<const PasswordEntry*>(&data.front());
68
69 // Some expected values. If it's not what we expect we don't even try to
70 // understand the data.
71 if (information->pre_header.pre_header_size != sizeof(PreHeader))
72 return false;
73
74 if (information->header.item_count != 2) // Username and Password
75 return false;
76
77 if (information->header.fixed_header_size != sizeof(Header))
78 return false;
79
80 const uint8* ptr = &data.front();
81 const uint8* offset_to_data = ptr + information->pre_header.header_size +
82 information->pre_header.pre_header_size;
83
84 const Entry* user_entry = information->entry;
85 const Entry* pass_entry = user_entry+1;
86
87 *username = reinterpret_cast<const wchar_t*>(offset_to_data +
88 user_entry->offset);
89 *password = reinterpret_cast<const wchar_t*>(offset_to_data +
90 pass_entry->offset);
91 return true;
92}
93
94std::wstring GetUrlHash(const std::wstring& url) {
95 HCRYPTPROV prov = NULL;
96 HCRYPTHASH hash = NULL;
97
98 std::wstring lower_case_url = StringToLowerASCII(url);
99 BOOL result = CryptAcquireContext(&prov, 0, 0, PROV_RSA_FULL, 0);
100 if (!result) {
101 if (GetLastError() == NTE_BAD_KEYSET) {
102 // The keyset does not exist. Create one.
103 result = CryptAcquireContext(&prov, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET);
104 }
105 }
106
107 if (!result) {
108 DCHECK(false);
109 return std::wstring();
110 }
111
112 // Initialize the hash.
113 if (!CryptCreateHash(prov, CALG_SHA1, 0, 0, &hash)) {
114 CleanupHashContext(prov, hash);
115 DCHECK(false);
116 return std::wstring();
117 }
118
119 // Add the data to the hash.
120 const unsigned char* buffer =
121 reinterpret_cast<const unsigned char*>(lower_case_url.c_str());
122 DWORD data_len = static_cast<DWORD>((lower_case_url.size() + 1) *
123 sizeof(wchar_t));
124 if (!CryptHashData(hash, buffer, data_len, 0)) {
125 CleanupHashContext(prov, hash);
126 DCHECK(false);
127 return std::wstring();
128 }
129
130 // Get the size of the resulting hash.
131 DWORD hash_len = 0;
132 DWORD buffer_size = sizeof(hash_len);
133 if (!CryptGetHashParam(hash, HP_HASHSIZE,
134 reinterpret_cast<unsigned char*>(&hash_len),
135 &buffer_size, 0)) {
136 CleanupHashContext(prov, hash);
137 DCHECK(false);
138 return std::wstring();
139 }
140
141 // Get the hash data.
142 scoped_array<unsigned char> new_buffer(new unsigned char[hash_len]);
143 if (!CryptGetHashParam(hash, HP_HASHVAL, new_buffer.get(), &hash_len, 0)) {
144 CleanupHashContext(prov, hash);
145 DCHECK(false);
146 return std::wstring();
147 }
148
149 std::wstring url_hash;
150
151 // Transform the buffer to an hexadecimal string.
152 unsigned char checksum = 0;
153 for (DWORD i = 0; i < hash_len; ++i) {
154 checksum += new_buffer.get()[i];
155 url_hash += StringPrintf(L"%2.2X", new_buffer.get()[i]);
156 }
157
158 url_hash += StringPrintf(L"%2.2X", checksum);
159
160 CleanupHashContext(prov, hash);
161 return url_hash;
162}
163
164bool DecryptPassword(const std::wstring& url,
165 const std::vector<unsigned char>& data,
166 std::wstring* username, std::wstring* password) {
167 std::wstring lower_case_url = StringToLowerASCII(url);
168 DATA_BLOB input = {0};
169 DATA_BLOB output = {0};
170 DATA_BLOB url_key = {0};
171
172 input.pbData = const_cast<unsigned char*>(&data.front());
173 input.cbData = static_cast<DWORD>((data.size()) *
174 sizeof(std::string::value_type));
175
176 url_key.pbData = reinterpret_cast<unsigned char*>(
177 const_cast<wchar_t*>(lower_case_url.data()));
178 url_key.cbData = static_cast<DWORD>((lower_case_url.size() + 1) *
179 sizeof(std::wstring::value_type));
180
181 if (CryptUnprotectData(&input, NULL, &url_key, NULL, NULL,
182 CRYPTPROTECT_UI_FORBIDDEN, &output)) {
183 // Now that we have the decrypted information, we need to understand it.
184 std::vector<unsigned char> decrypted_data;
185 decrypted_data.resize(output.cbData);
186 memcpy(&decrypted_data.front(), output.pbData, output.cbData);
187
188 GetUserPassFromData(decrypted_data, username, password);
189
190 LocalFree(output.pbData);
191 return true;
192 }
193
194 return false;
195}
196
197} // namespace ie7_password
license.botbf09a502008-08-24 00:55:55198