563e217f9668ccd69cb8eabf774d10920fe52150
[platform/upstream/iotjs.git] / src / js / ble_hci_socket_smp.js
1 /* Copyright 2016-present Samsung Electronics Co., Ltd. and other contributors
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /* Copyright (C) 2015 Sandeep Mistry sandeep.mistry@gmail.com
17  *
18  * Permission is hereby granted, free of charge, to any person obtaining a copy
19  * of this software and associated documentation files (the "Software"), to deal
20  * in the Software without restriction, including without limitation the rights
21  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22  * copies of the Software, and to permit persons to whom the Software is
23  * furnished to do so, subject to the following conditions:
24  *
25  * The above copyright notice and this permission notice shall be included in
26  * all copies or substantial portions of the Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34  * SOFTWARE.
35  */
36
37 var debug = console.log; //requir('debug')('ble_hci_socket_smp');
38
39 var events = require('events');
40 var util = require('util');
41
42 var crypto = require('ble_hci_socket_crypto');
43 var mgmt = require('ble_hci_socket_mgmt');
44
45 var SMP_CID = 0x0006;
46
47 var SMP_PAIRING_REQUEST = 0x01;
48 var SMP_PAIRING_RESPONSE = 0x02;
49 var SMP_PAIRING_CONFIRM = 0x03;
50 var SMP_PAIRING_RANDOM = 0x04;
51 var SMP_PAIRING_FAILED = 0x05;
52 var SMP_ENCRYPT_INFO = 0x06;
53 var SMP_MASTER_IDENT = 0x07;
54
55 var SMP_UNSPECIFIED = 0x08;
56
57 var Smp = function(aclStream, localAddressType, localAddress, remoteAddressType, remoteAddress) {
58   this._aclStream = aclStream;
59
60   this._iat = new Buffer([(remoteAddressType === 'random') ? 0x01 : 0x00]);
61   this._ia = new Buffer(remoteAddress.split(':').reverse().join(''), 'hex');
62   this._rat = new Buffer([(localAddressType === 'random') ? 0x01 : 0x00]);
63   this._ra = new Buffer(localAddress.split(':').reverse().join(''), 'hex');
64
65   this._stk = null;
66   this._random = null;
67   this._diversifier = null;
68
69   this.onAclStreamDataBinded = this.onAclStreamData.bind(this);
70   this.onAclStreamEncryptChangeBinded = this.onAclStreamEncryptChange.bind(this);
71   this.onAclStreamLtkNegReplyBinded = this.onAclStreamLtkNegReply.bind(this);
72   this.onAclStreamEndBinded = this.onAclStreamEnd.bind(this);
73
74   this._aclStream.on('data', this.onAclStreamDataBinded);
75   this._aclStream.on('encryptChange', this.onAclStreamEncryptChangeBinded);
76   this._aclStream.on('ltkNegReply', this.onAclStreamLtkNegReplyBinded);
77   this._aclStream.on('end', this.onAclStreamEndBinded);
78 };
79
80 util.inherits(Smp, events.EventEmitter);
81
82 Smp.prototype.onAclStreamData = function(cid, data) {
83   if (cid !== SMP_CID) {
84     return;
85   }
86
87   var code = data.readUInt8(0);
88
89   if (SMP_PAIRING_REQUEST === code) {
90     this.handlePairingRequest(data);
91   } else if (SMP_PAIRING_CONFIRM === code) {
92     this.handlePairingConfirm(data);
93   } else if (SMP_PAIRING_RANDOM === code) {
94     this.handlePairingRandom(data);
95   } else if (SMP_PAIRING_FAILED === code) {
96     this.handlePairingFailed(data);
97   }
98 };
99
100 Smp.prototype.onAclStreamEncryptChange = function(encrypted) {
101   if (encrypted) {
102     if (this._stk && this._diversifier && this._random) {
103       this.write(Buffer.concat([
104         new Buffer([SMP_ENCRYPT_INFO]),
105         this._stk
106       ]));
107
108       this.write(Buffer.concat([
109         new Buffer([SMP_MASTER_IDENT]),
110         this._diversifier,
111         this._random
112       ]));
113     }
114   }
115 };
116
117 Smp.prototype.onAclStreamLtkNegReply = function() {
118     this.write(new Buffer([
119       SMP_PAIRING_FAILED,
120       SMP_UNSPECIFIED
121     ]));
122
123     this.emit('fail');
124 };
125
126 Smp.prototype.onAclStreamEnd = function() {
127   this._aclStream.removeListener('data', this.onAclStreamDataBinded);
128   this._aclStream.removeListener('encryptChange', this.onAclStreamEncryptChangeBinded);
129   this._aclStream.removeListener('ltkNegReply', this.onAclStreamLtkNegReplyBinded);
130   this._aclStream.removeListener('end', this.onAclStreamEndBinded);
131 };
132
133 Smp.prototype.handlePairingRequest = function(data) {
134   this._preq = data;
135
136   this._pres = new Buffer([
137     SMP_PAIRING_RESPONSE,
138     0x03, // IO capability: NoInputNoOutput
139     0x00, // OOB data: Authentication data not present
140     0x01, // Authentication requirement: Bonding - No MITM
141     0x10, // Max encryption key size
142     0x00, // Initiator key distribution: <none>
143     0x01  // Responder key distribution: EncKey
144   ]);
145
146   this.write(this._pres);
147 };
148
149 Smp.prototype.handlePairingConfirm = function(data) {
150   this._pcnf = data;
151
152   this._tk = new Buffer('00000000000000000000000000000000', 'hex');
153   this._r = crypto.r();
154
155   this.write(Buffer.concat([
156     new Buffer([SMP_PAIRING_CONFIRM]),
157     crypto.c1(this._tk, this._r, this._pres, this._preq, this._iat, this._ia, this._rat, this._ra)
158   ]));
159 };
160
161 Smp.prototype.handlePairingRandom = function(data) {
162   var r = data.slice(1);
163
164   var pcnf = Buffer.concat([
165     new Buffer([SMP_PAIRING_CONFIRM]),
166     crypto.c1(this._tk, r, this._pres, this._preq, this._iat, this._ia, this._rat, this._ra)
167   ]);
168
169   if (this._pcnf.toString('hex') === pcnf.toString('hex')) {
170     this._diversifier = new Buffer('0000', 'hex');
171     this._random = new Buffer('0000000000000000', 'hex');
172     this._stk = crypto.s1(this._tk, this._r, r);
173
174     mgmt.addLongTermKey(this._ia, this._iat, 0, 0, this._diversifier, this._random, this._stk);
175
176     this.write(Buffer.concat([
177       new Buffer([SMP_PAIRING_RANDOM]),
178       this._r
179     ]));
180   } else {
181     this.write(new Buffer([
182       SMP_PAIRING_FAILED,
183       SMP_PAIRING_CONFIRM
184     ]));
185
186     this.emit('fail');
187   }
188 };
189
190 Smp.prototype.handlePairingFailed = function(data) {
191   this.emit('fail');
192 };
193
194 Smp.prototype.write = function(data) {
195   this._aclStream.write(SMP_CID, data);
196 };
197
198 module.exports = Smp;