1# BC Protocol
2
3This is an attempt to document the BC protocol. It is far from complete
4but should serve as a good basis for those wishing to develop apps for
5BC cameras.
6
7## Messages
8
9Each message has the general format:
10
11- Header: 20-24 bytes
12
13- Message Body
14
15### Header
16
17The header has the format:
18
19|    magic     |  message id  | message length | encryption offset |   encrypt  | message class |
20|--------------|--------------|----------------|-------------------|------------|---------------|
21| f0 de bc 0a  | 01 00 00 00  |  2c 07 00 00   |    00 00 00 01    |    01  dc  |     14 65     |
22
23- Magic 4 bytes
24- ID 4 bytes
25- Message length 4 bytes
26- Encryption offset 4 bytes
27- Encryption flag 1 byte
28- Unknown 1 byte
29- Message class 2 bytes
30
31Or
32
33|    Magic     |  Message ID  | Message Length | Encryption Offset |    Status Code    | Message Class | Payload Offset |
34|--------------|--------------|----------------|-------------------|-------------------|---------------|----------------|
35| f0 de bc 0a  | 01 00 00 00  |  28 01 00 00   |    00 00 00 01    |       c8 00       |     14 64     |   00 00 00 00  |
36
37
38- Magic 4 bytes
39- ID 4 bytes
40- Message length 4 bytes
41- Encryption offset 4 bytes
42- Status Code 2 bytes
43- Message class 2 bytes
44- Binary offset 4 bytes (Presence depend on message class)
45
46#### Magic
47
48The magic bytes for BC messages is always `f0 de bc 0a` for client <-> device.
49Or magic `a0 cd ed 0f` for device <-> device, eg NVR <-> IPC.
50When receiving packet these should be used to quickly discard invalid packets.
51
52#### Message ID
53
54Each function in BC has its own message ID. For example login is 1, video data
55is 3, motion detection is 33.
56
57For a more complete list please see the [messages doc](dissector/messages.md)
58
59#### Message length
60
61The message length contains the full length of the data to follow the header
62this includes both the XML and binary parts.
63
64#### Encryption offset
65
66The encryption offset is used as part of the decoding process. It is combined
67with the key to decrypt the data.
68
69Here is an example decrypter in rust
70
71```rust
72const XML_KEY: [u8; 8] = [0x1F, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78, 0xFF];
73
74pub fn crypt(offset: u32, buf: &[u8]) -> Vec<u8> {
75    let key_iter = XML_KEY.iter().cycle().skip(offset as usize % 8);
76    key_iter
77        .zip(buf)
78        .map(|(key, i)| *i ^ key ^ (offset as u8))
79        .collect()
80}
81```
82
83In short the key is offset by the encryption offset in the header. Then each
84encrypted byte is paired with the offseted keys bytes (looping the offseted
85key as necessary). Then each byte is XORed with the paired key byte and the
86offset.
87
88The key is the same for all cameras.
89
90Older cameras do not use encryption and all messages are sent as plain text.
91
92The offset bytes are actually made up of other useful information
93channel_id 1 byte - NVR channel related to request/response or `00` if N/A.
94stream_id 1 byte - `00`=clear, `01`=fluent, `04`=balanced
95unknown 1 byte  - Always `00`
96message_handle 1 byte - client increments per request, replies use request handle
97
98#### Encryption Flag
99
100Client will send the number `0xXXdc` and the server will reply `0xXXdd`.
101Where `XX` is one of the following.
102
103- 0 Unencrypted
104- 1 BC Encryption
105- 2 AES Encryption (Camera)
106- 3 AES Encryption (Client)
107
108`dc` means this encryption protol or lower. So `0x01dc` means BC on no encryption
109whereas `0x03dc` means AES, BC or Unencrypted.
110
111`dd` is the reply that the camera sends to the `dc` request. It is the chosen
112protocol that will be used.
113
114**Note:** When requesting AES the client sends `0x03dc` and the camera replies `0x02dc`.
115We are not sure why.
116
117Encryption is negotiated in the login request.
118
119#### Status Code
120
121In a request this is set to `00 00`.
122In a reply this is a http style response code.
123`c8 00` = 200 OK
124`90 01` = 400 Bad Request
125
126#### Message class
127
128The message class determines the length of the header. The following classes
129and header lengths are known.
130
131- 0x6514: "legacy" 20 bytes
132
133- 0x6614: "modern" 20 bytes
134
135- 0x6414: "modern" 24 bytes
136
137- 0x0000: "modern" 24 bytes
138
139#### Payload offset
140
141For messages that contain the payload offset field this represents where to start
142the payload part of the message. The total length of the message
143(extension XML + payload) is equal to the message length in the header
144so Payload Offset is total_length - this_offset. Where as this field also represents the end of the
145extension XML part of the message.
146
147# Login
148
149For message details see the [docs](dissector/messages.md)
150
151Clients should login by
152
153- Send legacy login message
154    - User and pass MD5'ed
155    - Capped at 32 bytes with a null terminator
156    - Bytes 32 is always zero so only first 31 bytes are compared
157
158- Receive modern upgrade message with nonce in XML
159
160- Send modern login:
161  - User and pass concatenated with the nonce
162  - Send MD5'ed user and password
163
164- Receive reply with device info
165
166# Starting Video
167
168Video is requested and received with message ID 3.
169
170Video is requested with an XML of the following format:
171
172```xml
173<?xml version="1.0" encoding="UTF-8" ?>
174<body>
175<Preview version="1.1">
176<channelId>0</channelId>
177<handle>0</handle>
178<streamType>mainStream</streamType>
179</Preview>
180</body>
181```
182
183streamType can be either
184
185- `mainStream` in which case it will be HD
186- `subStream` in which case it will be SD
187
188
189channelId is part of the NVR when multiple cameras use the same IP. In this
190case each camera has its own channelId.
191
192The `handle` is used when multiple streams are requested in a single login.
193This number should be unique for each stream. If not then that stream
194(Clear or Fluent) will not work until the camera is reset.
195
196
197The reply is first a message with the following Extension Xml
198
199```xml
200<?xml version="1.0" encoding="UTF-8" ?>
201<Extension version="1.1">
202<binaryData>1</binaryData>
203</Extension>
204```
205
206After which all message bodies of type id 3 are binary.
207
208The binary represents a stream of data that can be interrupted by packet
209boundaries. Clients should create a buffer and pop bytes for processing when
210complete media packets are received. Media packets descriptions can be found in
211the [docs](dissector/mediapacket.md)
212
213# Other Function
214
215Other data can be received from the camera by sending the appropriate header to
216the camera. For example sending the header for ID 78
217
218|    Magic     |  Message ID  | Message Length | Encryption Offset |    Status Code    | Message Class | Payload Offset |
219|--------------|--------------|----------------|-------------------|-------------------|---------------|----------------|
220| f0 de bc 0a  | 4e 00 00 00  |  d3 00 00 00   |    08 db 9c 00    |       c8 00       |     00 00     |   00 00 00 00  |
221
222The camera will reply with an xml with brightness and contrast
223
224```xml
225<?xml version="1.0" encoding="UTF-8" ?>
226<body>
227<VideoInput version="1.1">
228<channelId>0</channelId>
229<bright>128</bright>
230<contrast>128</contrast>
231<saturation>128</saturation>
232<hue>128</hue>
233</VideoInput>
234</body>
235```
236
237Some message IDs also require input along with the request header. For example
238
239ID 151 which is the users ability info requires the header
240
241|    Magic     |  Message ID  | Message Length | Encryption Offset |    Status Code    | Message Class | Payload Offset |
242|--------------|--------------|----------------|-------------------|-------------------|---------------|----------------|
243| f0 de bc 0a  | 97 00 00 00  |  a7 00 00 00   |    00 00 00 02    |       00 00       |     14 64     |   a7 00 00 00  |
244
245and the body of
246
247```xml
248<?xml version="1.0" encoding="UTF-8" ?>
249<Extension version="1.1">
250<userName>...</userName> <!-- Plain text username -->
251<token>system, network, alarm, record, video, image</token>
252</Extension>
253```
254
255Which contains the plain text of the username of interest and the tokens for
256abilities you want to know about.
257
258Details of expected formats should be found from the
259[docs](dissector/messages.md)
260
261#### AES Encryption
262
263If the encryption flag returned from the camera is `0x02dd` then AES encryption will be used.
264
265The cameras use AES cfb128 with an IV of `0123456789abcdef` the key is made as follows
266
267- Concatenated the `NONCE` from login with a `-` then with your plain text password
268- MD5 Hash this concatenated string
269- Represent the hash as hex string in all caps
270- Take the first 16 characters as the encryption key
271
272
273Here is an example:
274
275```rust
276use aes::Aes128;
277use cfb_mode::cipher::{NewStreamCipher, StreamCipher};
278use cfb_mode::Cfb;
279
280const IV: &[u8] = b"0123456789abcdef";
281let key_phrase = format!("{}-{}", nonce, passwd);
282let key_phrase_hash = format!("{:X}\0", md5::compute(&key_phrase))
283    .to_uppercase()
284    .into_bytes();
285let key = key_phrase_hash[0..16];
286
287let mut decrypted = encrypted.to_vec();
288Cfb::<Aes128>::new(key.into(), IV.into()).decrypt(&mut decrypted);
289return decrypted;
290```
291