Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(541)

Side by Side Diff: remoting/protocol/secure_p2p_socket.cc

Issue 7038053: Implementation of SecureP2PSocket (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: test for retry Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/protocol/secure_p2p_socket.h ('k') | remoting/protocol/secure_p2p_socket_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "remoting/protocol/secure_p2p_socket.h"
6
7 #include "base/logging.h"
8 #include "base/rand_util.h"
9 #include "crypto/symmetric_key.h"
10 #include "net/base/io_buffer.h"
11 #include "net/base/net_errors.h"
12
13 using net::CompletionCallback;
14 using net::IOBuffer;
15
16 namespace remoting {
Wez 2011/06/24 23:23:51 nit: Add a blank line after namespace foo {.
Sergey Ulanov 2011/06/24 23:36:40 IMO it is not necessary in case of nested namespac
17 namespace protocol {
18
19 namespace {
Wez 2011/06/24 23:23:51 nit: Blank line after namespace {.
20 const char kMaskSalt[16] = {0xDB, 0x68, 0xB5, 0xFD, 0x17, 0x0E, 0x15, 0x77,
21 0x56, 0xAF, 0x7A, 0x3A, 0x1A, 0x57, 0x75, 0x02};
22 const char kHashSalt[16] = {0x4E, 0x2F, 0x96, 0xAB, 0x0A, 0x39, 0x92, 0xA2,
23 0x56, 0x94, 0x91, 0xF5, 0x7E, 0x58, 0x2E, 0xFA};
24 const char kFrameType[4] = {0x0, 0x0, 0x0, 0x1};
25 const int kFrameTypeSize = sizeof(kFrameType);
26 const size_t kKeySize = 16;
27 const int kHeaderSize = 44;
28 const int kSeqNumberSize = 8;
29 const int kHashPosition = 0;
30 const int kNoncePosition = kKeySize;
31 const int kRawMessagePosition = kNoncePosition + kKeySize;
32 const int kSeqNumberPosition = kRawMessagePosition;
33 const int kFrameTypePosition = kSeqNumberPosition + kSeqNumberSize;
34 const int kMessagePosition = kFrameTypePosition + kFrameTypeSize;
35 const int kReadBufferSize = 65536;
36 const std::string kMaskSaltStr(kMaskSalt, kKeySize);
37 const std::string kHashSaltStr(kHashSalt, kKeySize);
38
39 inline void SetBE64(void* memory, uint64 v) {
40 uint8* mem_ptr = reinterpret_cast<uint8*>(memory);
41
42 mem_ptr[0] = static_cast<uint8>(v >> 56);
43 mem_ptr[1] = static_cast<uint8>(v >> 48);
44 mem_ptr[2] = static_cast<uint8>(v >> 40);
45 mem_ptr[3] = static_cast<uint8>(v >> 32);
46 mem_ptr[4] = static_cast<uint8>(v >> 24);
47 mem_ptr[5] = static_cast<uint8>(v >> 16);
48 mem_ptr[6] = static_cast<uint8>(v >> 8);
49 mem_ptr[7] = static_cast<uint8>(v >> 0);
50 }
51
52 inline uint64 GetBE64(const void* memory) {
53 const uint8* mem_ptr = reinterpret_cast<const uint8*>(memory);
54
55 return (static_cast<uint64>(mem_ptr[0]) << 56) |
56 (static_cast<uint64>(mem_ptr[1]) << 48) |
57 (static_cast<uint64>(mem_ptr[2]) << 40) |
58 (static_cast<uint64>(mem_ptr[3]) << 32) |
59 (static_cast<uint64>(mem_ptr[4]) << 24) |
60 (static_cast<uint64>(mem_ptr[5]) << 16) |
61 (static_cast<uint64>(mem_ptr[6]) << 8) |
62 (static_cast<uint64>(mem_ptr[7]) << 0);
63 }
64
65 } // namespace
66
67 ////////////////////////////////////////////////////////////////////////////
68 // SecureP2PSocket Implementation.
69 SecureP2PSocket::SecureP2PSocket(Socket* socket, const std::string& ice_key)
70 : socket_(socket),
71 write_seq_(0),
72 read_seq_(0),
73 user_read_callback_(NULL),
74 user_read_buf_len_(0),
75 user_write_callback_(NULL),
76 user_write_buf_len_(0),
77 ALLOW_THIS_IN_INITIALIZER_LIST(
78 read_callback_(NewCallback(this, &SecureP2PSocket::ReadDone))),
79 read_buf_(new net::IOBufferWithSize(kReadBufferSize)),
80 ALLOW_THIS_IN_INITIALIZER_LIST(
81 write_callback_(NewCallback(this, &SecureP2PSocket::WriteDone))),
82 msg_hasher_(crypto::HMAC::SHA1) {
83 // Make sure the key is valid.
84 CHECK(ice_key.size() == kKeySize);
85
86 // Create the mask key from ice key.
87 crypto::HMAC mask_hasher(crypto::HMAC::SHA1);
88 bool ret = mask_hasher.Init(
89 reinterpret_cast<const unsigned char*>(ice_key.data()), kKeySize);
90 DCHECK(ret) << "Initialize HMAC-SHA1 for mask failed.";
91 scoped_array<uint8> mask_digest(new uint8[mask_hasher.DigestLength()]);
92 mask_hasher.Sign(kMaskSaltStr, mask_digest.get(),
93 mask_hasher.DigestLength());
94 mask_key_.reset(crypto::SymmetricKey::Import(
95 crypto::SymmetricKey::AES,
96 std::string(mask_digest.get(), mask_digest.get() + kKeySize)));
97 DCHECK(mask_key_.get()) << "Import symmetric key failed.";
98
99 // Initialize the encryptor with mask key.
100 encryptor_.Init(mask_key_.get(), crypto::Encryptor::CTR, "");
101
102 // Create the hash key from ice key.
103 crypto::HMAC hash_hasher(crypto::HMAC::SHA1);
104 ret = hash_hasher.Init(
105 reinterpret_cast<const unsigned char*>(ice_key.data()), kKeySize);
106 DCHECK(ret) << "Initialize HMAC-SHA1 for hash failed.";
107 scoped_array<uint8> hash_key(new uint8[hash_hasher.DigestLength()]);
108 hash_hasher.Sign(kHashSaltStr, hash_key.get(), hash_hasher.DigestLength());
109
110 // Create a hasher for message.
111 ret = msg_hasher_.Init(hash_key.get(), kKeySize);
112 DCHECK(ret) << "Initialize HMAC-SHA1 for message failed.";
113 }
114
115 int SecureP2PSocket::Read(IOBuffer* buf, int buf_len,
116 CompletionCallback* callback) {
117 DCHECK(!user_read_buf_);
118 DCHECK(!user_read_buf_len_);
119 DCHECK(!user_read_callback_);
120
121 user_read_buf_ = buf;
122 user_read_buf_len_ = buf_len;
123 user_read_callback_ = callback;
124 return ReadInternal();
125 }
126
127 int SecureP2PSocket::Write(IOBuffer* buf, int buf_len,
128 CompletionCallback* callback) {
129 // See the spec for the steps taken in this method:
130 // http://www.whatwg.org/specs/web-apps/current-work/complete/video-conferenci ng-and-peer-to-peer-communication.html#peer-to-peer-connections
131 // 4. Increment sequence number by one.
132 ++write_seq_;
133
134 const int encrypted_buffer_size = kHeaderSize + buf_len;
135 scoped_refptr<net::IOBuffer> encrypted_buf =
136 new net::IOBuffer(encrypted_buffer_size);
137
138 // 6. Concatenate to form the raw message.
139 const int kRawMessageSize = kSeqNumberSize + kFrameTypeSize + buf_len;
140 std::string raw_message;
141 raw_message.resize(kRawMessageSize);
142 char* raw_message_buf = const_cast<char*>(raw_message.data());
143 SetBE64(raw_message_buf, write_seq_);
144 memcpy(raw_message_buf + kSeqNumberSize, kFrameType,
145 kFrameTypeSize);
146 memcpy(raw_message_buf + kSeqNumberSize + kFrameTypeSize,
147 buf->data(), buf_len);
148
149 // 7. Encrypt the message.
150 std::string nonce = base::RandBytesAsString(kKeySize);
151 CHECK(encryptor_.UpdateCounter(nonce));
152 std::string encrypted_message;
153 CHECK(encryptor_.Encrypt(raw_message, &encrypted_message));
154 memcpy(encrypted_buf->data() + kRawMessagePosition,
155 encrypted_message.data(), encrypted_message.size());
156
157 // 8. Concatenate nonce and encrypted message to form masked message.
158 memcpy(encrypted_buf->data() + kNoncePosition, nonce.data(), kKeySize);
159
160 // 10. Create hash from masked message with nonce.
161 scoped_array<uint8> msg_digest(new uint8[msg_hasher_.DigestLength()]);
162 msg_hasher_.Sign(
163 base::StringPiece(encrypted_buf->data() + kNoncePosition,
164 kRawMessageSize + kKeySize),
165 msg_digest.get(), msg_hasher_.DigestLength());
166 memcpy(encrypted_buf->data() + kHashPosition, msg_digest.get(), kKeySize);
167
168 // Write to the socket.
169 int ret = socket_->Write(encrypted_buf, encrypted_buffer_size,
170 write_callback_.get());
171 if (ret == net::ERR_IO_PENDING) {
172 DCHECK(callback);
173 user_write_callback_ = callback;
174 user_write_buf_len_ = buf_len;
175 return ret;
176 } else if (ret < 0) {
177 return ret;
178 }
179 DCHECK_EQ(buf_len + kHeaderSize, ret);
180 return buf_len;
181 }
182
183 bool SecureP2PSocket::SetReceiveBufferSize(int32 size) {
184 return true;
185 }
186
187 bool SecureP2PSocket::SetSendBufferSize(int32 size) {
188 return true;
189 }
190
191 int SecureP2PSocket::ReadInternal() {
192 while (true) {
193 int ret = socket_->Read(read_buf_, kReadBufferSize, read_callback_.get());
194 if (ret == net::ERR_IO_PENDING || ret < 0)
195 return ret;
196
197 ret = DecryptBuffer(ret);
198 if (ret == net::ERR_INVALID_RESPONSE)
Sergey Ulanov 2011/06/24 19:15:26 nit: Add comment explaining why we retry here.
199 continue;
200
201 user_read_buf_ = NULL;
202 user_read_buf_len_ = 0;
203 user_read_callback_ = NULL;
204 return ret;
205 }
206 }
207
208 void SecureP2PSocket::ReadDone(int err) {
209 net::CompletionCallback* callback = user_read_callback_;
210 user_read_callback_ = NULL;
211
212 if (err < 0) {
213 user_read_buf_len_ = 0;
214 user_read_buf_ = NULL;
215 callback->Run(err);
216 return;
217 }
218
219 int ret = DecryptBuffer(err);
220 if (ret == net::ERR_INVALID_RESPONSE)
221 ret = ReadInternal();
222 if (ret == net::ERR_IO_PENDING)
223 return;
224
225 user_read_buf_ = NULL;
226 user_read_buf_len_ = 0;
227 callback->Run(ret);
228 }
229
230 void SecureP2PSocket::WriteDone(int err) {
231 net::CompletionCallback* callback = user_write_callback_;
232 int buf_len = user_write_buf_len_;
233
234 user_write_callback_ = NULL;
235 user_write_buf_len_ = 0;
236
237 if (err >= 0) {
238 DCHECK_EQ(buf_len + kHeaderSize, err);
239 callback->Run(buf_len);
240 return;
241 }
242 callback->Run(err);
243 }
244
245 int SecureP2PSocket::DecryptBuffer(int size) {
246 if (size < kRawMessagePosition)
247 return net::ERR_INVALID_RESPONSE;
248
249 // See the spec for the steps taken in this method:
250 // http://www.whatwg.org/specs/web-apps/current-work/complete/video-conferenci ng-and-peer-to-peer-communication.html#peer-to-peer-connections
251 // 5. Compute hash of the message.
252 scoped_array<uint8> msg_digest(new uint8[msg_hasher_.DigestLength()]);
253 msg_hasher_.Sign(
254 base::StringPiece(read_buf_->data() + kNoncePosition,
255 size - kNoncePosition),
256 msg_digest.get(), msg_hasher_.DigestLength());
257
258 // 6. Compare the hash values.
259 int ret = memcmp(read_buf_->data(), msg_digest.get(), kKeySize);
260 if (ret)
261 return net::ERR_INVALID_RESPONSE;
262
263 // 7. Decrypt the message.
264 std::string nonce = std::string(
265 read_buf_->data() + kNoncePosition, kKeySize);
266 CHECK(encryptor_.UpdateCounter(nonce));
267 const int raw_message_size = size - kRawMessagePosition;
268
269 // TODO(hclam): Change Encryptor API to trim this memcpy.
270 std::string encrypted_message(read_buf_->data() + kRawMessagePosition,
271 raw_message_size);
272 std::string raw_message;
273 CHECK(encryptor_.Decrypt(encrypted_message, &raw_message));
274
275 if (raw_message_size < kSeqNumberSize)
276 return net::ERR_INVALID_RESPONSE;
277
278 // 12. Read the sequence number.
279 uint64 seq_number = GetBE64(raw_message.data());
280
281 // The spec says we reject the packet if it is out of order. We don't do
282 // this so allow upper levels to do reordering.
283
284 // 14. Save the most recent sequence number.
285 read_seq_ = seq_number;
286
287 // 15. Parse the frame type.
288 if (raw_message_size < kSeqNumberSize + kFrameTypeSize)
289 return net::ERR_INVALID_RESPONSE;
290 ret = memcmp(raw_message.data() + kSeqNumberSize, kFrameType,
291 kFrameTypeSize);
292 if (ret)
293 return net::ERR_INVALID_RESPONSE;
294
295 // 16. Read the message.
296 const int kMessageSize = raw_message_size - kSeqNumberSize - kFrameTypeSize;
297 memcpy(user_read_buf_->data(),
298 raw_message.data() + kSeqNumberSize + kFrameTypeSize, kMessageSize);
299 return kMessageSize;
300 }
301
302 } // namespace protocol
303 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/protocol/secure_p2p_socket.h ('k') | remoting/protocol/secure_p2p_socket_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698