/
SecurityPersonalizer.ino
1874 lines (1787 loc) · 67.8 KB
/
SecurityPersonalizer.ino
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
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2022 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*/
/**
* @ingroup MySigninggrp
* @{
* @file SecurityPersonalizer.ino
* @brief Security personalization sketch
*
* REVISION HISTORY
* - See git log (git log libraries/MySensors/examples/SecurityPersonalizer/SecurityPersonalizer.ino)
*/
/**
* @example SecurityPersonalizer.ino
* This sketch will personalize either none-volatile memory or ATSHA204A for security functions
* available in the MySensors library.<br>
* Details on personalization procedure is given in @ref personalization.<br>
* This sketch will when executed without modifications also print a guided workflow on the UART.
*/
#include "sha204_library.h"
#include "sha204_lib_return_codes.h"
/** @brief Make use of the MySensors framework without invoking the entire system */
#define MY_CORE_ONLY
#include <MySensors.h>
/************************************ User defined key data ***************************************/
/** @brief The user-defined HMAC key to use unless @ref GENERATE_HMAC_KEY is set */
#define MY_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
/** @brief The user-defined AES key to store in EEPROM unless @ref GENERATE_AES_KEY is set */
#define MY_AES_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
/** @brief The user-defined soft serial to use for soft signing unless @ref GENERATE_SOFT_SERIAL is set */
#define MY_SOFT_SERIAL 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
/***************************** Flags for guided personalization flow ******************************/
/**
* @def GENERATE_KEYS_ATSHA204A
* @brief Default settings for generating keys using ATSHA204A
*
* @note The generated keys displayed in the serial log with this setting needs to be written down
* and transferred to all nodes this gateway will communicate with. This is mandatory for ALL
* nodes for encryption (AES key). For signing (HMAC key) it is only required for nodes that
* use signing. Typically you set the values for @ref MY_HMAC_KEY and @ref MY_AES_KEY.
*/
//#define GENERATE_KEYS_ATSHA204A
/**
* @def GENERATE_KEYS_SOFT
* @brief Default settings for generating keys using software
*
* @b Important<br>
* You will need to ensure @ref MY_SIGNING_SOFT_RANDOMSEED_PIN is set to an unconnected analog pin
* in order to provide entropy to the software RNG if your hardware has no HWRNG.
*
* @note The generated keys displayed in the serial log with this setting needs to be written down
* and transferred to all nodes this gateway will communicate with. This is mandatory for ALL
* nodes for encryption (AES key). For signing (HMAC key) it is only required for nodes that
* use signing. Typically you set the values for @ref MY_HMAC_KEY and @ref MY_AES_KEY.
*/
//#define GENERATE_KEYS_SOFT
/**
* @def PERSONALIZE_ATSHA204A
* @brief Default settings for personalizing an ATSHA204A
*
* It is assumed that you have updated @ref MY_HMAC_KEY and @ref MY_AES_KEY with the keys displayed
* when executing this sketch with @ref GENERATE_KEYS_ATSHA204A or @ref GENERATE_KEYS_SOFT defined.
*/
//#define PERSONALIZE_ATSHA204A
/**
* @def PERSONALIZE_SOFT
* @brief Default settings for personalizing EEPROM for software signing
*
* It is assumed that you have updated @ref MY_HMAC_KEY and @ref MY_AES_KEY with the keys displayed
* when executing this sketch with @ref GENERATE_KEYS_ATSHA204A or @ref GENERATE_KEYS_SOFT defined.
*/
//#define PERSONALIZE_SOFT
/**
* @def PERSONALIZE_SOFT_RANDOM_SERIAL
* @brief This is an alternative to @ref PERSONALIZE_SOFT which will also store a randomly generated
* serial to EEPROM in addition to the actions performed by @ref PERSONALIZE_SOFT. Take note of the
* generated soft serial as it will be needed if you plan to use whitelisting. It should be
* unique for each node.
*
* @note This is only needed for targets that lack unique device IDs. The sketch will inform you if
* there is a need for generating a random serial or not. Check the "Hardware security
* peripherals" listing. If a target has a unique device ID and a serial in EEPROM, the serial
* in EEPROM will be used. If erased (replaced with FF:es) the unique device ID will be used
* instead.
*/
//#define PERSONALIZE_SOFT_RANDOM_SERIAL
/*************************** The settings below are for advanced users ****************************/
/**
* @def USE_SOFT_SIGNING
* @brief Uncomment this to generate keys by software and store them to EEPROM instead of ATSHA204A
*/
//#define USE_SOFT_SIGNING
/**
* @def LOCK_ATSHA204A_CONFIGURATION
* @brief Uncomment this to enable locking the ATSHA204A configuration zone
*
* It is still possible to change the key, and this also enable random key generation.
* @warning BE AWARE THAT THIS PREVENTS ANY FUTURE CONFIGURATION CHANGE TO THE CHIP
*/
//#define LOCK_ATSHA204A_CONFIGURATION
/**
* @def SKIP_UART_CONFIRMATION
* @brief Uncomment this for boards that lack UART
*
* This will disable additional confirmation for actions that are non-reversible.
*
* @b Important<br> For ATSHA204A, no confirmation will be required for locking any zones with this
* configuration! Also, if you generate keys on a board without UART, you have no way of determining
* what the key is unless it is stored in EEPROM.
*/
//#define SKIP_UART_CONFIRMATION
/**
* @def GENERATE_HMAC_KEY
* @brief Uncomment this to generate a random HMAC key using ATSHA204A or software depending on
* @ref USE_SOFT_SIGNING
* @note If not enabled, key defined by @ref MY_HMAC_KEY will be used instead.
*/
//#define GENERATE_HMAC_KEY
/**
* @def STORE_HMAC_KEY
* @brief Uncomment this to store HMAC key to ATSHA204A or EEPROM depending on @ref USE_SOFT_SIGNING
*/
//#define STORE_HMAC_KEY
/**
* @def GENERATE_AES_KEY
* @brief Uncomment this to generate a random AES key using ATSHA204A or software depending on
* @ref USE_SOFT_SIGNING
* @note If not enabled, key defined by @ref MY_AES_KEY will be used instead.
*/
//#define GENERATE_AES_KEY
/**
* @def STORE_AES_KEY
* @brief Uncomment this to store AES key to EEPROM
*/
//#define STORE_AES_KEY
/**
* @def GENERATE_SOFT_SERIAL
* @brief Uncomment this to generate a random serial number for software signing
* @note If not enabled, serial defined by @ref MY_SOFT_SERIAL will be used instead.
*/
//#define GENERATE_SOFT_SERIAL
/**
* @def STORE_SOFT_SERIAL
* @brief Uncomment this to store the serial number to EEPROM
*/
//#define STORE_SOFT_SERIAL
/**
* @def PRINT_DETAILED_ATSHA204A_CONFIG
* @brief Uncomment to print the detailed ATSHA204A configuration
*/
//#define PRINT_DETAILED_ATSHA204A_CONFIG
/**
* @def RESET_EEPROM_PERSONALIZATION
* @brief Uncomment to reset the personalization data in EEPROM to 0xFF:es
*/
//#define RESET_EEPROM_PERSONALIZATION
/********************* Guided mode flag configurations (don't change these) ***********************/
#ifdef GENERATE_KEYS_ATSHA204A
#define LOCK_ATSHA204A_CONFIGURATION // We have to lock configuration to enable random number generation
#define GENERATE_HMAC_KEY // Generate random HMAC key
#define GENERATE_AES_KEY // Generate random AES key
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif
#ifdef GENERATE_KEYS_SOFT
#define USE_SOFT_SIGNING // Use software backend
#define GENERATE_HMAC_KEY // Generate random HMAC key
#define GENERATE_AES_KEY // Generate random AES key
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif
#ifdef PERSONALIZE_ATSHA204A
#define LOCK_ATSHA204A_CONFIGURATION // We have to lock configuration to enable random number generation
#define STORE_HMAC_KEY // Store the HMAC key
#define STORE_AES_KEY // Store the AES key
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif
#ifdef PERSONALIZE_SOFT_RANDOM_SERIAL
#define GENERATE_SOFT_SERIAL // Generate a soft serial number
#define PERSONALIZE_SOFT // Do the rest as PERSONALIZE_SOFT
#endif
#ifdef PERSONALIZE_SOFT
#define USE_SOFT_SIGNING // Use software backend
#define STORE_HMAC_KEY // Store the HMAC key
#define STORE_AES_KEY // Store the AES key
#define STORE_SOFT_SERIAL // Store the soft serial number
#define SKIP_UART_CONFIRMATION // This is an automated mode
#endif
#if defined(GENERATE_HMAC_KEY) || defined(GENERATE_AES_KEY) || defined(GENERATE_SOFT_SERIAL)
#define GENERATE_SOMETHING
#endif
#if defined(MY_LOCK_MCU)
#undefine MY_LOCK_MCU // The Sketch after SecurityPersonaliter should lock the MCU
#endif
/********************************** Preprocessor sanitychecks *************************************/
#if defined(GENERATE_SOFT_SERIAL) && !defined(USE_SOFT_SIGNING)
#error Cannot generate soft serial using ATSHA204A, use USE_SOFT_SINGING for this
#endif
#if defined(STORE_SOFT_SERIAL) && !defined(USE_SOFT_SIGNING)
#error Cannot store soft serial to ATSHA204A, use USE_SOFT_SINGING for this
#endif
#if defined(PRINT_DETAILED_ATSHA204A_CONFIG) && defined(USE_SOFT_SIGNING)
#error Cannot print ATSHA204A config using software signing flag, disable USE_SOFT_SINGING for this
#endif
#if defined(LOCK_ATSHA204A_CONFIGURATION) && defined(USE_SOFT_SIGNING)
#error Cannot lock ATSHA204A config using software signing flag, disable USE_SOFT_SINGING for this
#endif
#ifdef GENERATE_KEYS_ATSHA204A
#ifdef USE_SOFT_SIGNING
#error You cannot select soft signing if you want to generate keys using ATSHA204A
#endif
#ifdef STORE_HMAC_KEY
#error Disable STORE_SOFT_KEY, you should not store keys in this mode
#endif
#ifdef STORE_SOFT_SERIAL
#error Disable STORE_SOFT_SERIAL, you should not store serial in this mode
#endif
#ifdef STORE_AES_KEY
#error Disable STORE_AES_KEY, you should not store keys in this mode
#endif
#if defined(GENERATE_KEYS_SOFT) ||\
defined (PERSONALIZE_ATSHA204A) ||\
defined (PERSONALIZE_SOFT) ||\
defined (PERSONALIZE_SOFT_RANDOM_SERIAL)
#error You can not enable GENERATE_KEYS_ATSHA204A together with other guided modes
#endif
#endif // GENERATE_KEYS_ATSHA204A
#ifdef GENERATE_KEYS_SOFT
#ifdef STORE_HMAC_KEY
#error Disable STORE_SOFT_KEY, you should not store keys in this mode
#endif
#ifdef STORE_SOFT_SERIAL
#error Disable STORE_SOFT_SERIAL, you should not store serial in this mode
#endif
#ifdef STORE_AES_KEY
#error Disable STORE_AES_KEY, you should not store keys in this mode
#endif
#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN
#error You have to set MY_SIGNING_SOFT_RANDOMSEED_PIN to a suitable value in this mode
#endif
#if defined(GENERATE_KEYS_ATSHA204A) ||\
defined (PERSONALIZE_ATSHA204A) ||\
defined (PERSONALIZE_SOFT) ||\
defined (PERSONALIZE_SOFT_RANDOM_SERIAL)
#error You can not enable GENERATE_KEYS_SOFT together with other guided modes
#endif
#endif // GENERATE_KEYS_SOFT
#ifdef PERSONALIZE_ATSHA204A
#ifdef USE_SOFT_SIGNING
#error You cannot select soft signing if you want to personalize an ATSHA204A
#endif
#if defined(GENERATE_KEYS_ATSHA204A) ||\
defined (GENERATE_KEYS_SOFT) ||\
defined (PERSONALIZE_SOFT) ||\
defined (PERSONALIZE_SOFT_RANDOM_SERIAL)
#error You can not enable PERSONALIZE_ATSHA204A together with other guided modes
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
#error You cannot reset EEPROM personalization when personalizing a device
#endif
#endif // PERSONALIZE_ATSHA204A
#ifdef PERSONALIZE_SOFT
#if defined(GENERATE_KEYS_ATSHA204A) ||\
defined (GENERATE_KEYS_SOFT) ||\
defined (PERSONALIZE_ATSHA204A)
#error You can not enable PERSONALIZE_SOFT together with other guided modes
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
#error You cannot reset EEPROM personalization when personalizing a device
#endif
#endif // PERSONALIZE_SOFT
#ifdef PERSONALIZE_SOFT_RANDOM_SERIAL
#if defined(GENERATE_KEYS_SOFT) ||\
defined (PERSONALIZE_ATSHA204A) ||\
defined (GENERATE_KEYS_ATSHA204A)
#error You can only enable one of the guided modes at a time
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
#error You cannot reset EEPROM personalization when personalizing a device
#endif
#endif // PERSONALIZE_SOFT_RANDOM_SERIAL
#if !defined(GENERATE_KEYS_ATSHA204A) &&\
!defined(GENERATE_KEYS_SOFT) &&\
!defined(PERSONALIZE_ATSHA204A) &&\
!defined(PERSONALIZE_SOFT) &&\
!defined(PERSONALIZE_SOFT_RANDOM_SERIAL) &&\
!defined(USE_SOFT_SIGNING) &&\
!defined(LOCK_ATSHA204A_CONFIGURATION) &&\
!defined(SKIP_UART_CONFIRMATION) &&\
!defined(GENERATE_HMAC_KEY) &&\
!defined(STORE_HMAC_KEY) &&\
!defined(GENERATE_SOFT_SERIAL) &&\
!defined(STORE_SOFT_SERIAL) &&\
!defined(GENERATE_AES_KEY) &&\
!defined(STORE_AES_KEY) &&\
!defined(PRINT_DETAILED_ATSHA204A_CONFIG) &&\
!defined(RESET_EEPROM_PERSONALIZATION)
/** @brief Set when there are no config flags defined */
#define NO_SETTINGS_DEFINED
#endif
#if defined(GENERATE_KEYS_ATSHA204A) ||\
defined(GENERATE_KEYS_SOFT) ||\
defined(PERSONALIZE_ATSHA204A) ||\
defined(PERSONALIZE_SOFT) ||\
defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
/** @brief Set when there is a guided mode flag defined */
#define GUIDED_MODE
#endif
/************************************* Function declarations ***************************************/
static void halt(bool success);
#ifdef GENERATE_SOMETHING
static bool generate_random_data(uint8_t* data, size_t sz);
#endif
static void generate_keys(void);
static void store_keys(void);
static void print_hex_buffer(uint8_t* data, size_t sz);
static void print_c_friendly_hex_buffer(uint8_t* data, size_t sz);
#ifdef STORE_HMAC_KEY
static bool store_hmac_key_data(uint8_t* data, size_t sz);
#endif
#ifdef STORE_AES_KEY
static bool store_aes_key_data(uint8_t* data, size_t sz);
#endif
#ifdef STORE_SOFT_SERIAL
static bool store_soft_serial_data(uint8_t* data, size_t sz);
#endif
#ifndef USE_SOFT_SIGNING
static void init_atsha204a_state(void);
#ifdef LOCK_ATSHA204A_CONFIGURATION
static void lock_atsha204a_config(void);
static uint16_t write_atsha204a_config_and_get_crc(void);
#endif
static bool get_atsha204a_serial(uint8_t* data);
#ifdef STORE_HMAC_KEY
static bool write_atsha204a_key(uint8_t* key);
#endif
#endif // not USE_SOFT_SIGNING
static void print_greeting(void);
static void print_ending(void);
static void probe_and_print_peripherals(void);
static void print_eeprom_data(void);
static void print_whitelisting_entry(void);
#ifdef PRINT_DETAILED_ATSHA204A_CONFIG
static void dump_detailed_atsha204a_configuration(void);
#endif
#ifdef RESET_EEPROM_PERSONALIZATION
static void reset_eeprom(void);
#endif
static void write_eeprom_checksum(void);
/**************************************** File local data *****************************************/
#if defined(GENERATE_HMAC_KEY) || defined(STORE_HMAC_KEY)
/** @brief The data to store as HAMC key in ATSHA204A or EEPROM */
static uint8_t user_hmac_key[32] = {MY_HMAC_KEY};
#endif
#if defined(GENERATE_SOFT_SERIAL) || defined(STORE_SOFT_SERIAL)
/** @brief The data to store as soft serial in EEPROM */
static uint8_t user_soft_serial[9] = {MY_SOFT_SERIAL};
#endif
#if defined(GENERATE_AES_KEY) || defined(STORE_AES_KEY)
/* @brief The data to store as AES key in EEPROM */
static uint8_t user_aes_key[16] = {MY_AES_KEY};
#endif
#ifndef USE_SOFT_SIGNING
const int sha204Pin = MY_SIGNING_ATSHA204_PIN; //!< The IO pin to use for ATSHA204A
atsha204Class sha204(sha204Pin); //!< atsha204Class
static uint8_t tx_buffer[SHA204_CMD_SIZE_MAX];
static uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
static uint8_t ret_code;
static uint8_t lockConfig = 0;
static uint8_t lockValue = 0;
#endif
static bool has_device_unique_id = false;
static const uint8_t reset_buffer[32] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
/******************************************* Functions ********************************************/
/** @brief Sketch setup code (all personalization is done here as it is a run-once sketch) */
void setup()
{
// Delay startup a bit for serial consoles to catch up
uint32_t enter = hwMillis();
while (hwMillis() - enter < (uint32_t)500);
#ifdef USE_SOFT_SIGNING
// initialize pseudo-RNG
hwRandomNumberInit();
#endif
while(!Serial); // For USB enabled devices, wait for USB enumeration before continuing
print_greeting();
#ifndef USE_SOFT_SIGNING
init_atsha204a_state();
// Lock configuration now if requested to enable RNG in ATSHA
#ifdef LOCK_ATSHA204A_CONFIGURATION
lock_atsha204a_config();
#endif
#endif
// Generate the requested keys (if any)
generate_keys();
#ifdef RESET_EEPROM_PERSONALIZATION
// If requested, reset EEPROM before storing keys
reset_eeprom();
#endif
// Store the keys (if configured to do so)
store_keys();
// Write a checksum on the EEPROM data
write_eeprom_checksum();
// Print current EEPROM
print_eeprom_data();
print_whitelisting_entry();
Serial.println();
print_ending();
halt(true);
}
/** @brief Sketch execution code (unused) */
void loop()
{
}
/** @brief Print a notice and halt the execution */
static void halt(bool success)
{
Serial.println();
Serial.println(
F("+------------------------------------------------------------------------------------+"));
Serial.println(
F("| Execution result |"));
Serial.println(
F("+------------------------------------------------------------------------------------+"));
if (success) {
Serial.println(F(
"| SUCCESS |"));
} else {
Serial.print(F(
"| FAILURE "));
#ifdef USE_SOFT_SIGNING
Serial.println(F(" |"));
#else
if (ret_code != SHA204_SUCCESS) {
Serial.print(F("(last ATSHA204A return code: 0x"));
if (ret_code < 0x10) {
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(ret_code, HEX);
Serial.println(F(") |"));
} else {
Serial.println(F(" |"));
}
#endif
}
Serial.println(
F("+------------------------------------------------------------------------------------+"));
while(1) {
doYield();
}
}
#ifdef GENERATE_SOMETHING
/**
* @brief Generate random data of specified size and store in provided buffer
* @param data Buffer to store bytes in (buffer is assumed to be of required size)
* @param sz Number of random bytes to generate
* @returns false if failed
*/
static bool generate_random_data(uint8_t* data, size_t sz)
{
#if defined(USE_SOFT_SIGNING) && ! defined(MY_HW_HAS_GETENTROPY)
for (size_t i = 0; i < sz; i++) {
data[i] = random(256) ^ micros();
uint32_t enter = hwMillis();
while (hwMillis() - enter < (uint32_t)2);
}
return true;
#elif defined(USE_SOFT_SIGNING) && defined(MY_HW_HAS_GETENTROPY)
hwGetentropy(&data, sz);
#else
ret_code = sha204.sha204m_random(tx_buffer, rx_buffer, RANDOM_SEED_UPDATE);
if ((ret_code != SHA204_SUCCESS) || (lockConfig != 0x00)) {
return false;
} else {
memcpy(data, rx_buffer+SHA204_BUFFER_POS_DATA, sz);
return true;
}
#endif // not USE_SOFT_SIGNING
}
#endif // GENERATE_SOMETHING
/** @brief Generate keys depending on configuration */
static void generate_keys(void)
{
#ifdef GENERATE_SOMETHING
Serial.println(
F("+------------------------------------------------------------------------------------+"));
Serial.println(
F("| Key generation |"));
Serial.println(
F("+--------+--------+------------------------------------------------------------------+"));
Serial.println(
F("| Key ID | Status | Key |"));
Serial.println(
F("+--------+--------+------------------------------------------------------------------+"));
#endif
#ifdef GENERATE_HMAC_KEY
Serial.print(F("| HMAC | "));
if (!generate_random_data(user_hmac_key, 32)) {
memset(user_hmac_key, 0xFF, 32);
Serial.print(F("FAILED | "));
} else {
Serial.print(F("OK | "));
}
print_hex_buffer(user_hmac_key, 32);
Serial.println(F(" |"));
#endif
#ifdef GENERATE_AES_KEY
Serial.print(F("| AES | "));
if (!generate_random_data(user_aes_key, 16)) {
memset(user_aes_key, 0xFF, 16);
Serial.print(F("FAILED | "));
} else {
Serial.print(F("OK | "));
}
print_hex_buffer(user_aes_key, 16);
Serial.println(F(" |"));
#endif
#ifdef GENERATE_SOFT_SERIAL
Serial.print(F("| SERIAL | "));
if (has_device_unique_id) {
Serial.println(F("N/A | MCU has a unique serial which will be used instead. |"));
} else {
if (!generate_random_data(user_soft_serial, 9)) {
memset(user_soft_serial, 0xFF, 9);
Serial.print(F("FAILED | "));
} else {
Serial.print(F("OK | "));
}
print_hex_buffer(user_soft_serial, 9);
Serial.println(F(" |"));
}
#endif
#if defined(GENERATE_SOMETHING) && !defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
Serial.println(
F("+--------+--------+------------------------------------------------------------------+"));
Serial.println();
Serial.println(
F("+------------------------------------------------------------------------------------+"));
Serial.println(
F("| Key copy section |"));
Serial.println(
F("+------------------------------------------------------------------------------------+"));
#ifdef GENERATE_HMAC_KEY
Serial.print(F("#define MY_HMAC_KEY "));
print_c_friendly_hex_buffer(user_hmac_key, 32);
Serial.println();
#endif
#ifdef GENERATE_AES_KEY
Serial.print(F("#define MY_AES_KEY "));
print_c_friendly_hex_buffer(user_aes_key, 16);
Serial.println();
#endif
#ifdef GENERATE_SOFT_SERIAL
Serial.print(F("#define MY_SOFT_SERIAL "));
print_c_friendly_hex_buffer(user_soft_serial, 9);
Serial.println();
#endif
Serial.println(
F("+------------------------------------------------------------------------------------+"));
Serial.println();
#elif defined(PERSONALIZE_SOFT_RANDOM_SERIAL)
Serial.println(
F("+--------+--------+------------------------------------------------------------------+"));
Serial.println();
#endif
}
#ifdef RESET_EEPROM_PERSONALIZATION
/** @brief Reset EEPROM */
static void reset_eeprom(void)
{
uint8_t validation_buffer[32];
Serial.println(
F("+------------------------------------------------------------------------------------+"));
Serial.println(
F("| EEPROM reset |"));
Serial.println(
F("+--------+---------------------------------------------------------------------------+"));
Serial.println(
F("| Key ID | Status |"));
Serial.println(
F("+--------+---------------------------------------------------------------------------+"));
Serial.print(F("| HMAC | "));
hwWriteConfigBlock((void*)reset_buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
// Validate data written
hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
if (memcmp(validation_buffer, reset_buffer, 32) != 0) {
Serial.println(F("FAILED |"));
} else {
Serial.println(F("OK |"));
}
Serial.print(F("| AES | "));
hwWriteConfigBlock((void*)reset_buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
// Validate data written
hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
if (memcmp(validation_buffer, reset_buffer, 16) != 0) {
Serial.println(F("FAILED |"));
} else {
Serial.println(F("OK |"));
}
Serial.print(F("| SERIAL | "));
hwWriteConfigBlock((void*)reset_buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
// Validate data written
hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
if (memcmp(validation_buffer, reset_buffer, 9) != 0) {
Serial.println(F("FAILED |"));
} else {
Serial.println(F("OK |"));
}
Serial.println(
F("+--------+---------------------------------------------------------------------------+"));
}
#endif // RESET_EEPROM_PERSONALIZATION
static void write_eeprom_checksum(void)
{
uint8_t buffer[SIZE_SIGNING_SOFT_HMAC_KEY + SIZE_RF_ENCRYPTION_AES_KEY + SIZE_SIGNING_SOFT_SERIAL];
uint8_t hash[32];
uint8_t checksum;
hwReadConfigBlock((void*)buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS,
SIZE_SIGNING_SOFT_HMAC_KEY);
hwReadConfigBlock((void*)&buffer[SIZE_SIGNING_SOFT_HMAC_KEY],
(void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, SIZE_RF_ENCRYPTION_AES_KEY);
hwReadConfigBlock((void*)&buffer[SIZE_SIGNING_SOFT_HMAC_KEY + SIZE_RF_ENCRYPTION_AES_KEY],
(void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, SIZE_SIGNING_SOFT_SERIAL);
hwReadConfigBlock((void*)&checksum, (void*)EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS,
SIZE_PERSONALIZATION_CHECKSUM);
SHA256(hash, buffer, sizeof(buffer));
hwWriteConfigBlock((void*)&hash[0], (void*)EEPROM_PERSONALIZATION_CHECKSUM_ADDRESS,
SIZE_PERSONALIZATION_CHECKSUM);
}
/** @brief Store keys depending on configuration */
static void store_keys(void)
{
#if defined(STORE_HMAC_KEY) || defined(STORE_AES_KEY) || defined(STORE_SOFT_SERIAL)
Serial.println(
F("+------------------------------------------------------------------------------------+"));
Serial.println(
F("| Key storage |"));
Serial.println(
F("+--------+--------+------------------------------------------------------------------+"));
Serial.println(
F("| Key ID | Status | Key |"));
Serial.println(
F("+--------+--------+------------------------------------------------------------------+"));
#endif
#ifdef STORE_HMAC_KEY
Serial.print(F("| HMAC | "));
if (!store_hmac_key_data(user_hmac_key)) {
Serial.print(F("FAILED | "));
} else {
Serial.print(F("OK | "));
}
print_hex_buffer(user_hmac_key, 32);
Serial.println(F(" |"));
#endif
#ifdef STORE_AES_KEY
Serial.print(F("| AES | "));
if (!store_aes_key_data(user_aes_key)) {
Serial.print(F("FAILED | "));
} else {
Serial.print(F("OK | "));
}
print_hex_buffer(user_aes_key, 16);
Serial.println(F(" |"));
#endif
#ifdef STORE_SOFT_SERIAL
Serial.print(F("| SERIAL | "));
if (has_device_unique_id) {
memset(user_soft_serial, 0xFF, 9);
}
if (!store_soft_serial_data(user_soft_serial)) {
Serial.print(F("FAILED | "));
} else {
Serial.print(F("OK | "));
}
if (has_device_unique_id) {
Serial.println(F("EEPROM reset. MCU has a unique serial which will be used instead.|"));
} else {
print_hex_buffer(user_soft_serial, 9);
Serial.println(F(" |"));
}
#endif
#if defined(STORE_HMAC_KEY) || defined(STORE_AES_KEY) || defined(STORE_SOFT_SERIAL)
Serial.println(
F("+--------+--------+------------------------------------------------------------------+"));
Serial.println();
#endif
}
/**
* @brief Print provided buffer as HEX data on serial console
* @param data Buffer to print
* @param sz Number of bytes to print
*/
static void print_hex_buffer(uint8_t* data, size_t sz)
{
for (size_t i=0; i<sz; i++) {
if (data[i] < 0x10) {
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(data[i], HEX);
}
}
/**
* @brief Print provided buffer as C friendly copy+paste HEX data on serial console
* @param data Buffer to print
* @param sz Number of bytes to print
*/
static void print_c_friendly_hex_buffer(uint8_t* data, size_t sz)
{
for (size_t i=0; i<sz; i++) {
Serial.print("0x");
if (data[i] < 0x10) {
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(data[i], HEX);
if (i < sz-1) {
Serial.print(',');
}
}
}
#ifdef STORE_HMAC_KEY
/**
* @brief Store the provided data as HMAC key to ATSHA204A or EEPROM depending on @ref USE_SOFT_SIGNING flag
* @param data Key to store
* @returns false if failed
*/
static bool store_hmac_key_data(uint8_t* data)
{
#ifdef USE_SOFT_SIGNING
static uint8_t validation_buffer[32];
hwWriteConfigBlock((void*)data, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
// Validate data written
hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
if (memcmp(validation_buffer, data, 32) != 0) {
return false;
} else {
return true;
}
#else
// It will not be possible to write the key if the configuration zone is unlocked
if (lockConfig == 0x00) {
// Write the key to the appropriate slot in the data zone
if (!write_atsha204a_key(data)) {
return false;
} else {
return true;
}
} else {
return false;
}
#endif
}
#endif
#ifdef STORE_AES_KEY
/**
* @brief Store the provided data as AES key to EEPROM
* @param data Key to store
* @returns false if failed
*/
static bool store_aes_key_data(uint8_t* data)
{
static uint8_t validation_buffer[16];
hwWriteConfigBlock((void*)data, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
// Validate data written
hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
if (memcmp(validation_buffer, data, 16) != 0) {
return false;
} else {
return true;
}
}
#endif // STORE_AES_KEY
#ifdef STORE_SOFT_SERIAL
/**
* @brief Store the provided data as serial number to EEPROM
* @param data Serial to store
* @returns false if failed
*/
static bool store_soft_serial_data(uint8_t* data)
{
static uint8_t validation_buffer[9];
hwWriteConfigBlock((void*)data, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
// Validate data written
hwReadConfigBlock((void*)validation_buffer, (void*)EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
if (memcmp(validation_buffer, data, 9) != 0) {
return false;
} else {
return true;
}
}
#endif // STORE_SOFT_SERIAL
#ifndef USE_SOFT_SIGNING
/**
* @brief Initialize and read states in ATSHA204A
*/
static void init_atsha204a_state(void)
{
// Read out lock config bits to determine if locking is possible
ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2);
if (ret_code != SHA204_SUCCESS) {
halt(false);
} else {
lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3];
lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2];
}
}
#ifdef LOCK_ATSHA204A_CONFIGURATION
static void lock_atsha204a_config(void)
{
Serial.println(
F("+------------------------------------------------------------------------------------+"));
Serial.println(
F("| ATSHA204A configuration locking |"));
Serial.println(
F("+------------------------------------------------------------------------------------+"));
if (lockConfig != 0x00) {
uint16_t crc;
(void)crc;
// Write config and get CRC for the updated config
crc = write_atsha204a_config_and_get_crc();
// List current configuration before attempting to lock
#ifdef PRINT_DETAILED_ATSHA204A_CONFIG
Serial.println(
F("| New ATSHA204A Configuration |"));
dump_detailed_atsha204a_configuration();
#endif // PRINT_DETAILED_ATSHA204A_CONFIG
#ifndef SKIP_UART_CONFIRMATION
// Purge serial input buffer
while (Serial.available()) {
Serial.read();
}
Serial.println(
F("| * Send SPACE character now to lock the configuration... |"));
while (Serial.available() == 0);
if (Serial.read() == ' ')
#endif //not SKIP_UART_CONFIRMATION
{
Serial.print(F("| * Locking configuration..."));
// Correct sequence, resync chip
ret_code = sha204.sha204c_resync(SHA204_RSP_SIZE_MAX, rx_buffer);
if (ret_code != SHA204_SUCCESS && ret_code != SHA204_RESYNC_WITH_WAKEUP) {
Serial.println(
F("+------------------------------------------------------------------------------------+"));
halt(false);
}
// Lock configuration zone
ret_code = sha204.sha204m_execute(SHA204_LOCK, SHA204_ZONE_CONFIG,
crc, 0, NULL, 0, NULL, 0, NULL,
LOCK_COUNT, tx_buffer, LOCK_RSP_SIZE, rx_buffer);
if (ret_code != SHA204_SUCCESS) {
Serial.println(F("Failed |"));
Serial.println(
F("+------------------------------------------------------------------------------------+"));
halt(false);
} else {
Serial.println(F("Done |"));
// Update lock flags after locking
ret_code = sha204.sha204m_read(tx_buffer, rx_buffer, SHA204_ZONE_CONFIG, 0x15<<2);
if (ret_code != SHA204_SUCCESS) {
Serial.println(
F("+------------------------------------------------------------------------------------+"));
halt(false);
} else {
lockConfig = rx_buffer[SHA204_BUFFER_POS_DATA+3];
lockValue = rx_buffer[SHA204_BUFFER_POS_DATA+2];
}
}
}
#ifndef SKIP_UART_CONFIRMATION
else {
Serial.println(
F("| * Unexpected answer. Skipping locking. |"));
Serial.println(
F("+------------------------------------------------------------------------------------+"));
}
#endif //not SKIP_UART_CONFIRMATION
} else {
Serial.println(
F("| * Skipping configuration write and lock (configuration already locked). |"));
Serial.println(
F("+------------------------------------------------------------------------------------+"));
}
Serial.println();
}
/**
* @brief Write default configuration and return CRC of the configuration bits
* @returns CRC over the configuration bits
*/
static uint16_t write_atsha204a_config_and_get_crc(void)
{
uint16_t crc = 0;
uint8_t config_word[4];
// We will set default settings from datasheet on all slots. This means that we can use slot 0 for the key
// as that slot will not be readable (key will therefore be secure) and slot 8 for the payload digest
// calculationon as that slot can be written in clear text even when the datazone is locked.
// Other settings which are not relevant are kept as is.
for (int i=0; i < 88; i += 4) {
bool do_write = true;
if (i == 20) {
config_word[0] = 0x8F;
config_word[1] = 0x80;
config_word[2] = 0x80;
config_word[3] = 0xA1;
} else if (i == 24) {
config_word[0] = 0x82;
config_word[1] = 0xE0;
config_word[2] = 0xA3;
config_word[3] = 0x60;
} else if (i == 28) {