Chromium Code Reviews

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: use Encryptor CTR Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | | Annotate | Revision Log
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;
Sergey Ulanov 2011/06/22 23:51:35 not needed because you include io_buffer.h.
Alpha Left Google 2011/06/23 21:53:01 This using is just so that I don't need net:: when
Sergey Ulanov 2011/06/23 23:12:45 Oh, yes, I misread it, sorry.
15
16 namespace remoting {
17 namespace protocol {
18
19 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 size_t kKeySize = 16;
26 const int kHeaderSize = 44;
27 const int kSeqNumberSize = 8;
28 const int kFrameTypeSize = sizeof(kFrameType);
Sergey Ulanov 2011/06/22 23:51:35 nit: put this next to kFrameType.
Alpha Left Google 2011/06/23 21:53:01 Done.
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 Set8(void* memory, size_t offset, uint8 v) {
Sergey Ulanov 2011/06/22 23:51:35 Do you really need Set8 and Get8? You just case |m
Alpha Left Google 2011/06/23 21:53:01 Done.
40 static_cast<uint8*>(memory)[offset] = v;
41 }
42
43 inline uint8 Get8(const void* memory, size_t offset) {
44 return static_cast<const uint8*>(memory)[offset];
45 }
46
47 inline void SetBE64(void* memory, uint64 v) {
48 Set8(memory, 0, static_cast<uint8>(v >> 56));
49 Set8(memory, 1, static_cast<uint8>(v >> 48));
50 Set8(memory, 2, static_cast<uint8>(v >> 40));
51 Set8(memory, 3, static_cast<uint8>(v >> 32));
52 Set8(memory, 4, static_cast<uint8>(v >> 24));
53 Set8(memory, 5, static_cast<uint8>(v >> 16));
54 Set8(memory, 6, static_cast<uint8>(v >> 8));
55 Set8(memory, 7, static_cast<uint8>(v >> 0));
56 }
57
58 inline uint64 GetBE64(const void* memory) {
59 return (static_cast<uint64>(Get8(memory, 0)) << 56)
60 | (static_cast<uint64>(Get8(memory, 1)) << 48)
Sergey Ulanov 2011/06/22 23:51:35 nit: don't wrap operators, '|' should be at the en
Alpha Left Google 2011/06/23 21:53:01 Done.
61 | (static_cast<uint64>(Get8(memory, 2)) << 40)
62 | (static_cast<uint64>(Get8(memory, 3)) << 32)
63 | (static_cast<uint64>(Get8(memory, 4)) << 24)
64 | (static_cast<uint64>(Get8(memory, 5)) << 16)
65 | (static_cast<uint64>(Get8(memory, 6)) << 8)
66 | (static_cast<uint64>(Get8(memory, 7)) << 0);
67 }
68
69 } // namespace
70
71 ////////////////////////////////////////////////////////////////////////////
72 // SecureP2PSocket Implementation.
73 SecureP2PSocket::SecureP2PSocket(Socket* socket, const std::string& ice_key)
74 : socket_(socket),
75 write_seq_(0),
76 read_seq_(0),
77 user_read_callback_(NULL),
78 user_read_buf_len_(0),
79 user_write_callback_(NULL),
80 user_write_buf_len_(0),
81 ALLOW_THIS_IN_INITIALIZER_LIST(
82 read_callback_(NewCallback(this, &SecureP2PSocket::ReadDone))),
83 read_buf_(new net::IOBufferWithSize(kReadBufferSize)),
84 ALLOW_THIS_IN_INITIALIZER_LIST(
85 write_callback_(NewCallback(this, &SecureP2PSocket::WriteDone))),
86 msg_hasher_(crypto::HMAC::SHA1) {
87 // Make sure the key is valid.
88 CHECK(ice_key.size() == kKeySize);
89
90 // Create the mask key from ice key.
91 crypto::HMAC mask_hasher(crypto::HMAC::SHA1);
92 bool ret = mask_hasher.Init(
93 reinterpret_cast<const unsigned char*>(ice_key.data()), kKeySize);
94 DCHECK(ret) << "Initialize HMAC-SHA1 for mask failed.";
95 scoped_array<uint8> mask_digest(new uint8[mask_hasher.DigestLength()]);
96 mask_hasher.Sign(kMaskSaltStr, mask_digest.get(),
97 mask_hasher.DigestLength());
98 mask_key_.reset(crypto::SymmetricKey::Import(
99 crypto::SymmetricKey::AES,
100 std::string(mask_digest.get(), mask_digest.get() + kKeySize)));
101 DCHECK(mask_key_.get()) << "Import symmetric key failed.";
102
103 // Initialize the encryptor with mask key.
104 encryptor_.Init(mask_key_.get(), crypto::Encryptor::CTR, "");
105
106 // Create the hash key from ice key.
107 crypto::HMAC hash_hasher(crypto::HMAC::SHA1);
108 ret = hash_hasher.Init(
109 reinterpret_cast<const unsigned char*>(ice_key.data()), kKeySize);
110 DCHECK(ret) << "Initialize HMAC-SHA1 for hash failed.";
111 scoped_array<uint8> hash_key(new uint8[hash_hasher.DigestLength()]);
112 hash_hasher.Sign(kHashSaltStr, hash_key.get(), hash_hasher.DigestLength());
113
114 // Create a hasher for message.
115 ret = msg_hasher_.Init(hash_key.get(), kKeySize);
116 DCHECK(ret) << "Initialize HMAC-SHA1 for message failed.";
117 }
118
119 int SecureP2PSocket::Read(IOBuffer* buf, int buf_len,
120 CompletionCallback* callback) {
121 DCHECK(!user_read_buf_);
122 DCHECK(!user_read_buf_len_);
123 DCHECK(!user_read_callback_);
124
125 user_read_buf_ = buf;
126 user_read_buf_len_ = buf_len;
127 user_read_callback_ = callback;
128 return ReadInternal();
129 }
130
131 int SecureP2PSocket::Write(IOBuffer* buf, int buf_len,
132 CompletionCallback* callback) {
133 // See the spec for the steps taken in this method:
134 // http://www.whatwg.org/specs/web-apps/current-work/complete/video-conferenci ng-and-peer-to-peer-communication.html#peer-to-peer-connections
135 // 4. Increment sequence number by one.
136 ++write_seq_;
137
138 const int kEncryptedBufferSize = kHeaderSize + buf_len;
Sergey Ulanov 2011/06/22 23:51:35 This should not be a const.
Alpha Left Google 2011/06/23 21:53:01 This is just to make sure I don't modify it later.
Sergey Ulanov 2011/06/23 23:12:45 call it encrypted_buffer_size?
139 scoped_refptr<net::IOBuffer> encrypted_buf =
140 new net::IOBuffer(kEncryptedBufferSize);
141
142 // 6. Concatenate to form the raw message.
143 const int kRawMessageSize = kSeqNumberSize + kFrameTypeSize + buf_len;
Sergey Ulanov 2011/06/22 23:51:35 why is this a const? it depends on buf_len
Alpha Left Google 2011/06/23 21:53:01 Just so that I never modify it.
Sergey Ulanov 2011/06/23 23:12:45 raw_message_size?
144 std::string raw_message;
145 raw_message.resize(kRawMessageSize);
146 char* raw_message_buf = const_cast<char*>(raw_message.data());
147 SetBE64(raw_message_buf, write_seq_);
148 memcpy(raw_message_buf + kSeqNumberSize, kFrameType,
149 kFrameTypeSize);
150 memcpy(raw_message_buf + kSeqNumberSize + kFrameTypeSize,
151 buf->data(), buf_len);
152
153 // 7. Decrypt the message.
Sergey Ulanov 2011/06/22 23:51:35 s/Decrypt/Encrypt/
Alpha Left Google 2011/06/23 21:53:01 Done.
154 std::string nonce = base::RandBytesAsString(kKeySize);
155 CHECK(encryptor_.UpdateCounter(nonce));
156 std::string encrypted_message;
157 CHECK(encryptor_.Encrypt(raw_message, &encrypted_message));
158 memcpy(encrypted_buf->data() + kRawMessagePosition,
159 encrypted_message.data(), encrypted_message.size());
160
161 // 8. Concatenate nonce and encrypted message to form masked message.
162 memcpy(encrypted_buf->data() + kNoncePosition, nonce.data(), kKeySize);
163
164 // 10. Create hash from masked message with nonce.
165 scoped_array<uint8> msg_digest(new uint8[msg_hasher_.DigestLength()]);
166 msg_hasher_.Sign(
167 base::StringPiece(encrypted_buf->data() + kNoncePosition,
168 kRawMessageSize + kKeySize),
169 msg_digest.get(), msg_hasher_.DigestLength());
170 memcpy(encrypted_buf->data() + kHashPosition, msg_digest.get(), kKeySize);
171
172 // Write to the socket.
173 int ret = socket_->Write(encrypted_buf, kEncryptedBufferSize,
174 write_callback_.get());
175 if (ret == net::ERR_IO_PENDING) {
176 DCHECK(callback);
177 user_write_callback_ = callback;
178 user_write_buf_len_ = buf_len;
179 return ret;
180 } else if (ret < 0) {
181 return ret;
182 }
183 DCHECK_EQ(buf_len + kHeaderSize, ret);
184 return buf_len;
185 }
186
187 bool SecureP2PSocket::SetReceiveBufferSize(int32 size) {
188 return true;
189 }
190
191 bool SecureP2PSocket::SetSendBufferSize(int32 size) {
192 return true;
193 }
194
195 int SecureP2PSocket::ReadInternal() {
196 int ret = socket_->Read(read_buf_, kReadBufferSize, read_callback_.get());
197 if (ret == net::ERR_IO_PENDING || ret < 0)
198 return ret;
199
200 ret = DecryptBuffer(ret);
201 if (ret == net::ERR_INVALID_RESPONSE)
202 return ReadInternal();
Sergey Ulanov 2011/06/22 23:51:35 It looks wrong that ReadInternal() is recursive. R
Alpha Left Google 2011/06/23 21:53:01 I changed this code to simply return ret so that u
Sergey Ulanov 2011/06/23 23:12:45 I think it's better to handle it here. In most cas
203
204 user_read_buf_ = NULL;
205 user_read_buf_len_ = 0;
206 user_read_callback_ = NULL;
207 return ret;
208 }
209
210 void SecureP2PSocket::ReadDone(int err) {
211 net::CompletionCallback* callback = user_read_callback_;
212 user_read_callback_ = NULL;
213
214 if (err < 0) {
215 user_read_buf_len_ = 0;
216 user_read_buf_ = NULL;
217 callback->Run(err);
218 return;
219 }
220
221 int ret = DecryptBuffer(err);
222 if (ret == net::ERR_INVALID_RESPONSE)
223 ret = ReadInternal();
224 if (ret == net::ERR_IO_PENDING)
225 return;
226
227 user_read_buf_ = NULL;
228 user_read_buf_len_ = 0;
229 callback->Run(ret);
230 }
231
232 void SecureP2PSocket::WriteDone(int err) {
233 net::CompletionCallback* callback = user_write_callback_;
234 int buf_len = user_write_buf_len_;
235
236 user_write_callback_ = NULL;
237 user_write_buf_len_ = 0;
238
239 if (err >= 0) {
240 DCHECK_EQ(buf_len + kHeaderSize, err);
241 callback->Run(buf_len);
242 return;
243 }
244 callback->Run(err);
245 }
246
247 int SecureP2PSocket::DecryptBuffer(int size) {
248 if (size > user_read_buf_len_)
249 return net::ERR_MSG_TOO_BIG;
Sergey Ulanov 2011/06/22 23:51:35 UDP sockets just truncate packets in case read buf
Alpha Left Google 2011/06/23 21:53:01 Done.
250 if (size < kRawMessagePosition)
251 return net::ERR_INVALID_RESPONSE;
252
253 // See the spec for the steps taken in this method:
254 // http://www.whatwg.org/specs/web-apps/current-work/complete/video-conferenci ng-and-peer-to-peer-communication.html#peer-to-peer-connections
255 // 5. Compute hash of the message.
256 scoped_array<uint8> msg_digest(new uint8[msg_hasher_.DigestLength()]);
257 msg_hasher_.Sign(
258 base::StringPiece(read_buf_->data() + kNoncePosition,
259 size - kNoncePosition),
260 msg_digest.get(), msg_hasher_.DigestLength());
261
262 // 6. Compare the hash values.
263 int ret = memcmp(read_buf_->data(), msg_digest.get(), kKeySize);
264 if (ret)
265 return net::ERR_INVALID_RESPONSE;
266
267 // 7. Decrypt the message.
268 std::string nonce = std::string(
269 read_buf_->data() + kNoncePosition, kKeySize);
270 CHECK(encryptor_.UpdateCounter(nonce));
271 const int kRawMessageSize = size - kRawMessagePosition;
272
273 // TODO(hclam): Change Encryptor API to trim this memcpy.
274 std::string encrypted_message(read_buf_->data() + kRawMessagePosition,
275 kRawMessageSize);
276 std::string raw_message;
277 CHECK(encryptor_.Decrypt(encrypted_message, &raw_message));
278
279 if (kRawMessageSize < kSeqNumberSize)
280 return net::ERR_INVALID_RESPONSE;
281
282 // 12. Read the sequence number.
283 uint64 seq_number = GetBE64(raw_message.data());
284
285 // 13. Reject if this packet is too old.
286 // TODO(hclam): Determine if we should follow the spec. The spec says drop
287 // the packet and don't do reordering but we can do better.
288 if (false && seq_number < read_seq_)
Sergey Ulanov 2011/06/22 23:51:35 why "if(false)" ? Maybe just remove this code and
Alpha Left Google 2011/06/23 21:53:01 Done.
289 return net::ERR_INVALID_RESPONSE;
290
291 // 14. Save the most recent sequence number.
292 read_seq_ = seq_number;
293
294 // 15. Parse the frame type.
295 if (kRawMessageSize < kSeqNumberSize + kFrameTypeSize)
296 return net::ERR_INVALID_RESPONSE;
297 ret = memcmp(raw_message.data() + kSeqNumberSize, kFrameType,
298 kFrameTypeSize);
299 if (ret)
300 return net::ERR_INVALID_RESPONSE;
301
302 // 16. Read the message.
303 const int kMessageSize = kRawMessageSize - kSeqNumberSize - kFrameTypeSize;
304 memcpy(user_read_buf_->data(),
305 raw_message.data() + kSeqNumberSize + kFrameTypeSize, kMessageSize);
306 return kMessageSize;
307 }
308
309 } // namespace protocol
310 } // namespace remoting
OLDNEW

Powered by Google App Engine