aboutsummaryrefslogtreecommitdiff
path: root/dcs8000lh.md
blob: a680e6bfe122ce000243662dc38ca76d315cee39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
# D-Link DCS-8000LH

![D-Link DCS-8000LH](https://eu.dlink.com/uk/en/products/-/media/product-pages/dcs/8000lh/dcs_8000lh_front.png)

These are random notes descibing how I changed my D-Link DCS-8000LH
from a cloud camera to a locally managed IP camera, streaming H.264
MPEG-TS over HTTP and HTTPS. Some of the tools and ideas might work
for other cameras too, given some model specific adaptation.

Complete defogging requires modifying one of the file systems in the
camera.  This implies a slight risk of ending up with a brick. You
have now been warned...

This is tested and developed on firmware versions v2.01.03 and
v2.02.02 only.  The final complete procedure has only been tested with
v2.02.02. It should work fine with v2.01.03 and other versions, in
theory, but could fail like anything untested.  Please let me know if
you have an original v2.01.03 firmware update from D-Link, or any
other version for that matter, or know where firmware updates can be
downloaded.

The v2.02.02 update is available from
https://mydlinkmpfw.auto.mydlink.com/DCS-8000LH/DCS-8000LH_Ax_v2.02.02_3014.bin
at the time of writing. But I assume this link stops working as soon
as there is a newer version available.


## Changelog

* v0.01 (20190515) - initial published version
* v0.02 (20190515) - added RTSP support and information

## Problem

My D-Link DCS-8000LH came with firmware version 2.01.03 from factory.
This firmware is locked to the [**mydlink**](https://www.mydlink.com)
app/cloud service.  It does not provide a local NIPCA compatible HTTP
API or similar, and it does not stream video over HTTP, HTTPS or RTSP.

Additionally, there is no way to downgrade the firmware.  In fact,
there is no documented way to install any firmware image at all,
except trusting the "mydlink" cloud service to do it for you.


## Solution

#### Primary goals achieved:

* configuration of network and admin password via Bluetooth LE, without
  registering with D-Link or using the [**mydlink**](https://www.mydlink.com) app at all
* streaming MPEG-TS directly from camera over HTTP and HTTPS
* direct RTSP streaming
* NIPCA API configuration over HTTP and HTTPS, supporting settings
  like LED, nightmode, etc


#### And some extra goodies which came for free

* Firmware upgrades and downgrades via HTTP
* telnet server with a root account (admin/PIN Code)
* easy access to serial console, using the same root account
* running arbitrary commands on the camera using Bluetooth

Read on for all the gory details...


### Requirements

 * a Linux PC with a Bluetooth controller
 * python3 with @IanHarvey's
  [**bluepy**](https://ianharvey.github.io/bluepy-doc/index.html)
  library
 * WiFi network with WPA2-PSK and a known password
 * mksquashfs from the squashfs-tools package
 * a tftp server or web server accepting file uploads (for backups)
 * guts :-)

Most recent Linux distros will probably do. The bluepy library can be
installed using pip if it is not available as a distro package.  Other
types of WiFi networks might work, but has not been tested with the
provided tools.  The squashfs-tools are only necessary if you want to
rebuild the "mydlink" alternative file system.  I assume you can even
run the tools without installing Linux, by using a Linux "Live"
CD/DVD/USB stick.

This was developed and tested on Debian Buster.
 


### Camera configuration using the Bluetooth LE GATT API

The "mydlink" app uses Bluetooth LE for camera setup, authenticated by
the camera pincode.  This repo includes an alternative python script
with a few extra goodies, but needing a better name:
[**dcs8000lh-configure.py**](dcs8000lh-configure.py)

(Why not an Android app?  Because it would take me much more time to
write. Should be fairly easy to do though, for anyone with enough
interest.  You can find all the necessary protocol details here and in
the python code. Please let me know if you are interested)

The script does not support scanning for the simple reason that this
would require root access for not real gain.  You have to provide the
**PIN Code** from the camera label
anyway.  Reading the **MAC ID** as well is simple enough
![camera label](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-label.jpg)

The **PIN Code** and **MAC** is also printed on the code card that
came with the camera:
![code card](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-code-card.jpg)


Note that the command line **address** paramenter must be formatted as
**01:23:45:67:89:AB** instead of the **0123456789AB** format printed
on the label.

Current script help text at the time of writing shows what the script
can do:

```
$ ./dcs8000lh-configure.py -h
usage: dcs8000lh-configure.py [-h] [--essid ESSID] [--wifipw WIFIPW]
                              [--survey] [--netconf] [--sysinfo]
                              [--command COMMAND] [--telnetd] [--lighttpd]
                              [--rtsp] [--unsignedfw] [--attrs] [-V]
                              address pincode

IPCam Bluetooth configuration tool.

positional arguments:
  address            IPCam Bluetooth MAC address (01:23:45:67:89:AB)
  pincode            IPCam PIN Code (6 digits)

optional arguments:
  -h, --help         show this help message and exit
  --essid ESSID      Connect to this WiFi network
  --wifipw WIFIPW    Password for ESSID
  --survey           List WiFi networks seen by the IPCam
  --netconf          Print current network configuration
  --sysinfo          Dump system configuration
  --command COMMAND  Run command on IPCam
  --telnetd          Start telnet server on IPCam
  --lighttpd         Start web server on IPCam
  --rtsp             Enable access to RTSP server on IPCam
  --unsignedfw       Allow unsigned firmware
  --attrs            Dump IPCam GATT characteristics
  -V, --version      show program's version number and exit
```


#### Real session excample after a clean upgrade to firmware v2.02.02, followed by factory reset 

1. Start by making sure the camera can see our WiFi network.  This
   also verifies that we can connect and authenticate against the
   Bluetooth LE IPCam service, without making any changes to any
   camera settings:

```
$ ./dcs8000lh-configure.py B0:C5:54:AA:BB:CC 123456 --survey
Connecting to B0:C5:54:AA:BB:CC...
Verifying IPCam service
Connected to 'DCS-8000LH-BBCC'
DCS-8000LH-BBCC is scanning for WiFi networks...
{'I': 'AirLink126FD4', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '47'}
{'I': 'Antiboks', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '73'}
{'I': 'ASV17', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '47'}
{'I': 'ASV17-dlink', 'M': '0', 'C': '6', 'S': '4', 'E': '2', 'P': '57'}
{'I': 'DIRECT-33-HP%20ENVY%205000%20series', 'M': '0', 'C': '1', 'S': '4', 'E': '2', 'P': '46'}
{'I': 'fjorde123', 'M': '0', 'C': '1', 'S': '4', 'E': '2', 'P': '55'}
{'I': 'JOJ', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '48'}
{'I': 'Kjellerbod', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '75'}
{'I': 'Landskap_24', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '46'}
{'I': 'mgmt', 'M': '0', 'C': '1', 'S': '4', 'E': '2', 'P': '72'}
{'I': 'Rindedal', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '68'}
{'I': 'risikovirus', 'M': '0', 'C': '1', 'S': '4', 'E': '2', 'P': '45'}
{'I': 'risikovirus%20WIFI', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '45'}
{'I': 'Stavik2014', 'M': '0', 'C': '6', 'S': '4', 'E': '2', 'P': '47'}
{'I': 'TomterNett1', 'M': '0', 'C': '6', 'S': '4', 'E': '2', 'P': '44'}
{'I': 'VIF', 'M': '0', 'C': '11', 'S': '4', 'E': '2', 'P': '47'}
Done.
```

2. We're going to use the 'Kjellerbod' network, so that looks good.
   Select it and give the associated WiFi password to the camera:

```
$ ./dcs8000lh-configure.py B0:C5:54:AA:BB:CC 123456 --essid Kjellerbod --wifipw redacted
Connecting to B0:C5:54:AA:BB:CC...
Verifying IPCam service
Connected to 'DCS-8000LH-BBCC'
DCS-8000LH-BBCC is scanning for WiFi networks...
Will configure: M=0;I=Kjellerbod;S=4;E=2;K=redacted
Done.
```

3. Verify that the camera connected to the Wifi network and got an
   address.  If not, go back and try again, making sure you are using
   the correct WiFi password:

```
$ ./dcs8000lh-configure.py B0:C5:54:AA:BB:CC 123456 --netconf
Connecting to B0:C5:54:AA:BB:CC...
Verifying IPCam service
Connected to 'DCS-8000LH-BBCC'
wifi link is Up
wifi config: {'M': '0', 'I': 'Kjellerbod', 'S': '4', 'E': '2'}
ip config: {'I': '192.168.2.37', 'N': '255.255.255.0', 'G': '192.168.2.1', 'D': '148.122.16.253'}
Done.
```


**WARNING**: You must make a backup of your device at this point if
you haven't done so already.  See the [**Backup**](#Backup) section
below.  I only skipped it in this example because I already had a
complete backup of my camera.



4. We need HTTP NIPCA API for the remaining tasks, so temporarily
   start lighttpd on the camera:
   
```
$ ./dcs8000lh-configure.py B0:C5:54:AA:BB:CC 123456 --lighttpd
Connecting to B0:C5:54:AA:BB:CC...
Verifying IPCam service
Connected to 'DCS-8000LH-BBCC'
Attempting to run '[ $(tdb get HTTPServer Enable_byte) -eq 1 ] || tdb set HTTPServer Enable_byte=1' on DCS-8000LH-BBCC by abusing the 'set admin password' request
Attempting to run '/etc/rc.d/init.d/extra_lighttpd.sh start' on DCS-8000LH-BBCC by abusing the 'set admin password' request
Done.
```

Note that this implicitly changes a couple of settings which are
stored in the ["db"](#Partitions) NVRAM partition, and therefore will persist until
the next factory reset:
 *  extra_lighttpd.sh will exit without doing anything unless
    **HTTPServer Enable** is set
 * the admin password is set both because we're abusing that BLE
   request, and because we need it for the HTTP API access.  The
   script only supports setting the password to the **PIN Code**.
   
*This password restriction is because I'm lazy - there is nothing in
the camera or protocol preventing the password from being set to
something else. But the script would then need the new password as
an additional input parameter for most commands*


5. Disable firmware signature verification. Only firmwares signed by
   D-Link are accepted by default. This feature can be disabled by
   changing a variable in the ["db"](#Partitions) NVRAM partition:

```
$ ./dcs8000lh-configure.py B0:C5:54:AA:BB:CC 123456  --unsignedfw 
Connecting to B0:C5:54:AA:BB:CC...
Verifying IPCam service
Connected to 'DCS-8000LH-BBCC'
Attempting to run 'tdb set SecureFW _TrustLevel_byte=0' on DCS-8000LH-BBCC by abusing the 'set admin password' request
Done.
```

6. The final step is the dangerous one.  It replaces the file system
   on the [**userdata**](#Partitions) partition with our home cooked one.  The D-Link
   firmware uses this partition exclusively for the "mydlink" cloud
   tools, which we don't need.  The rest of the system is not touched
   by our firmware update.  The camera will therefore run exactly the
   same kernel and rootfs as before the update, whatever version they
   were.  I.e., the firmware version does not change - only the
   "mydlink" version.
   

**NOTE**; You need to [build](#BuildFirmware) a **fw.tar** firmware
update image first.

```
$ curl --http1.0 -u admin:123456 --form upload=@fw.tar http://192.168.2.37/config/firmwareupgrade.cgi
upgrade=ok
```

See the section on [error handling](#Errors) if the upgrade request
returned anything else.

The camera will reboot automatically at this point, assuming the
update was successful.  From now both with telnetd and lighttpd
running, and with external access to the RTSP server. All services
will use the same **admin:PIN Code** account for authentication.

So we now have access to direct [streaming](#Streaming) over HTTP,
HTTPS and RTSP without ever having been in contact with the
[**mydlink**](https://www.mydlink.com) service!



### <a name="Streaming"></a>Streaming video locally

Which was the whole point of all this... We can now stream directly
from the camera using for example:


#### HTTP or HTTPS
```
vlc https://192.168.2.37/video/mpegts.cgi
vlc https://192.168.2.37/video/flv.cgi
```

Authenticate using the **admin** user with **PIN Code** as password

AFAICS, this camera does not support MJPEG encoding. But you can
always use ffmpeg to transcode the H.264 anyway.  Looking closer at a
stream sample:


```
$ curl --insecure -u admin:123456 https://192.168.2.37/video/mpegts.cgi>/tmp/stream
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 93.1G    0  438k    0     0  92872      0  12d 11h  0:00:04  12d 11h 92853^C

$ mediainfo /tmp/stream 
General
ID                                       : 1 (0x1)
Complete name                            : /tmp/stream
Format                                   : MPEG-TS
File size                                : 500 KiB
Duration                                 : 5 s 433 ms
Overall bit rate mode                    : Variable
Overall bit rate                         : 752 kb/s

Video
ID                                       : 257 (0x101)
Menu ID                                  : 1 (0x1)
Format                                   : AVC
Format/Info                              : Advanced Video Codec
Format profile                           : High@L4
Format settings, CABAC                   : Yes
Format settings, ReFrames                : 1 frame
Format settings, GOP                     : M=1, N=30
Codec ID                                 : 27
Duration                                 : 5 s 450 ms
Width                                    : 1 280 pixels
Height                                   : 720 pixels
Display aspect ratio                     : 16:9
Frame rate mode                          : Variable
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive

Audio
ID                                       : 256 (0x100)
Menu ID                                  : 1 (0x1)
Format                                   : AAC
Format/Info                              : Advanced Audio Codec
Format version                           : Version 2
Format profile                           : LC
Muxing mode                              : ADTS
Codec ID                                 : 15
Duration                                 : 3 s 456 ms
Bit rate mode                            : Variable
Channel(s)                               : 1 channel
Channel positions                        : Front: C
Sampling rate                            : 16.0 kHz
Frame rate                               : 15.625 FPS (1024 spf)
Compression mode                         : Lossy
```


#### RTSP

Direct RTSP access is also supported, using the same **admin** user.

The RTSP URLs are configurable, so the proper way to use RTSP is to
first check the URL of the wanted profile using the NIPCA API:

```
$ curl -u admin:123456 --insecure 'https://192.168.2.37/config/rtspurl.cgi?profileid=1'
profileid=1
urlentry=live/profile.0
video_codec=H264
audio_codec=OPUS
```

and then connect to this RTSP URL:

```
$ vlc rtsp://192.168.2.37/live/profile.0
```

Note that persistent RTSP access can be enabled with original
unmodified D-Link firmware, using the Bluetooth **--rtsp** option.
This modifies the necessary settings.  The **rtspd** service is
already started by default in the original firmware.

So there is no need to mess with the firmware at all if all you want
is RTSP.


#### <a name="Errors"></a>Errors during firmware update via HTTP

The **firmwareupgrade.cgi** script running in the camera isn't much
smarter than the rest of the system, so there are a few important
things keep in mind.  These are found by trial-and-error:

 * HTTP/1.1 might not work - the firmwareupgrade.cgi script does not support **100 Continue** AFAICS
 * The firmware update image should be provided as a **file** input field from a form
 * The field name must be **upload**.

Use the exact curl command provided above, replacing only the PIN
Code, IP address and firmware filename.  This should work.  Anything
else might not.

The camera must be manually rebooted by removing power or pressing
reset if the firmware upgrade fails for any reason. The
**firmwareupgrade.cgi** script stops most processes, inluding the
Bluetooth handler, and fails to restart them on errors. 

There will be no permanent harm if the upload fails.  But note that
you have to repeat the **--lighttpd** step after rebooting the camera,
before you can retry. It does not start automatically until we've
installed our modified "mydlink" alternative.

The contents of the fw.tar file must obviously be a valid, encrypted,
firmware update intended for the specified hardware.  It must also be
signed.  But the signing key can be unknown to the camera provided the
previous **--unsignedfw** request above was successful.

The [**Makefile**](Makefile) provided here shows how to [build](#BuildFirmware) a valid firmware
update, but for the DCS-8000LH only!  It does not support any other
model. It will create a new throwaway signing key if it canæt find a
real one, and include the associated public key in the archive in case
you want to verify the signature manually.

Note that the encryption key might be model specific.  I do not know
this as I have no other model to look at.  Please let me know if you
have any information on this topic.

The encryption key is part ot the [**pib**](#Partitions) partition, and can be
read from a shell using
```
pibinfo PriKey
```

Or you can simply look at your partition backup.  The key is stored as
a plain text *RSA PRIVATE KEY* PEM blob, so it is easy to spot. This
repo includes a copy of my [key](keys/DCS-8000LH-PriKey.pem) as I see
no point in attempting to keep a well known shared key like this one
"secret"


### <a name="Backup"></a>Backup

Create a backup of everything *before* you mess up.  Restoring will be
hard anyway, so don't rely on that.  But you can forget about
restoring at all unless you have a backup, so make it anyway.

Note that the [**pib**](#Partitions) partition contains data which are
specific to **your** camera, and cannot be restored from any other
source!  This includes
 * model number
 * hardware revision
 * mac address
 * feature bits
 * private keys, pincode and passwords

Well, OK, we can restore most of the [**pib**](#Partitions)  using information from
the [camera label](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-label.jpg), but
it's better to avoid having to do that...

A backup is also useful for analyzing the file systems offline.

Making a backup without networking is inconvenient, so setup
networking first. In theory, you could dump the flash to the serial
console. But this would be very time consuming and tiresome.

The D-Link firmware provides a selection of network file transfer
tools. Pick anyone you like:
 * tftp
 * wget
 * curl
 * ...and probably more

I've been using tftp for my backups because it is simple. You'll
obviously need a tftp server for this. Google for instructions on
setting that up.  You could alternatively set up a web server and use
wget or curl to post the files there, but this is more complx to set
up IMHO.

Here is one example of how to enable temporary telnet access and
copying all camera flash partitions to a tftp server:

```
$ ./dcs8000lh-configure.py B0:C5:54:AA:BB:CC 123456 --telnetd
Connecting to B0:C5:54:AA:BB:CC...
Verifying IPCam service
Connected to 'DCS-8000LH-BBCC'
Adding the 'admin' user as an alias for 'root'
Attempting to run 'grep -Eq ^admin: /etc/passwd||echo admin:x:0:0::/:/bin/sh >>/etc/passwd' on DCS-8000LH-BBCC by abusing the 'set admin password' request
Setting the 'admin' user password to '123456'
Attempting to run 'grep -Eq ^admin:x: /etc/passwd&&echo admin:123456|chpasswd' on DCS-8000LH-BBCC by abusing the 'set admin password' request
Starting telnetd
Attempting to run 'pidof telnetd||telnetd' on DCS-8000LH-BBCC by abusing the 'set admin password' request
 

Attempting to run '[ $(tdb get HTTPServer Enable_byte) -eq 1 ] || tdb set HTTPServer Enable_byte=1' on DCS-8000LH-BBCC by abusing the 'set admin password' request
Attempting to run '/etc/rc.d/init.d/extra_lighttpd.sh start' on DCS-8000LH-BBCC by abusing the 'set admin password' request
Done.


$ telnet 192.168.2.37
Trying 192.168.2.37...
Connected to 192.168.2.37.
Escape character is '^]'.
localhost login: admin
Password: 


BusyBox v1.22.1 (2019-02-14 17:06:35 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.


# for i in 0 1 2 3 4 5 6 7 8; do tftp -l /dev/mtd${i}ro -r mtd$i -p 192.168.2.1; done`
```

Change 192.168.2.37 to the address of your camera and 192.168.2.1 to
the address of your tftp server. Note that most tftp servers require
existing and writable destination files. Refer to your tftp server docs
for details.



## All the gory details


### Restoring original D-Link firmware

The D-Link firmware, including the mydlink tools in the
[**userdata**](#Partitions) partition, can be restored by doing a
manual firmware upgrade providing a firmware update from D-Link.  Real
example, going back to v2.02.02:

```
$ curl --http1.0 -u admin:123456 --form upload=@DCS-8000LH_Ax_v2.02.02_3014.bin http://192.168.2.37/config/firmwareupgrade.cgi
curl: (52) Empty reply from server
```

I don't know why I got that **Empty reply** warning instead of the
expected **upgrade=ok**, but update went fine so I guess it can safely
be ignored. Might be a side effect of rewriting the root file system,
which the firmwareupgrade.cgi script is running from.


### Serial console

Entirely optional.  The defogging procedure does not require console
access, but it can be very useful when debugging problems related to
network configuration etc.

There is a 4 hole female header with 2 mm spacing in the bottom of the
camera. This header is easily accessible without opening the case at
all. But you will need to remove the bottom label to find it:
![label removed](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-label-removed.jpg)

Take a picure of the lable or save the information somewhere else
first, in case you make the it unreadable in the process.

Mate with a 3 (or 4) pin male 2 mm connector, or use sufficiently
solid wires.  The pins need to be 6-10 mm long.  The pins will mess up the QR code, but the rest of the label can be left intact if you're careful:
![header with pins](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-label-with-serial-pins.jpg)

The pinout seen from egde to center of camera  is:


| 1    | 2  | 3  | 4   |
|------|----|----|-----|
| 3.3V | TX | RX | GND |

and the serial port parameters are 57600 8N1.


You obviously need a 3.3V TTL adapter for this, Look at for example
at the generic OpenWrt console instructions if you need guidance.
![USB ttl adapter connected](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-serial-connected.jpg)


Do not connect the 3.3V pin.  All USB TTL adapters are powered by the
USB bus.



### Opening the case

Remove the top and bottom parts of the sylinder.  I assume the two
remaning halves of the sylinder are simple held together by clips, but
I did not verify this after discovering the easily accessible console
header.

The top lid is clipped on:
![top lid](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-top-lid.jpg)

The bottom cover is held in place by two screws under the label:
![bottom cover](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-label-removed.jpg)


Removing the bottom cover reveals the reset button and the console header:
![bottom removed](https://www.mork.no/~bjorn/dcs8000lh/dcs8000lh-bottom-without-cover.jpg)


### U-Boot

My DCS-8000LH came with this boot loader:

`U-Boot 2014.01-rc2-V1.1 (Jun 06 2018 - 03:44:37)`

But it is patched/configured to require a password for access to the
U-Boot prompt. Fortunately, D-Link makes the password readily
available in their GPL package :-) It is found in the file
`DCS-8000LH-GPL/configs/gpl_defconfig`:

`ALPHA_FEATURES_UBOOT_LOGIN_PASSWORD="alpha168"`

Enter **alpha168** password when you see

`Press ESC to abort autoboot in 3 seconds`

and you'll get a `rlxboot#` prompt, with access to these U-Boot commands :

```
rlxboot# ?
?       - alias for 'help'
base    - print or set address offset
bootm   - boot application image from memory
bootp   - boot image via network using BOOTP/TFTP protocol
cmp     - memory compare
coninfo - print console devices and information
cp      - memory copy
crc32   - checksum calculation
echo    - echo args to console
editenv - edit environment variable
efuse   - efuse readall | read addr
env     - environment handling commands
fephy   - fephy read/write
go      - start application at address 'addr'
help    - print command description/usage
imxtract- extract a part of a multi-image
loadb   - load binary file over serial line (kermit mode)
loadx   - load binary file over serial line (xmodem mode)
loady   - load binary file over serial line (ymodem mode)
loop    - infinite loop on address range
md      - memory display
mm      - memory modify (auto-incrementing address)
mw      - memory write (fill)
nm      - memory modify (constant address)
ping    - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
reset   - Perform RESET of the CPU
setenv  - set environment variables
setethaddr- set eth address
setipaddr- set ip address
sf      - SPI flash sub-system
source  - run script from memory
tftpboot- boot image via network using TFTP protocol
tftpput - TFTP put command, for uploading files to a server
tftpsrv - act as a TFTP server and boot the first received file
update  - update image
version - print monitor, compiler and linker version
```

Using the boot loader for image manipulation will be hard though,
since the camera has no ethernet, USB or removable flash and the boot
loader has no WiFi driver.  It is probably possible to load an image
over serial, but I don't have the patience for that...

The environment is fixed and pretty clean:
```
rlxboot# printenv
=3
addmisc=setenv bootargs ${bootargs}console=ttyS0,${baudrate}panic=1
baudrate=57600
bootaddr=(0xBC000000 + 0x1e0000)
bootargs=console=ttyS1,57600 root=/dev/mtdblock8 rts_hconf.hconf_mtd_idx=0 mtdparts=m25p80:256k(boot),128k(pib),1024k(userdata),128k(db),128k(log),128k(dbbackup),128k(logbackup),3072k(kernel),11264k(rootfs)
bootcmd=bootm 0xbc1e0000
bootfile=/vmlinux.img
ethact=r8168#0
ethaddr=00:00:00:00:00:00
load=tftp 80500000 ${u-boot}
loadaddr=0x82000000
stderr=serial
stdin=serial
stdout=serial

Environment size: 533/131068 bytes
```

So we can get ourselves a root shell:


```
rlxboot# setenv bootargs ${bootargs} init=/bin/sh
rlxboot# ${bootcmd}
```

Nothing is mounted or started since /sbin/init is skipped altogether
in this case.  Not even /sys and /proc.  We can emulate a semi-normal
system by running

`/etc/rc.d/rcS` 

as the first command.  And then run for example

`telnetd -l /bin/sh`

to enable temporary passwordless telnet into the camera instead of/in
addition to the serial console. This is futile unless you have
networking of course. I will not go into details on how to do that
from the shell. Use the much simpler Bluetooth procedure described
above. Or the "mydlink" app if you prefer.


### OEM boot log

```
U-Boot 2014.01-rc2-V1.1 (Jun 06 2018 - 03:44:37)

rx5281 prid=0xdc02
DRAM:  64 MiB @ 800 MHz
Skipping flash_init
Flash: 0 Bytes
flash status is 0, 2, 0
SF: Detected W25Q128FV with page size 256 Bytes, erase size 64 KiB, total 16 MiB
Using default environment

In:    serial
Out:   serial
Err:   serial
Net:   Realtek PCIe GBE Family Controller mcfg = 0024
no hw config header
new_ethaddr = 00:00:00:00:00:00
r8168#0
no hw config header
Press ESC to abort autoboot in 3 seconds## Booting kernel from Legacy Image at bc1e0000 ...
get header OKimage_get_kernel check hcrc
image_get_kernel print contents
   Image Name:   linux_3.10
   Created:      2018-06-05  19:44:27 UTC
   Image Type:   MIPS Linux Kernel Image (uncompressed)
   Data Size:    1662157 Bytes = 1.6 MiB
   Load Address: 804d4960
   Entry Point:  804d4960
   Verifying Checksum ... OK
   Loading Kernel Image ... OK

Starting kernel ...

Linux version 3.10.27 (jenkins@DMdssdFW1) (gcc version 4.8.5 20150209 (prerelease) (Realtek RSDK-4.8.5p1 Build 2278) ) #1 PREEMPT Wed Jun 6 03:36:32 CST 2018
prom cpufreq = 500000000
prom memsize = 67108864
hw_ver: 0x1, hw_rev: 0x2, isp_ver: 0x0
bootconsole [early0] enabled
CPU revision is: 0000dc02
Determined physical RAM map:
 memory: 04000000 @ 00000000 (usable)
Reserved contiguous memory at 0x4f3000
Zone ranges:
  Normal   [mem 0x00000000-0x03ffffff]
Movable zone start for each node
Early memory node ranges
  node   0: [mem 0x00000000-0x03ffffff]
icache: 32kB/32B, dcache: 16kB/32B, scache: 0kB/0B
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 16256
Kernel command line: console=ttyS1,57600 root=/dev/mtdblock8 rts_hconf.hconf_mtd_idx=0 mtdparts=m25p80:256k(boot),128k(pib),1024k(userdata),128k(db),128k(log),128k(dbbackup),128k(logbackup),3072k(kernel),11264k(rootfs)
PID hash table entries: 256 (order: -2, 1024 bytes)
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 48600k/65536k available (3844k kernel code, 16936k reserved, 888k data, 192k init, 0k highmem)
SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
Preemptible hierarchical RCU implementation.
NR_IRQS:49
Calibrating delay loop... 498.89 BogoMIPS (lpj=2494464)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
pinctrl core: initialized pinctrl subsystem
regulator-dummy: no parameters
NET: Registered protocol family 16
rtsxb2 registered with IRQs
INFO: initializing USB host ...
INFO: initializing spi host ...0
spi platform id is 0
INFO: initializing I2C master ...
INFO: initializing DMA controller ...
INFO: initializing SD controller ...
INFO: initializing snd device ...
snd resvd mem size : 1048576
INFO: initializing pinctrl device ...
pinctrl_platform pinctrl_platform: rtspc registered with IRQs
INFO: initializing ethernet devices ...
INFO: initializing dwc_otg devices ...
INFO: initializing USB phy ...
INFO: initializing ISP device ...
isp resvd mem addr : 0x005f3000, size : 0xa00000
ISP camera platform devices added
INFO: initializing watchdog controller ...
INFO: initializing PWM controller ...
INFO: initializing crypto device ...
INFO: initializing pmu device ...
bio: create slab <bio-0> at 0
rts_dmac rts_dmac: DesignWare DMA Controller, 1 channels
INFO: realtek DMA engine inited
SCSI subsystem initialized
spic-platform spic-platform.0: master is unqueued, this is deprecated
INFO:allocate spi master 0, 0
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
usbphy-platform usbphy-platform: Initialized Realtek IPCam USB Phy module
Linux video capture interface: v2.00
Advanced Linux Sound Architecture Driver Initialized.
Bluetooth: Core ver 2.16
NET: Registered protocol family 31
Bluetooth: HCI device and connection manager initialized
Bluetooth: HCI socket layer initialized
Bluetooth: L2CAP socket layer initialized
Bluetooth: SCO socket layer initialized
NET: Registered protocol family 2
TCP established hash table entries: 512 (order: 0, 4096 bytes)
TCP bind hash table entries: 512 (order: -1, 2048 bytes)
TCP: Hash tables configured (established 512 bind 512)
TCP: reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
squashfs: version 4.0 (2009/01/31) Phillip Lougher
NFS: Registering the id_resolver key type
Key type id_resolver registered
Key type id_legacy registered
jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
msgmni has been set to 94
NET: Registered protocol family 38
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
Serial: 8250/16550 driver, 3 ports, IRQ sharing disabled
serial8250: ttyS0 at MMIO 0x18810000 (irq = 6) is a 16550A
console [ttyS1] enabled, bootconsole disabled
console [ttyS1] enabled, bootconsole disabled
serial8250: ttyS1 at MMIO 0x18810100 (irq = 6) is a 16550A
serial8250: ttyS2 at MMIO 0x18810200 (irq = 6) is a 16550A
dbg_iomem initialized!
m25p80 spi0.0: unrecognized id mx25l12845e
m25p80 spi0.0: found w25q128fv, expected m25p80
m25p80 spi0.0: w25q128fv (16384 Kbytes)
9 cmdlinepart partitions found on MTD device m25p80
Creating 9 MTD partitions on "m25p80":
0x000000000000-0x000000040000 : "boot"
0x000000040000-0x000000060000 : "pib"
0x000000060000-0x000000160000 : "userdata"
0x000000160000-0x000000180000 : "db"
0x000000180000-0x0000001a0000 : "log"
0x0000001a0000-0x0000001c0000 : "dbbackup"
0x0000001c0000-0x0000001e0000 : "logbackup"
0x0000001e0000-0x0000004e0000 : "kernel"
0x0000004e0000-0x000000fe0000 : "rootfs"
invalid hconf_mtd_idx!
hconf init failed
rtl8168 Gigabit Ethernet driver 8.038.00-NAPI loaded
rtl8168 rtl8168 (unregistered net_device): Get invalid MAC address from flash!
eth%d: 0xb8400000, 00:00:00:00:00:00, IRQ 10
PPP generic driver version 2.4.2
PPP MPPE Compression module registered
NET: Registered protocol family 24
ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
ehci-rts: ehci-rts platform driver
ehci-platform ehci-platform: EHCI Host Controller
ehci-platform ehci-platform: new USB bus registered, assigned bus number 1
ehci-platform ehci-platform: irq 11, io mem 0x18100000
ehci-platform ehci-platform: USB 2.0 started, EHCI 1.00
usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
usb usb1: Product: EHCI Host Controller
usb usb1: Manufacturer: Linux 3.10.27 ehci_hcd
usb usb1: SerialNumber: ehci-platform
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
ohci-platform ohci-platform: Generic Platform OHCI Controller
ohci-platform ohci-platform: new USB bus registered, assigned bus number 2
ohci-platform ohci-platform: irq 11, io mem 0x18180000
usb usb2: New USB device found, idVendor=1d6b, idProduct=0001
usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
usb usb2: Product: Generic Platform OHCI Controller
usb usb2: Manufacturer: Linux 3.10.27 ohci_hcd
usb usb2: SerialNumber: ohci-platform
hub 2-0:1.0: USB hub found
hub 2-0:1.0: 1 port detected
dwc_otg: version 3.10b 20-MAY-2013
Core Release: 3.10a
Setting default values for core params
WARN::dwc_otg_set_param_dev_tx_fifo_size:6354: Value is larger then power-on FIFO size

WARN::dwc_otg_set_param_dev_tx_fifo_size:6354: Value is larger then power-on FIFO size

Using Buffer DMA mode
Periodic Transfer Interrupt Enhancement - disabled
Multiprocessor Interrupt Enhancement - disabled
OTG VER PARAM: 0, OTG VER FLAG: 0
Shared Tx FIFO mode
usbcore: registered new interface driver usb-storage
g_mass_storage gadget: Mass Storage Function, version: 2009/09/11
g_mass_storage gadget: Number of LUNs=1
 lun0: LUN: removable file: (no medium)
g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
g_mass_storage gadget: g_mass_storage ready
usb device pull 1
i2c /dev entries driver
Unable to read RTP_REG_CHIP_VERSION reg
rtp_mfd 0-0030: pre_init() failed: -140
rtp_mfd: probe of 0-0030 failed with error -140
Stopped watchdog timer
timer margin: 8 sec
nf_conntrack version 0.5.0 (759 buckets, 3036 max)
ip_tables: (C) 2000-2006 Netfilter Core Team
TCP: cubic registered
NET: Registered protocol family 17
Bluetooth: RFCOMM TTY layer initialized
Bluetooth: RFCOMM socket layer initialized
usb 1-1: new high-speed USB device number 2 using ehci-platform
Bluetooth: RFCOMM ver 1.11
Bluetooth: BNEP (Ethernet Emulation) ver 1.3
Bluetooth: BNEP filters: protocol multicast
Bluetooth: BNEP socket layer initialized
Key type dns_resolver registered
ALSA device list:
  No soundcards found.
VFS: Mounted root (squashfs filesystem) readonly on device 31:8.
Freeing unused kernel memory: 192K (804b0000 - 804e0000)
usb 1-1: New USB device found, idVendor=0bda, idProduct=b720
usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-1: Product: 802.11n WLAN Adapter
usb 1-1: Manufacturer: Realtek
usb 1-1: SerialNumber: 00e04c000001
init started: BusyBox v1.22.1 (2018-06-06 03:10:44 CST)
starting pid 54, tty '': '/etc/rc.d/rcS start'
mount: mounting none on /proc/bus/usb failed: No such file or directory
rm: can't remove '/dev/mtd9': No such file or directory
mknod: /dev/console: File exists
soc-audio soc-audio.0: ASoC: machine RLX_INTERN_CARD should use snd_soc_register_card()
soc-audio soc-audio.0:  rlx-codec-digital <-> pcm-platform mapping ok
soc-audio soc-audio.0:  rlx-codec-analog <-> pcm-platform mapping ok
pinctrl_platform pinctrl_platform: request() failed for pin 0
pinctrl_platform pinctrl_platform: pin-0 (pinctrl-rts:0) status -16
request GPIO failed
sd-platform: probe of rts3901-sdhc failed with error -16
rtscam:rtscam_soc_probe
rtscam:rtscam_hx280_probe
rtscam:hx280enc:HW at base <0x18060000> with ID <0x48314810>
rtscam:rtscam_jpgenc_probe
rtscam:rtstream_init
rtscam:begin to load fw from isp.fw
rtscam:Load firmware size : 131072.
rtscam:Found ISP 1.006 device
rtscam:video device <rts3901-isp> registered
rtscam:rts3901-isp initialized
Setup db... ok.
Startting dbd... Password for 'root' changed
ok.
set the date to default:
Wed Jun  6 00:00:00 UTC 2018
No SD Device Path Exists.
rc.sysinit start ok.
============ normal mode ===============
dbd(181) is already running.
Startting tz_dst... ok.
setsystz ok
Startting watchDog... ok.
Startting avcd... 
mic vol = 80
avcd ok.
starting create_certificate...get server.pem... ok.
Startting dbus-daemon... ok.
Startting bluetoothd... sendCmd : 0
Open /tmp/ap_list fail: No such file or directory
main[283] Fail to get channel of Kjellerbod network
configure : 
 			Wireless : essid: Kjellerbod, encryp_method: AES, auth_method: WPA2PSK 
 			Network  : dhcp_enable: 1, hostname: DCS-8000LH 
Open /proc/sys/net/ipv6/conf/wlan0/autoconf fail: No such file or directory
killall: rtspd: no process killed
killall: udhcpc: no process killed
killall: wifiAutoReconnect: no process killed
sendCmd : 0
sendCmd : 0
/bin/sh: dibbler-client: not found
killall: orthrus: no process killed
killall: orthrusipv6: no process killed
killall: pppd: no process killed
killall: zcip: no process killed
wlan1 MAC [b2:c5:54:4c:cc:73]
23186 wpa_supplicant -B -c /tmp/wpa_supplicant.conf -i wlan0 -P /tmp/wpa_supplicant.pid
rfkill: Cannot open RFKILL control device
ioctl[SIOCSIWAP]: Operation not permitted
udhcpc (v1.22.1) started
/sbin/udhcpc.sh: line 1: /etc/rc.d/init.d/zcip.sh: not found
Sending discover...
Sending discover...
Sending select for 192.168.2.37...
Lease of 192.168.2.37 obtained, lease time 432000
ifdown: interface wlan1 not configured
cat: can't open '/tmp/wifi-led.pid': No such file or directory
sh: you need to specify whom to kill
deleting routers
route: ioctl 0x890c failed: No such process
adding dns 148.122.16.253
adding dns 148.122.164.253
start network services, ...
Startting mDNSResponder... ok.
Starting rtspd... ok.
Startting ntpd... disabled.
Startting firewall...ok.
/etc/rc.d/rcS: /etc/rc.d/rcS.d/S24network.sh: line 5: /etc/rc.d/init.d/network_services_ipv6.sh: not found
Starting Apple Darwin Multicast DNS / DNS Service Discovery daemon: mdnsd.
Jun  6 02:00:32 mDNSResponder: mDNSResponder (Engineering Build) (Jun  6 2018 03:55:36) starting
Jun  6 02:00:32 mDNSResponder: mDNS_AddDNSServer: Lock not held! mDNS_busy (0) mDNS_reentrancy (0)
Jun  6 02:00:32 mDNSResponder: mDNS_AddDNSServer: Lock not held! mDNS_busy (0) mDNS_reentrancy (0)
Jun  6 02:00:32 mDNSResponder: WARNING: mdnsd continuing as root because user "nobody" does not exist
Startting ntpd... disabled.
Startting db_analysis... ok.
Startting firewall...ok.
rtspd(1011 878) is already running.
Startting myDlinkEvent... ok.
2018-06-06 02:00:37 | INFO    | tcp_listen               |  176| listening 127.0.0.1:7000
2018-06-06 02:00:37 | INFO    | http_listen              |   40| waiting new connections ...
rc.local start ok.
starting pid 1157, tty '': '/bin/busybox getty -L ttyS1 57600 vt100'

localhost login: May  6 22:34:35 mDNSResponder: mDNS_Execute: mDNSPlatformRawTime went backwards by 438780374 ticks; setting correction factor to -1542198966
May  6 22:34:37 mDNSResponderPosix: mDNSCoreReceive: mDNSPlatformRawTime went backwards by 438777274 ticks; setting correction factor to 1206127822
```


### <a name="Partitions"></a>Partitions

The D-Link DCS-8000LH partitions are:
```
# cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 00040000 00010000 "boot"
mtd1: 00020000 00010000 "pib"
mtd2: 00100000 00010000 "userdata"
mtd3: 00020000 00010000 "db"
mtd4: 00020000 00010000 "log"
mtd5: 00020000 00010000 "dbbackup"
mtd6: 00020000 00010000 "logbackup"
mtd7: 00300000 00010000 "kernel"
mtd8: 00b00000 00010000 "rootfs"
```
Or as seen by the driver with start and end addresses:

```
9 cmdlinepart partitions found on MTD device m25p80
Creating 9 MTD partitions on "m25p80":
0x000000000000-0x000000040000 : "boot"
0x000000040000-0x000000060000 : "pib"
0x000000060000-0x000000160000 : "userdata"
0x000000160000-0x000000180000 : "db"
0x000000180000-0x0000001a0000 : "log"
0x0000001a0000-0x0000001c0000 : "dbbackup"
0x0000001c0000-0x0000001e0000 : "logbackup"
0x0000001e0000-0x0000004e0000 : "kernel"
0x0000004e0000-0x000000fe0000 : "rootfs"
```

Partition usage:

 | number | name        | start    | end      | size     | fstype   | contents          |
 | ------ | ----------- | -------- | -------- | -------- | -------- | ---------------   |
 | 0      | "boot"      | 0x000000 | 0x040000 | 0x40000  | boot     | U-Boot            |
 | 1      | "pib"       | 0x040000 | 0x060000 | 0x20000  | raw      | device info       |
 | 2      | "userdata"  | 0x060000 | 0x160000 | 0x100000 | squashfs | mydlink (/opt)    |
 | 3      | "db"        | 0x160000 | 0x180000 | 0x20000  | tar.gz   | non-volatile data |
 | 4      | "log"       | 0x180000 | 0x1a0000 | 0x20000  | raw?     | empty             |
 | 5      | "dbbackup"  | 0x1a0000 | 0x1c0000 | 0x20000  | tar.gz   | copy of "db"      |
 | 6      | "logbackup" | 0x1c0000 | 0x1e0000 | 0x20000  | raw?     | empty             |
 | 7      | "kernel"    | 0x1e0000 | 0x4e0000 | 0x300000 | uImage   | Linux 3.10        |
 | 8      | "rootfs"    | 0x4e0000 | 0xfe0000 | 0xb00000 | squashfs | rootfs (/)        |


The D-Link firmware updates I have looked at will replace the
"userdata", "kernel" and "rootfs" partitions, but leave other
partitions unchanged. I imagine that the "boot" partition might be
upgraded too if deemed necessary by D-Link. But it was not touched
when going from 2.01.03 to 2.02.02.

The "log" and "logbackup" appear to be currently unused.  But I am
reluctant trusting this, given their names.  I guess they could be
cleaned and overwritten anytime.  They are too small to be very useful
anyway.  You can't put any writable file system om them with only two
erase blocks.


### Backing up dynamic data

This is not necessary for system operation as any non-volatile data is
saved in the [**db**](#Partitions) partition anyway.  But it can still be useful to
have a copy of the system state for offline studying, so I also like
to save a working copy of /tmp:
```
tar zcvf /tmp/tmp.tgz /tmp/
tftp -l /tmp/tmp.tgz -r tmp.tgz -p 192.168.2.1
```


### Why can we run the NIPCA webserver before we modify the firmware?

D-Link left all the webserver parts in the firmware, including all the
NIPCA CGI tools. The only change they made was disabling the startup
script.

The webserver can be enabled and started manually from the shell by
running:

```
tdb set HTTPServer Enable_byte=1
/etc/rc.d/init.d/extra_lighttpd.sh start
```

This is precisely what our Bluetooth tool does when it is called with
the **--lighttpd** option.

The `HTTPServer Enable_byte` is persistent, so setting is only
necessary once. Unless you do a factory reset.


### What's the problem with the RTSP server in the unmodified firmware?

The original D-Link firmware is already running **rtspd**, but it is only
listening on the loopback address 127.0.0.1.  It is probably intended
as a backend server for the **mydlink** services.

We can make rtspd listen on all addresses by clearing the **RTPServer
RejectExtIP** setting.  Both rtspd and the firewall need a restart for
this to have an effect.  Enabling **RTPServer Authenticate** is
probably a good idea when doing this, to prevent the camera from
streaming to anyone who can connect.

```
tdb set RTPServer RejectExtIP_byte=0
tdb set RTPServer Authenticate_byte=1
/etc/rc.d/init.d/firewall.sh reload
/etc/rc.d/init.d/rtspd.sh restart
```

These settings are persistent as usual, so they only need to be
modified after factory resets. Changing the settings and then
rebooting the camera will therefore enable remote RTSP access, since
both services are running by default in the D-Link firmware.


### The "userdata" file system

The [**userdata**](#Partitions) you backed up as **mtd2** contains a xz compressed
squasfs file system, with most of the mydlink cloud tools. The file
system can be unpacked on a Linux system using unsquashfs:
```
$ unsquashfs mtd2
Parallel unsquashfs: Using 4 processors
15 inodes (22 blocks) to write

[=============================================================================================================================================================================================================|] 22/22 100%

created 12 files
created 1 directories
created 3 symlinks
created 0 devices
created 0 fifos
$ ls -la squashfs-root/
total 1156
drwxr-xr-x  2 bjorn bjorn    340 Feb 14 10:58 .
drwxrwxrwt 41 root  root    2280 May 13 15:13 ..
-rwxr-xr-x  1 bjorn bjorn  13184 Feb 14 10:58 ca-refresh
-rwxr-xr-x  1 bjorn bjorn 273692 Feb 14 10:58 cda
lrwxrwxrwx  1 bjorn bjorn      9 May 13 15:13 cert -> /tmp/cert
-rwxr-xr-x  1 bjorn bjorn   5991 Feb 14 10:58 client-ca.crt.pem
lrwxrwxrwx  1 bjorn bjorn      7 May 13 15:13 config -> /tmp/db
-rwxr-xr-x  1 bjorn bjorn 436428 Feb 14 10:58 da_adaptor
-rwxr-xr-x  1 bjorn bjorn      4 Feb 14 10:58 dcp_version
-rwxr-xr-x  1 bjorn bjorn    814 Feb 14 10:58 device.cfg
lrwxrwxrwx  1 bjorn bjorn     17 May 13 15:13 lib -> /var/libevent/lib
-rwxr-xr-x  1 bjorn bjorn      5 Feb 14 10:58 m2m
-rwxr-xr-x  1 bjorn bjorn   6220 Feb 14 10:58 mydlink_watchdog.sh
-rwxr-xr-x  1 bjorn bjorn   1034 Feb 14 10:58 opt.local
-rwxr-xr-x  1 bjorn bjorn 171828 Feb 14 10:58 sa
-rwxr-xr-x  1 bjorn bjorn 242028 Feb 14 10:58 strmsvr
-rwxr-xr-x  1 bjorn bjorn     10 Feb 14 10:58 version
```

The primary entry point here is the **opt.local** init-script.  This
is also the only required file.  The **version** file is read by the
Bluetooth API, and reported as the mydlink version, which makes it
useful for verifying a modified camera.  Our alternate
[**userdata**](#Partitions) file system contains only these two
files. But one could imagine including a number of other useful tools,
like tcpdump, a ssh server etc.

It is also possible to keep all the D-Link files, if that's
wanted. The original **opt.local** script can be modified to leave
mydlink support running while still starting other features.  We could
even add our own non-volatile setting to choose one or the other, or
both, and making it a configuration thing. Fantasy is the only
limiting factor.

Repacking the files into a camera compatible squashfs file system:
```
mksquashfs squashfs-root mtd2.new -all-root -comp xz
```

Note that **xz** compression is required.  No other compression is
supported AFAIK.

There are simpler ways to write the new file system to the camera than
creating a firmware update package, if you just want to test it. One
example:

```
tftp -r mtd2.new -l /tmp/mtd2.new -g 192.168.2.1
cat /tmp/mtd2.new >/dev/mtdblock2 
```

But DON'T do that unless you both have a backup and know what you are
doing...

You should reboot the camera after doing this, unless you make sure
you stop any process running from the previous /opt system and remount
it properly.


### <a name="NIPCA"></a>Using NIPCA to manage the camera

The local web server provides a direct camera management API, but not
a web GUI application. All API requests require authentication. We
have added a single admin user, using the pincode from the camera
label as passord.  More users can be adding if necessary, even by
using the API itself.

Read the NIPCA reference spec for usage, or look at the script names
under **/var/www** in the [**rootfs**](#Partitions) and simply try
them out. Most API endpoints return a list of current settings. Some
of the settings can be set by GET requests by providing the new values
as URL parameters.

A few NIPCA references of different age:
 * http://gurau-audibert.hd.free.fr/josdblog/wp-content/uploads/2013/09/CGI_2121.pdf
 * https://docplayer.net/33354138-Network-ip-camera-application-programming-interface-nipca.html
 * ftp://ftp.dlink.net.pl/dcs/dcs-2132L/documentation/DCS-2132L_NIPCA_support table_1-9-5_20131211.pdf
 * https://www.airlivecam.eu/data/IP%20Camera%20Open%20API.doc

Google for more. Be aware that a most of these settings depend on the
hardware.  There is obviously no point in trying to manage an SD card
slot of the DCS-8000LH...

A few of examples, using curl to read and set configuration variables:
```
$ curl -u admin:123456 http://192.168.2.37/common/info.cgi
model=DCS-8000LH
product=Wireless Internet Camera
brand=D-Link
version=2.02
build=02
hw_version=A
nipca=1.9.7
name=DCS-8000LH
location=
macaddr=B0:C5:54:AA:BB:CC
ipaddr=192.168.2.37
netmask=255.255.255.0
gateway=192.168.2.1
wireless=yes
inputs=0
outputs=0
speaker=no
videoout=no
pir=no
icr=yes
ir=yes
mic=yes
led=no
td=no
playing_music=no
whitelightled=no

$ curl -u admin:123456 'http://192.168.2.37/config/datetime.cgi' 
method=1
timeserver=ntp1.dlink.com
timezone=1
utcdate=2019-05-09
utctime=13:25:14
date=2019-05-09
time=15:25:14
dstenable=yes
dstauto=yes
offset=01:00
starttime=3.2.0/02:00:00
stoptime=11.1.0/02:00:00

$ curl -u admin:123456 http://192.168.2.37/config/led.cgi?led=off
led=off
```

Most camera settings can be controlled using this API and e.g curl for
the command line.  There are also packages implementing API clients,
like for example this nodejs one: https://www.npmjs.com/package/nipca



### Bluetooth LE GATT API

The Bluetooth service is in a "locked" mode by default. This is
controlled by the "Ble Mode" persistent setting stored in the **db**
partition. If true ("1"), then most of the Bluetooth commands are
rejected.  But changing the setting manually will not help much, since
the system automatically enter lock mode 180 seconds after the last
Bluetooth client disconnected.

The challenge -> response unlock method described below is much more
useful.


#### Converting the PIN Code to a Bluetooth unlock key

Most Bluetooth commands are rejected when locked.  Access to the full
Bluetooth API can be unlocked by using the PIN Code printed on the
camera label.  This code is not sent directly over the air
though. Instead it is combined with a random challenge.

Both the random challenge and the matching key are generated by the
application `sbin/gen_bt_config` on the camera side.  The key is
calculated by taking the first 16 bytes of the base64 encoded md5
digest of

 * model string + '-'  four last mac digits (or Bluetooth device name?)
 * PIN Code
 * challenge.

Note that this application depends on bluetooth libraries, which are
not in /lib. So we have to set LD\_LIBRARY\_PATH to run it manually:

```
# LD_LIBRARY_PATH=/var/bluetooth/lib sbin/gen_bt_config update_key_only
In main:182: modelStr = 'DCS-8000LH'
In main:183: mac = 'b0:c5:54:ab:cd:ef'
In update_ble_key:87: key data = 'DCS-8000LH-CDEF012345b2gaescrbldchnik'
```

I've slightly obfuscated my data here - the pincode in the above case
is `012345`, and the dynamically generated challenge is
`b2gaescrbldchnik`. The generated challenge and key are stored in
`/tmp/db/db.xml` and can be read directly from there:
```
# grep Key /tmp/db/db.xml |tail -2
<ChallengeKey type="3" content="b2gaescrbldchnik" />
<Key type="5" content="jrtY6nONQ5rV+2Ph" />
```

Or you can read them using the same tools the Bluetooth system uses:
```
# tdb get Ble ChallengeKey_ss
b2gaescrbldchnik
# mdb get ble_key
jrtY6nONQ5rV+2Ph
```

Yes, the D-Link code does actually use tdb for the first one and mdb
for the second.  I have absolutely no idea why,... It is possible to
read the key using tdb too:

```
# tdb get Ble Key_ss
jrtY6nONQ5rV+2Ph
```

Generating the same key by hand on a Linux system is simple:

```
$ echo -n 'DCS-8000LH-CDEF012345b2gaescrbldchnik' | md5sum | xxd -r -p | base64 | cut -c-16
jrtY6nONQ5rV+2Ph
```

#### Characteristic UUIDs

D-Link is using the GATT BlueZ example plugin, patching it to add
their camera specific endpoints.  This means that we can find all the
API "documentation" in the
`DCS-8000LH-GPL/package/bluez_utils/feature-patch/5.28/customized-mydlink.patch`
file in the GPL archive.

This defines a number of 16bit UUIDs with mostly nonsense names:
```
+#define IPCAM_UUID		0xD001
+#define A000_UUID		0xA000
+#define A001_UUID		0xA001
+#define A100_UUID		0xA100
+#define A101_UUID		0xA101
+#define A102_UUID		0xA102
+#define A103_UUID		0xA103
+#define A104_UUID		0xA104
+#define A200_UUID		0xA200
+#define A201_UUID		0xA201
+#define A300_UUID		0xA300
+#define A301_UUID		0xA301
+#define A302_UUID		0xA302
+#define A303_UUID		0xA303
+#define A304_UUID		0xA304
```


`IPCAM_UUID` is registered as the `GATT_PRIM_SVC_UUID`, which means
that it shows up as a primary GATT service we can look for when
looking for a supported camera.

The rest of the UUIDs are characteristics of this primary service. The
API is based on reading or writing these characteristics.


#### Data formatting

Both input and output parameters are sent as ascii strings using
key=value pairs joined by `;`, with an exception for the nested KV
pairs in the WiFi survey results.  All keys are single upper case
characters. Key names are somewhat reused, so the exact meaning depend
on the characteristic.

Values are either integers, including boolean 0/1, or some set of
ascii text.

Three real examples, read from 0xA001, 0xA200 and 0xA104:
```
M=1;C=b2gaescrbldchnik
N=DCS-8000LH;P=1;T=1557349762;Z=CET-1CEST,M3.5.0,M10.5.0/3;F=2.01.03;H=A1;M=B0C554ABCDEF;V=3.0.0-b71
I=192.168.2.37;N=255.255.255.0;G=192.168.2.1;D=148.122.16.253
```

#### Listing characteristics


The **gattool** Linux command line tool is useful for exploring
Bluetooth LE devices.  You can look for primary services and list
associated characteristics of a service:
```
[B0:C5:54:AA:BB:CC][LE]> primary 
attr handle: 0x0001, end grp handle: 0x0008 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0010, end grp handle: 0x0010 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0011, end grp handle: 0x002e uuid: 0000d001-0000-1000-8000-00805f9b34fb
[B0:C5:54:AA:BB:CC][LE]> characteristics 0x0011
handle: 0x0012, char properties: 0x12, char value handle: 0x0013, uuid: 0000a000-0000-1000-8000-00805f9b34fb
handle: 0x0015, char properties: 0x0a, char value handle: 0x0016, uuid: 0000a001-0000-1000-8000-00805f9b34fb
handle: 0x0017, char properties: 0x02, char value handle: 0x0018, uuid: 0000a100-0000-1000-8000-00805f9b34fb
handle: 0x0019, char properties: 0x0a, char value handle: 0x001a, uuid: 0000a101-0000-1000-8000-00805f9b34fb
handle: 0x001b, char properties: 0x08, char value handle: 0x001c, uuid: 0000a102-0000-1000-8000-00805f9b34fb
handle: 0x001d, char properties: 0x02, char value handle: 0x001e, uuid: 0000a103-0000-1000-8000-00805f9b34fb
handle: 0x001f, char properties: 0x02, char value handle: 0x0020, uuid: 0000a104-0000-1000-8000-00805f9b34fb
handle: 0x0021, char properties: 0x0a, char value handle: 0x0022, uuid: 0000a200-0000-1000-8000-00805f9b34fb
handle: 0x0023, char properties: 0x08, char value handle: 0x0024, uuid: 0000a201-0000-1000-8000-00805f9b34fb
handle: 0x0025, char properties: 0x0a, char value handle: 0x0026, uuid: 0000a300-0000-1000-8000-00805f9b34fb
handle: 0x0027, char properties: 0x02, char value handle: 0x0028, uuid: 0000a301-0000-1000-8000-00805f9b34fb
handle: 0x0029, char properties: 0x08, char value handle: 0x002a, uuid: 0000a302-0000-1000-8000-00805f9b34fb
handle: 0x002b, char properties: 0x08, char value handle: 0x002c, uuid: 0000a303-0000-1000-8000-00805f9b34fb
handle: 0x002d, char properties: 0x02, char value handle: 0x002e, uuid: 0000a304-0000-1000-8000-00805f9b34fb
```

It is also possible to read and write characteristics using this tool,
but this can be a bit cumbersome unless you are fluent in ASCII coding
;-)



#### The IPCam characteristics

Guessed meanings of each characteristic, based on the source code and
some trial and error. Not necessarily how D-Link would describe them:


| UUID | op     | description     | format                                  | keys                                                                                                            |
| ---- | ------ | --------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| A000 | read   | last status     | C=%d;A=%d;R=%d                          | C: uuid, A: mode, R: state                                                                                      |
| A000 | notify | last status     | C=%d;A=%d;R=%d                          | C: uuid, A: mode, R: state                                                                                      |
| A001 | read   | challenge       | M=%d;C=%s                               | M: opmode, C: challenge                                                                                         |
| A001 | write  | auth            | M=%d;K=%s                               | M: opmode, K: key                                                                                               |
| A100 | read   | wifi survey     | N=%d;P=%d;...                           |                                                                                                                 |
| A101 | read   | wifi config     | M=%s;I=%s;S=%s;E=%s                     | M: opmode, I: essid, S: 4 , E: 2                                                                                |
| A101 | write  | wifi config     | M=%s;I=%s;S=%s;E=%s;K=%s                | M: opmode, I: essid, S: 4 , E: 2, K: password                                                                   |
| A102 | write  | wifi connect    | C=%d                                    | C: connect (0/1)                                                                                                |
| A103 | read   | wifi status     | S=%d                                    | S: wifi link status (0,1,?)                                                                                     |
| A104 | read   | ip config       | I=%s;N=%s;G=%s;D=%s                     | I: address, N: netmask, G: gateway, D: DNS-server                                                               |
| A200 | read   | system info     | N=%s;P=%d;T=%d;Z=%s;F=%s;H=%s;M=%s;V=%s | N: devicename, P: haspin (0/1), T: time (unix epoch), Z: timezone, F: fwver, H: hwver, M: macaddr, V:mydlinkver |
| A200 | write  | name and time   | N=%s;T=%d;Z=%s                          | N: devicename, T: time (unix epoch), Z: timezone                                                                |
| A201 | write  | admin password  | P=%s;N=%s                               | P: current password, N: new password                                                                            |
| A300 | read   | reg state       | G=%d                                    | G: registration state (0/1)                                                                                     |
| A300 | write  | reg state       | G=%d                                    | G: registration state (0/1)                                                                                     |
| A301 | read   | provisioning    | N=%s;T=%s;U=%s                          | N: username, T: footprint, U: portal                                                                            |
| A302 | write  | restart mydlink | C=%d                                    | C: restart (0/1)                                                                                                |
| A303 | write  | register        | S=%s;M=%s                               | S: , M:  (written to /tmp/mydlink/reg_info, and then kill -USR1 `pidof da_adaptor`)                             |
| A304 | read   | register        | S=%d;E=%d                               | S: , E:  (cat /tmp/mydlink/reg_st)                                                                              |


The UUIDs from 0xA300 to 0xA304 are all related to the mydlink cloud
service, and therefore not of much use to us.  I haven't bothered
trying to figure out exactly how they are used.

We could in theory use the 0xA303 request which simply calls
**/opt/opt.local restart**.  But with the gaping 0xA201 hole,
allowing **any** command, there isn't much need for this one...

A few more details on the more complex characteristics:


##### A000

The only characteristic sent as notifications.  But it can also be
read directly for syncronous operations.

The value is the state to the last Bluetooth action:

	"C=%d;A=%d;R=%d", last_action_status.uuid, last_action_status.mode, last_action_status.state


##### A100

The wifi survey scan results are split in 128 byte "pages", where each
page starts with the total number of pages and the current page
number.  The characteristic value must be read as many times as the
given total.

For example, reading 3 pages:
```
[B0:C5:54:AA:BB:CC][LE]> char-read-hnd 0x0018
Characteristic value/descriptor: 4e 3d 33 3b 50 3d 31 3b 4c 3d 49 3d 41 6e 74 69 62 6f 6b 73 2c 4d 3d 30 2c 43 3d 36 2c 53 3d 34 2c 45 3d 32 2c 50 3d 36 32 26 4c 3d 49 3d 41 53 56 31 37 2c 4d 3d 30 2c 43 3d 31 31 2c 53 3d 34 2c 45 3d 32 2c 50 3d 34 36 26 4c 3d 49 3d 41 53 56 31 37 2d 64 6c 69 6e 6b 2c 4d 3d 30 2c 43 3d 36 2c 53 3d 34 2c 45 3d 32 2c 50 3d 36 38 26 4c 3d 49 3d 66 6a 6f 72 64 65 31 32 33 2c 4d 3d 30 
[B0:C5:54:AA:BB:CC][LE]> char-read-hnd 0x0018
Characteristic value/descriptor: 4e 3d 33 3b 50 3d 32 3b 2c 43 3d 31 2c 53 3d 34 2c 45 3d 32 2c 50 3d 35 38 26 4c 3d 49 3d 4a 4f 4a 2c 4d 3d 30 2c 43 3d 31 31 2c 53 3d 34 2c 45 3d 32 2c 50 3d 34 37 26 4c 3d 49 3d 4b 6a 65 6c 6c 65 72 62 6f 64 2c 4d 3d 30 2c 43 3d 36 2c 53 3d 34 2c 45 3d 32 2c 50 3d 36 32 26 4c 3d 49 3d 6d 67 6d 74 2c 4d 3d 30 2c 43 3d 31 2c 53 3d 34 2c 45 3d 32 2c 50 3d 37 34 26 4c 3d 49 3d 52 69 
[B0:C5:54:AA:BB:CC][LE]> char-read-hnd 0x0018
Characteristic value/descriptor: 4e 3d 33 3b 50 3d 33 3b 6e 64 65 64 61 6c 2c 4d 3d 30 2c 43 3d 31 31 2c 53 3d 34 2c 45 3d 32 2c 50 3d 36 32 
```

These strings are decoded as:
```
N=3;P=1;L=I=Antiboks,M=0,C=6,S=4,E=2,P=62&L=I=ASV17,M=0,C=11,S=4,E=2,P=46&L=I=ASV17-dlink,M=0,C=6,S=4,E=2,P=68&L=I=fjorde123,M=0
N=3;P=2;,C=1,S=4,E=2,P=58&L=I=JOJ,M=0,C=11,S=4,E=2,P=47&L=I=Kjellerbod,M=0,C=6,S=4,E=2,P=62&L=I=mgmt,M=0,C=1,S=4,E=2,P=74&L=I=Ri
N=3;P=3;ndedal,M=0,C=11,S=4,E=2,P=62
```

Which, when joined after removing the N/P paging info, becomes::
```
L=I=Antiboks,M=0,C=6,S=4,E=2,P=62&L=I=ASV17,M=0,C=11,S=4,E=2,P=46&L=I=ASV17-dlink,M=0,C=6,S=4,E=2,P=68&L=I=fjorde123,M=0,C=1,S=4,E=2,P=58&L=I=JOJ,M=0,C=11,S=4,E=2,P=47&L=I=Kjellerbod,M=0,C=6,S=4,E=2,P=62&L=I=mgmt,M=0,C=1,S=4,E=2,P=74&L=I=Rindedal,M=0,C=11,S=4,E=2,P=62
```

And after splitting this on & we get the final result:
```
L=I=Antiboks,M=0,C=6,S=4,E=2,P=62
L=I=ASV17,M=0,C=11,S=4,E=2,P=46
L=I=ASV17-dlink,M=0,C=6,S=4,E=2,P=68
L=I=fjorde123,M=0,C=1,S=4,E=2,P=58
L=I=JOJ,M=0,C=11,S=4,E=2,P=47
L=I=Kjellerbod,M=0,C=6,S=4,E=2,P=62
L=I=mgmt,M=0,C=1,S=4,E=2,P=74
L=I=Rindedal,M=0,C=11,S=4,E=2,P=62
```

So each L entry is made up of the same set of keys:

 * I: essid
 * M: opmode? or authalg? (always 0 in the sample)
 * C: channel (2.4 GHz only)
 * S: key_mgmt/auth_alg/proto?
 * E: key_mgmt/auth_alg/proto?
 * P: relative signal. Higher is better. dBm + 100?

Still need to figure out the mapping of the M,S,E keys to
wpa_supplicant config settings. I assume they represent enums.  But we
can simply treat them as opaque values since we only use the survey
data to help setup WiFi anyway. We copy these to the setup request,
and do not need to know what they mean.


FWIW, my example setting `M=0;I=Kjellerbod;S=4;E=2`
is mapped to this wpa_supplicant configuration:
```
# cat /tmp/wpa_supplicant.conf 
ctrl_interface=/var/run/wpa_supplicant
device_type=4-0050F204-3
model_name=DCS-8000LH
manufacturer=D-Link
os_version=01020300
config_methods=push_button virtual_push_button
eapol_version=1
network={
        scan_ssid=1
        ssid="Kjellerbod"
        key_mgmt=WPA-PSK
        auth_alg=OPEN
        proto=RSN
        psk="redeacted"
}
```

##### A201

This write request allows setting an admin password, used for example
by the webserver. It takes the old and new passwords as unencoded
input, verifies that the old password matches, and then change the
admin password to the provided new one.

The initial password is empty, which prevents webserver
authentication. Simply provide an empty string for the old password in
the first request: **P=;N=newpassword**

But this request is much more useful in other ways.... The new passord
(N_str) is processed like this (after slight compression of the
interesting code lines):

```C
	snprintf(cmd, sizeof(cmd), "mdb set admin_passwd %s", N_str);
	snprintf(cmdbuf, sizeof(cmdbuf), "%s > %s 2>&1", cmd, p_name);
	fp = popen(cmdbuf, "r");
```

You don't have to be a security expert to see the problem here. But
one mans bug is another mans feature :-)


##### A303

The two strings S and M are url decoded and checked for special
characters.  Then the **orginal** url encoded strings are written to
**/tmp/mydlink/reg_info** and SIGUSR1 is sent to the **da_adaptor**
process.  Presumably triggering it to reread the reg_info file.

It is pretty safe to assume that this provides some registration info
to the mydlink system, allowing it to connect to the cloud service.

The set of allowed characters is rather interesting:
```
 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
```

Which initially made me think that this was an obvious security hole,
since I missed the point that it's the url encoded strings that are
used on the command line.

But given the quality of the rest of the code here, I would be very
surprised if there isn't an issue or ten in the da_adaptor code
allowing this to be abused. It's just a bit harder to figure out
without the source code.



#### Manually restarting bluetoothd for debugging

It can be useful to run bluetoothd in the foreground when debugging
BLE interaction, or testing modified versions of bluetoothd.  Log in
using telnet or serial and simply stop and restart bluetoothd with the
-d and -n options:

```
# /etc/rc.d/init.d/bluetoothd.sh stop
Stopping bluetoothd... ok.
# LD_LIBRARY_PATH=/var/bluetooth/lib /var/bluetooth/bin/bluetoothd -d -n -E -p "gatt,gatt_example" 
bluetoothd[25020]: Bluetooth daemon 5.28
bluetoothd[25020]: src/main.c:parse_config() parsing main.conf
bluetoothd[25020]: src/main.c:parse_config() discovto=0
bluetoothd[25020]: src/main.c:parse_config() pairto=0
bluetoothd[25020]: src/main.c:parse_config() auto_to=3600
bluetoothd[25020]: src/main.c:parse_config() name=DCS-8000LH-CDEF
bluetoothd[25020]: src/main.c:parse_config() Key file does not have key 'Class'
bluetoothd[25020]: src/main.c:parse_config() Key file does not have key 'DeviceID'
bluetoothd[25020]: src/main.c:parse_config() Key file does not have key 'ReverseServiceDiscovery'
bluetoothd[25020]: src/main.c:parse_config() ControllerMode=le
bluetoothd[25020]: src/gatt.c:gatt_init() Starting GATT server
bluetoothd[25020]: src/adapter.c:adapter_init() sending read version command
bluetoothd[25020]: Starting SDP server
bluetoothd[25020]: src/sdpd-service.c:register_device_id() Adding device id record for 0002:1d6b:0246:051c
etc.
```

Note that the GATT GAP service will only pick up the device name
whenever it is set.  This causes an empty Device Name characteristic
(0x2a00) after restarting bluetoothd.

The name must therefore be changed and set again after every
bluetoothd restart as a workaround:

```
/var/bluetooth/bin/hciconfig hci0 name foo
/var/bluetooth/bin/hciconfig hci0 name DCS-8000LH-CDEF
```


### Firmware updates

There are at least two shell scripts providing a firmware update
service in the D-Link firmware:

 * /var/www/config/firmwareupgrade.cgi
 * /sbin/fwupdate

They are both pretty similar and obviously come from the same source.
The main difference is that firmwareupgrade.cgi provides the NIPCA
firmwareupgrade service, while fwupdate is a command line tool.

The web service is most interesting for us, providing both the upload
and upgrade in one simple tool.  The fwupdate tool is used by the
mydlink cloud tool **da_adaptor** , via an fw_upgrade symlink.


#### Downloading the latest OEM firmware

The mydlink tools will set up two URLs in `/tmp/db/device.cf`:
```
      "FirmwareDaemonURL": "http://mp-eu-fwd.auto.mydlink.com:80/fw-upgrade",
      "SecuredFirmwareDaemonURL": "https://mp-eu-fwd.auto.mydlink.com:443/fw-upgrade",
```

Calling either one of these with a model parameter will return a
download URL for the latest firmware version.  Examples:

```
$ curl -D - http://mp-eu-fwd.auto.mydlink.com:80/fw-upgrade?model=DCS-8000LH
HTTP/1.1 200 OK
Date: Sun, 27 Oct 2019 16:27:24 GMT
Content-Length: 280
Etag: "9a475c16361db8ba3ebfe679554680dbb6bf2c38"
Content-Type: text/html; charset=UTF-8
Server: TornadoServer/5.1.1

{"code": 0, "fw_ver_type": "REGULAR", "process_time": 210, "main_board": {"url": "http://mydlinkmpfw.auto.mydlink.com/DCS-8000LH/DCS-8000LH_Ax_v2.03.02_3412.bin", "fw_version": "2.03.02", "md5": "3e26e96fcd8fcf711c1362a4cbfb2d48"}, "release_version": "3.2.7-b02", "device_id": ""}
```


```
$ curl -D - 'https://mp-eu-fwd.auto.mydlink.com:443/fw-upgrade?model=DCS-8100LH'
HTTP/1.1 200 OK
Date: Sun, 27 Oct 2019 16:35:39 GMT
Content-Length: 281
Etag: "5714f3e62ca04e5c3465705cb4c7356f1d50e54f"
Content-Type: text/html; charset=UTF-8
Server: TornadoServer/5.1.1

{"code": 0, "fw_ver_type": "REGULAR", "process_time": 210, "main_board": {"url": "https://mydlinkmpfw.auto.mydlink.com/DCS-8100LH/DCS-8100LH_Ax_v2.04.01_3619.bin", "fw_version": "2.04.01", "md5": "eb097a680754b94b81da6326b468121f"}, "release_version": "3.2.7-b03", "device_id": ""}
```
 
Download and verify the md5 sum:
 
```
$ curl -O https://mydlinkmpfw.auto.mydlink.com/DCS-8100LH/DCS-8100LH_Ax_v2.04.01_3619.bin
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 19.5M  100 19.5M    0     0  2457k      0  0:00:08  0:00:08 --:--:-- 2727k

$ md5sum DCS-8100LH_Ax_v2.04.01_3619.bin
eb097a680754b94b81da6326b468121f  DCS-8100LH_Ax_v2.04.01_3619.bin
```
 
 
#### Signed and encrypted

Looking at the contents of a firmware update from D-Link can be
demotivating at the beginning:

```
$ tar xvf DCS-8000LH_Ax_v2.02.02_3014.bin 
update.bin.aes
update.aes
aes.key.rsa
certificate.info
sign.sha1.rsa

$ file *
aes.key.rsa:      data
certificate.info: ASCII text
sign.sha1.rsa:    data
update.aes:       data
update.bin.aes:   data

$ ls -l
total 10956
-rw-r--r-- 1 bjorn bjorn      128 Feb 14 10:58 aes.key.rsa
-rw-r--r-- 1 bjorn bjorn      130 Feb 14 10:58 certificate.info
-rw-r--r-- 1 bjorn bjorn      128 Feb 14 10:58 sign.sha1.rsa
-rw-r--r-- 1 bjorn bjorn 10268368 Feb 14 10:58 update.aes
-rw-r--r-- 1 bjorn bjorn   936464 Feb 14 10:58 update.bin.aes
```

So all the interesting stuff is AES encrypted, and the AES key is RSA
encrypted.  The only directly readable file is this one, and it
doesn't tell us much:

```
$ cat certificate.info 
Publisher:DMdssdFW1
Supported Models:DCS-8000LH,DCS-8000LH
Firmware Version:1.0.0
Target:update.bin
Build No:3014
Contents:update
```

Not much we can do about this then.  Or so it seems...  Until we look
at **firmwareupgrade.cgi**, or **fwupdate** which has almost the same
code:

```sh
verifyFirmware() {
	result=uploadSign
	#tar tf "$UPLOADBIN" > /dev/null 2> /dev/null || return 1
	fw_sign_verify.sh "$UPLOADBIN" /etc/db/verify.key > /dev/null 2> /dev/null || return 1
	return 0
}

decryptFirmware() {
	result=uploadDecrypt
	pibinfo PriKey > $dir/decrypt.key 2> /dev/null
	fw_decrypt.sh $dir/decrypt.key $out > /dev/null 2> /dev/null || return 1
	return 0
}
```

Can it be that simple?  Yes, it is.

Looking further at the **fw_sign_verify.sh** and **fw_decrypt.sh**,
used by both update tools, confirms it.  The firmware is verified by
using the RSA public key in **/etc/db/verify.key** to decrypt the hash
in **sign.sha1.rsa**.  Then it is decrypted using a key from the
factory data **pib** partition.



#### Further unpacking the firmware update

So we have the keys and the hashing algorithms we need to both verify
and decrypt this firmware.  We can run the commands found in
**fw_decrypt.sh** to get the real contents (slightly adapted to modern
openssl versions):

```
$ openssl rsautl -decrypt -in aes.key.rsa -inkey decrypt.key -out aes.key

$ openssl aes-128-cbc -v -md md5 -kfile aes.key -nosalt -d -in update.bin.aes -out update.bin
bufsize=8192
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
bytes read   :   936464
bytes written:   936454

$ openssl aes-128-cbc -v -md md5 -kfile aes.key -nosalt -d -in update.aes -out update
bufsize=8192
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
bytes read   : 10268368
bytes written: 10268355

$ file update.bin update
update.bin: POSIX shell script, ASCII text executable
update:     data
```

OK, the **update** file is still in an unknown format, but at least
we have the tool used to write it to the system. And it is a shell
script, so we have the source to look at too!  But 936454 bytes is a
hell of a shell script, and this is of course because most of it is an
uuencoded binary.  So we don't know exactly what that does.  But it is
named ddPack so a fair guess is that it is a tool for dd'ing multiple
file systems or other images packed as a single file.  That's really
enough info.

binwalk shows that the **update** file is just two squashfs systems
and a kernel, with a 1024 header of some sort.  The header presumably
tells ddPack how it should apply these three images:

```
$ binwalk update

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
1024          0x400           Squashfs filesystem, little endian, version 4.0, compression:xz, size: 338755 bytes, 16 inodes, blocksize: 131072 bytes, created: 2019-02-14 09:58:28
340992        0x53400         uImage header, header size: 64 bytes, header CRC: 0x675F081D, created: 2019-02-14 09:31:53, image size: 1661571 bytes, Data Address: 0x804D4960, Entry Point: 0x804D4960, data CRC: 0x73083021, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: none, image name: "linux_3.10"
2002627       0x1E8EC3        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 8265620 bytes, 2145 inodes, blocksize: 131072 bytes, created: 2019-02-14 09:58:45
```

But we can easily guess that without knowing anything about the
header.  There is only one alternative:
 * The kernel goes into the **kernel** partition
 * The 8265620 bytes squasfs system goes into the **rootfs** partition
 * The remaining squasfs system goes into the **userdata** partition

So there is no need to analyze ddPack.  We have the necessary entry
points for **fwupdate** or **firmwareupgrade.cgi** in the
**update.bin** script, and that's what we needed to know for the next
step:


#### Creating our own firmware updates

We do have shell access, so we can simply write the file systems we
want to flash as shown earlier.  We don't need to use the D-Link
scripts.  But where's the fun in that?

There is one challenge here: The D-Link tools are expecting signed and
encrypted firmware updates.  They will run their verifyFirmware() and
decryptFirmware() functions, and fail the update if any of the returns
an error.

But bailing out on verification errors is only the default setting, as
illustrated by this code from **fwupdate** (there is code with similar
functionality in **firmwareupgrade.cgi**):


```sh
        TrustLevel=`tdb get SecureFW _TrustLevel_byte`
        verifyFirmware
        ret=$?
        case $ret in
                2)
                        sign="not_signed"
                ;;
                0)
                        sign="trust"
                ;;
                *)
                        sign="untrust"
                ;;
        esac    
        if [ "$do_up" = "1" -a "$ret" != "0" -a "$TrustLevel" = "1" ]; then
                echo "3"
                return 1
        fi
```

So we don't need to sign the firmware if we change the **SecureFW
_TrustLevel** setting.  Or we can even sign it with a key unknown to
the camera if we like.  Which can be useful if we ever replace the
[**rootfs**]](#Partitions), since it will allow us to install our own verification
key and use it with D-Links tools.

But what about the encryption?  This cannot be disabled.  This gets
even better: The decrypting key so graciously provided to us in the
[**pib**](#Partitions) partition is an RSA private key. So not only can we decrypt
the firmware with it, but we can also encrypt! Nice.


The [**Makefile**](Makefile) in this repo has examples of how to use this to
create firmware update images which are accepted by the **fwupdate**
and **firmwareupgrade.cgi** tools.  It uses an alternatative
[**update.bin**](update.sh) made to modify only the [**userdata**](#Partitions) partition.  This
way we can install our own code in the camera, but still leave the
D-Link camera OS unmodified.


#### <a name="BuildFirmware"></a>Bulding the example firmware update in this repo

Rebuilding the example is as easy as typing **make**.  The Makefile is a
noisy one, so you can see all that's going on:
```
$ make
echo "WARNING: keys/DCS-8000LH-sign.pem is missing - using a new abitrary key instead"
WARNING: keys/DCS-8000LH-sign.pem is missing - using a new abitrary key instead
[ -f random-signkey.pem ] || openssl genrsa -out random-signkey.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
...............................................................................................................................+++++
........................................................................................................................................................+++++
e is 65537 (0x010001)
openssl rsa -pubout -in random-signkey.pem -out verify.key
writing RSA key
echo "Publisher:DMdssdFW1" >certificate.info
echo "Supported Models:DCS-8000LH,DCS-8000LH" >>certificate.info
echo "Firmware Version:1.0.0" >>certificate.info
echo "Target:update.bin" >>certificate.info
echo "Build No:9999" >>certificate.info
echo "Contents:update" >>certificate.info
openssl rand 16 > aes.key
openssl rsautl -encrypt -in aes.key -inkey keys/DCS-8000LH-PriKey.pem -out aes.key.rsa
sed -ne 's/"//g' -e 's/^VERSION *= *//p' dcs8000lh-configure.py >version
mksquashfs version opt.local opt.squashfs -all-root -comp xz
Parallel mksquashfs: Using 4 processors
Creating 4.0 filesystem on opt.squashfs, block size 131072.
[===============================================================================================================================================================================================================|] 2/2 100%

Exportable Squashfs 4.0 filesystem, xz compressed, data block size 131072
        compressed data, compressed metadata, compressed fragments, compressed xattrs
        duplicates are removed
Filesystem size 1.08 Kbytes (0.00 Mbytes)
        60.69% of uncompressed filesystem size (1.79 Kbytes)
Inode table size 98 bytes (0.10 Kbytes)
        100.00% of uncompressed inode table size (98 bytes)
Directory table size 46 bytes (0.04 Kbytes)
        100.00% of uncompressed directory table size (46 bytes)
Number of duplicate files found 0
Number of inodes 3
Number of files 2
Number of fragments 1
Number of symbolic links  0
Number of device nodes 0
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 1
Number of ids (unique uids + gids) 1
Number of uids 1
        root (0)
Number of gids 1
        root (0)
openssl aes-128-cbc -md md5 -kfile aes.key -nosalt -e -out update.aes -in opt.squashfs
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
sed  -e "s/@@MODEL@@/\"DCS-8000LH\"/" -e "s/@@MD5SUM@@/\"f1a1d3952c1630e5adb53e7f93b59d5e\"/" -e "s/@@VERSION@@/\"1.0.0-9999\"/" update.sh >update.bin
openssl aes-128-cbc -md md5 -kfile aes.key -nosalt -e -out update.bin.aes -in update.bin
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
openssl dgst -sha1 update.aes | cut -d' ' -f2 > update.sha1
cat update.bin.aes aes.key.rsa certificate.info update.sha1 | openssl dgst -sha1 | cut -d' ' -f2 > sign.sha1
openssl rsautl -sign -inkey random-signkey.pem -out sign.sha1.rsa -in sign.sha1
tar cvf fw.tar certificate.info aes.key.rsa sign.sha1.rsa update.aes update.bin.aes verify.key
certificate.info
aes.key.rsa
sign.sha1.rsa
update.aes
update.bin.aes
verify.key

```

This will produce a new **fw.tar** firmware update image.


## Contact


Please contact me on bjorn@mork.no if you have questions, comments or
just want to say hi.

But please note that I won't be able to provide any support for this.
I am making this information available for educational purposes.  If
you find it useful, then great!  If you brick a camera, then I am
truly sorry about that.  But there isn't much I can do about it....