├── .gitignore ├── LICENSE.txt ├── README.md ├── cases ├── CreditCard-v0.1 │ ├── CreditCard-base.dxf │ ├── CreditCard-base.stl │ ├── CreditCard-face.dxf │ ├── CreditCard-face.stl │ ├── README.md │ └── credit-card.skp └── README.md ├── diagrams ├── README.md ├── breadboard.png ├── breadboard │ └── Firefly.fzz ├── schematic.pdf └── schematic │ ├── Firefly-cache.lib │ ├── Firefly.kicad_pcb │ ├── Firefly.pro │ ├── Firefly.sch │ ├── FireflyComponents.dcm │ └── FireflyComponents.lib └── source ├── firefly ├── firefly.ino ├── firefly_display.cpp ├── firefly_display.h └── firefly_fonts.h └── libs ├── ethers ├── LICENSE.txt ├── README.md ├── library.properties └── src │ ├── asm_avr.inc │ ├── asm_avr_mult_square.inc │ ├── curve-specific.inc │ ├── ethers.c │ ├── ethers.h │ ├── keccak256.c │ ├── keccak256.h │ ├── platform-specific.inc │ ├── types.h │ ├── uECC.c │ ├── uECC.h │ └── uECC_vli.h ├── firefly_blecast ├── README.md ├── library.properties └── src │ ├── aes-otfks-decrypt.c │ ├── aes.h │ ├── firefly_blecast.c │ └── firefly_blecast.h └── firefly_qrcode ├── LICENSE.txt ├── keywords.txt ├── library.properties └── src ├── firefly_qrcode.c └── firefly_qrcode.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Richard Moore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Firefly Wallet 2 | ============== 3 | 4 | Weclome to the Firefly DIY Air-gapped Hardware Wallet project. 5 | 6 | There are still a few things we are working on, but we wanted to start 7 | getting some files into the hands of eager developers. 8 | 9 | Also, if you haven't seen it already, please check out our 10 | [crowdsale](https://ethers.io/#!/app-link/contribute.firefly.city). By contributing 11 | to the project, you get your very own yourNameHere.firefly.eth ENS name on the 12 | Ethereum network and you help fund development of the Firefly. 13 | 14 | 15 | Folders 16 | ------- 17 | 18 | ### Cases 19 | 20 | Currently limited to 3D printable cases, but we would love to have other 21 | creative people out there contribute interesting designs. 22 | 23 | A better 3D printable case will be published soon, but for the impatient, 24 | the prototype case is available. 25 | 26 | 27 | ### Diagrams 28 | 29 | Images and source files used to build documentation. Currently we have a circuit schematic 30 | and a breadboard layout for prototyping. 31 | 32 | 33 | ### Source 34 | 35 | The Arduino sketch and required (modified) libraries. 36 | 37 | 38 | Future 39 | ------ 40 | 41 | - Better 3D printed case; still being designed, but easier to wire (soonish) 42 | - Printed Circuit Board (PCB) layout for more advanced, but tinier devices (future) 43 | 44 | 45 | License 46 | ------- 47 | 48 | MIT and BSD license. Each file includes license information at the top. 49 | -------------------------------------------------------------------------------- /cases/CreditCard-v0.1/README.md: -------------------------------------------------------------------------------- 1 | Credit Card V0.1 2 | ================ 3 | 4 | This is a very early prototype, and my first time using [SketchUp](http://www.sketchup.com). 5 | 6 | This is the version seen in the [demo video](https://firefly.city/#demo). 7 | 8 | A newer Credit Card V1 will be out soon: 9 | 10 | - Shorter wire distances and easier wire routing 11 | - Power switch 12 | 13 | However, for a quick and simple case you can use **today**, so you can work 14 | on the Firefly from a coffee shop, this is what we use. 15 | -------------------------------------------------------------------------------- /cases/CreditCard-v0.1/credit-card.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefly/wallet/58d7ce5427ed49484280cc0ccbe90cc1db3120b5/cases/CreditCard-v0.1/credit-card.skp -------------------------------------------------------------------------------- /cases/README.md: -------------------------------------------------------------------------------- 1 | Cases 2 | ===== 3 | 4 | Here is a list of cases. 5 | 6 | We welcome any interesting additions; they do not need to be 3D printed; a How-To on 7 | building a Firefly into an Altoids tin or other clever ideas are just as awesome. 8 | 9 | Credit Card v0.1 10 | ---------------- 11 | 12 | This is the case used for the prototype in our [demo viedo](https://firefly.city/#demo). 13 | 14 | It has several problems, which we are addressing in a new revision: 15 | 16 | - No power switch 17 | - Overly complicated routing of wires 18 | 19 | If you are impatient, feel free to try it out. Otherwise, we highly recommend you wait 20 | for the next version. 21 | 22 | 23 | Credit Card v0.2 24 | ---------------- 25 | 26 | The changes we are working on: 27 | 28 | - Orienting the Arduino Nano to be vertical to minimize wiring distances 29 | - Adding a power slider/spinner 30 | - Likely stacking the batteries 31 | 32 | Coming soon. 33 | 34 | 35 | -------------------------------------------------------------------------------- /diagrams/README.md: -------------------------------------------------------------------------------- 1 | Diagrams 2 | ======== 3 | 4 | This folder will eventually get rolled into the documentation. 5 | 6 | These files are still very experimental. 7 | 8 | **Important:** The nRF24L01+ **MUST** be powered with the 3.3V; the 5V rail **WILL** destroy it. 9 | 10 | 11 | Schematic 12 | --------- 13 | 14 | The schematic here is a simple overview of the connections required between 15 | the various components. 16 | 17 | This schematic diagram can be opened and modified using [kicad](http://kicad-pcb.org). 18 | 19 | 20 | Breadboard 21 | ---------- 22 | 23 | The breadboard diagram is useful for quickly prototyping the Firefly wallet and 24 | verify components are working coreectly. 25 | 26 | The breadboard diagram can be opened and modified using [Fritzing](http://fritzing.org). 27 | 28 | 29 | License 30 | ------- 31 | 32 | All documents in here are licensed under the MIT license and the Creative 33 | Commons CC-BY-4 license; whichever you are more comforatable with. 34 | -------------------------------------------------------------------------------- /diagrams/breadboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefly/wallet/58d7ce5427ed49484280cc0ccbe90cc1db3120b5/diagrams/breadboard.png -------------------------------------------------------------------------------- /diagrams/breadboard/Firefly.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefly/wallet/58d7ce5427ed49484280cc0ccbe90cc1db3120b5/diagrams/breadboard/Firefly.fzz -------------------------------------------------------------------------------- /diagrams/schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefly/wallet/58d7ce5427ed49484280cc0ccbe90cc1db3120b5/diagrams/schematic.pdf -------------------------------------------------------------------------------- /diagrams/schematic/Firefly-cache.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.3 2 | #encoding utf-8 3 | # 4 | # ArduinoNano 5 | # 6 | DEF ArduinoNano PC 0 40 Y Y 1 F N 7 | F0 "PC" 0 900 60 H V C CNN 8 | F1 "ArduinoNano" 0 -900 60 H V C CNN 9 | F2 "" 0 -900 60 H I C CNN 10 | F3 "" 0 -900 60 H I C CNN 11 | DRAW 12 | S 350 -800 -350 800 0 1 0 N 13 | X D13 1 -550 700 200 R 50 50 1 1 B 14 | X 3V3 2 -550 600 200 R 50 50 1 1 w 15 | X AREF 3 -550 500 200 R 50 50 1 1 W 16 | X A0 4 -550 400 200 R 50 50 1 1 I 17 | X A1 5 -550 300 200 R 50 50 1 1 I 18 | X A2 6 -550 200 200 R 50 50 1 1 I 19 | X A2 7 -550 100 200 R 50 50 1 1 I 20 | X A3 8 -550 0 200 R 50 50 1 1 I 21 | X A4 9 -550 -100 200 R 50 50 1 1 I 22 | X A5 10 -550 -200 200 R 50 50 1 1 I 23 | X D2 20 550 -300 200 L 50 50 1 1 B 24 | X D12 30 550 700 200 L 50 50 1 1 B 25 | X A6 11 -550 -300 200 R 50 50 1 1 I 26 | X D3 21 550 -200 200 L 50 50 1 1 B 27 | X 5V 12 -550 -400 200 R 50 50 1 1 w 28 | X D4 22 550 -100 200 L 50 50 1 1 B 29 | X RST 13 -550 -500 200 R 50 50 1 1 I 30 | X D5 23 550 0 200 L 50 50 1 1 B 31 | X GND 14 -550 -600 200 R 50 50 1 1 P 32 | X D6 24 550 100 200 L 50 50 1 1 B 33 | X VIN 15 -550 -700 200 R 50 50 1 1 W 34 | X D7 25 550 200 200 L 50 50 1 1 B 35 | X TX 16 550 -700 200 L 50 50 1 1 O 36 | X D8 26 550 300 200 L 50 50 1 1 B 37 | X RX 17 550 -600 200 L 50 50 1 1 I 38 | X D9 27 550 400 200 L 50 50 1 1 B 39 | X RST 18 550 -500 200 L 50 50 1 1 I 40 | X D10 28 550 500 200 L 50 50 1 1 B 41 | X GND 19 550 -400 200 L 50 50 1 1 P 42 | X D11 29 550 600 200 L 50 50 1 1 B 43 | ENDDRAW 44 | ENDDEF 45 | # 46 | # Battery 47 | # 48 | DEF Battery BT 0 0 N N 1 F N 49 | F0 "BT" 100 100 50 H V L CNN 50 | F1 "Battery" 100 0 50 H V L CNN 51 | F2 "" 0 60 50 V I C CNN 52 | F3 "" 0 60 50 V I C CNN 53 | DRAW 54 | S -80 -55 80 -65 0 1 0 F 55 | S -80 70 80 60 0 1 0 F 56 | S -52 -78 50 -98 0 1 0 F 57 | S -52 47 50 27 0 1 0 F 58 | P 2 0 1 0 0 -60 0 -50 N 59 | P 2 0 1 0 0 -40 0 -30 N 60 | P 2 0 1 0 0 -20 0 -10 N 61 | P 2 0 1 0 0 0 0 10 N 62 | P 2 0 1 0 0 20 0 30 N 63 | P 2 0 1 0 0 70 0 100 N 64 | P 2 0 1 10 10 105 50 105 N 65 | P 2 0 1 10 30 125 30 85 N 66 | X + 1 0 200 100 D 50 50 1 1 P 67 | X - 2 0 -200 100 U 50 50 1 1 P 68 | ENDDRAW 69 | ENDDEF 70 | # 71 | # Display 72 | # 73 | DEF Display OLED 0 40 Y Y 1 F N 74 | F0 "OLED" 0 0 60 H V C CNN 75 | F1 "Display" 350 -600 60 H V C CNN 76 | F2 "" 0 -100 60 H I C CNN 77 | F3 "" 0 -100 60 H I C CNN 78 | DRAW 79 | S 500 -500 -500 500 0 1 0 N 80 | X GND 1 -150 700 200 D 50 50 1 1 I 81 | X VCC 2 -50 700 200 D 50 50 1 1 I 82 | X SCL 3 50 700 200 D 50 50 1 1 I 83 | X SDA 4 150 700 200 D 50 50 1 1 I 84 | ENDDRAW 85 | ENDDEF 86 | # 87 | # GND 88 | # 89 | DEF GND #PWR 0 0 Y Y 1 F P 90 | F0 "#PWR" 0 -250 50 H I C CNN 91 | F1 "GND" 0 -150 50 H V C CNN 92 | F2 "" 0 0 50 H I C CNN 93 | F3 "" 0 0 50 H I C CNN 94 | DRAW 95 | P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N 96 | X ~ ~ 0 0 0 D 50 50 1 1 W N 97 | ENDDRAW 98 | ENDDEF 99 | # 100 | # R 101 | # 102 | DEF R R 0 0 N Y 1 F N 103 | F0 "R" 80 0 50 V V C CNN 104 | F1 "R" 0 0 50 V V C CNN 105 | F2 "" -70 0 50 V I C CNN 106 | F3 "" 0 0 50 H I C CNN 107 | $FPLIST 108 | R_* 109 | R_* 110 | $ENDFPLIST 111 | DRAW 112 | S -40 -100 40 100 0 1 10 N 113 | X ~ 1 0 150 50 D 50 50 1 1 P 114 | X ~ 2 0 -150 50 U 50 50 1 1 P 115 | ENDDRAW 116 | ENDDEF 117 | # 118 | # SW_Push 119 | # 120 | DEF SW_Push SW 0 40 N N 1 F N 121 | F0 "SW" 50 100 50 H V L CNN 122 | F1 "SW_Push" 0 -60 50 H V C CNN 123 | F2 "" 0 200 50 H I C CNN 124 | F3 "" 0 200 50 H I C CNN 125 | DRAW 126 | C -80 0 20 0 1 0 N 127 | C 80 0 20 0 1 0 N 128 | P 2 0 1 0 0 50 0 120 N 129 | P 2 0 1 0 100 50 -100 50 N 130 | X 1 1 -200 0 100 R 50 50 0 1 P 131 | X 2 2 200 0 100 L 50 50 0 1 P 132 | ENDDRAW 133 | ENDDEF 134 | # 135 | # nRF24L01+ 136 | # 137 | DEF nRF24L01+ Radio 0 40 Y Y 1 F N 138 | F0 "Radio" -50 400 60 H V C CNN 139 | F1 "nRF24L01+" 300 -400 60 H V C CNN 140 | F2 "" 350 200 60 H I C CNN 141 | F3 "" 350 200 60 H I C CNN 142 | DRAW 143 | S 500 -300 -600 300 0 1 0 N 144 | X GND 1 700 -200 200 L 50 50 1 1 I 145 | X VCC 2 700 -100 200 L 50 50 1 1 I 146 | X CE 3 700 100 200 L 50 50 1 1 I 147 | X CSN 4 -500 -500 200 U 50 50 1 1 I 148 | X SCK 5 -400 -500 200 U 50 50 1 1 I 149 | X MOSI 6 -300 -500 200 U 50 50 1 1 I 150 | X MISO 7 -200 -500 200 U 50 50 1 1 I 151 | X IRQ 8 700 200 200 L 50 50 1 1 I 152 | ENDDRAW 153 | ENDDEF 154 | # 155 | #End Library 156 | -------------------------------------------------------------------------------- /diagrams/schematic/Firefly.kicad_pcb: -------------------------------------------------------------------------------- 1 | (kicad_pcb (version 4) (host kicad "dummy file") ) 2 | -------------------------------------------------------------------------------- /diagrams/schematic/Firefly.pro: -------------------------------------------------------------------------------- 1 | update=Wednesday, October 25, 2017 'amt' 02:02:24 am 2 | version=1 3 | last_client=kicad 4 | [pcbnew] 5 | version=1 6 | LastNetListRead= 7 | UseCmpFile=1 8 | PadDrill=0.600000000000 9 | PadDrillOvalY=0.600000000000 10 | PadSizeH=1.500000000000 11 | PadSizeV=1.500000000000 12 | PcbTextSizeV=1.500000000000 13 | PcbTextSizeH=1.500000000000 14 | PcbTextThickness=0.300000000000 15 | ModuleTextSizeV=1.000000000000 16 | ModuleTextSizeH=1.000000000000 17 | ModuleTextSizeThickness=0.150000000000 18 | SolderMaskClearance=0.000000000000 19 | SolderMaskMinWidth=0.000000000000 20 | DrawSegmentWidth=0.200000000000 21 | BoardOutlineThickness=0.100000000000 22 | ModuleOutlineThickness=0.150000000000 23 | [cvpcb] 24 | version=1 25 | NetIExt=net 26 | [eeschema] 27 | version=1 28 | LibDir= 29 | [eeschema/libraries] 30 | LibName1=power 31 | LibName2=device 32 | LibName3=transistors 33 | LibName4=conn 34 | LibName5=linear 35 | LibName6=regul 36 | LibName7=74xx 37 | LibName8=cmos4000 38 | LibName9=adc-dac 39 | LibName10=memory 40 | LibName11=xilinx 41 | LibName12=microcontrollers 42 | LibName13=dsp 43 | LibName14=microchip 44 | LibName15=analog_switches 45 | LibName16=motorola 46 | LibName17=texas 47 | LibName18=intel 48 | LibName19=audio 49 | LibName20=interface 50 | LibName21=digital-audio 51 | LibName22=philips 52 | LibName23=cypress 53 | LibName24=siliconi 54 | LibName25=opto 55 | LibName26=atmel 56 | LibName27=contrib 57 | LibName28=valves 58 | LibName29=./FireflyComponents 59 | LibName30=switches 60 | [general] 61 | version=1 62 | [schematic_editor] 63 | version=1 64 | PageLayoutDescrFile= 65 | PlotDirectoryName=plot/ 66 | SubpartIdSeparator=0 67 | SubpartFirstId=65 68 | NetFmtName= 69 | SpiceForceRefPrefix=0 70 | SpiceUseNetNumbers=0 71 | LabSize=60 72 | -------------------------------------------------------------------------------- /diagrams/schematic/Firefly.sch: -------------------------------------------------------------------------------- 1 | EESchema Schematic File Version 2 2 | LIBS:power 3 | LIBS:device 4 | LIBS:transistors 5 | LIBS:conn 6 | LIBS:linear 7 | LIBS:regul 8 | LIBS:74xx 9 | LIBS:cmos4000 10 | LIBS:adc-dac 11 | LIBS:memory 12 | LIBS:xilinx 13 | LIBS:microcontrollers 14 | LIBS:dsp 15 | LIBS:microchip 16 | LIBS:analog_switches 17 | LIBS:motorola 18 | LIBS:texas 19 | LIBS:intel 20 | LIBS:audio 21 | LIBS:interface 22 | LIBS:digital-audio 23 | LIBS:philips 24 | LIBS:cypress 25 | LIBS:siliconi 26 | LIBS:opto 27 | LIBS:atmel 28 | LIBS:contrib 29 | LIBS:valves 30 | LIBS:FireflyComponents 31 | LIBS:switches 32 | LIBS:Firefly-cache 33 | EELAYER 25 0 34 | EELAYER END 35 | $Descr A4 11693 8268 36 | encoding utf-8 37 | Sheet 1 1 38 | Title "Preliminary Firefly Schematic" 39 | Date "2017-10-24" 40 | Rev "0.1" 41 | Comp "" 42 | Comment1 "" 43 | Comment2 "if you are familiar with circuit design and/or kicad." 44 | Comment3 "many changes. Please feel free to update this document" 45 | Comment4 "This is a preliminary document which will likely undergo" 46 | $EndDescr 47 | $Comp 48 | L Display OLED1 49 | U 1 1 59EEC845 50 | P 3350 4100 51 | F 0 "OLED1" H 3350 4100 60 0000 C CNN 52 | F 1 "Display" H 3700 3500 60 0000 C CNN 53 | F 2 "" H 3350 4000 60 0001 C CNN 54 | F 3 "" H 3350 4000 60 0001 C CNN 55 | 1 3350 4100 56 | 1 0 0 -1 57 | $EndComp 58 | $Comp 59 | L R 10kΩ 60 | U 1 1 59EEC8B2 61 | P 6400 5100 62 | F 0 "10kΩ" V 6480 5100 50 0000 C CNN 63 | F 1 "R1" V 6400 5100 50 0000 C CNN 64 | F 2 "" V 6330 5100 50 0001 C CNN 65 | F 3 "" H 6400 5100 50 0001 C CNN 66 | 1 6400 5100 67 | 1 0 0 -1 68 | $EndComp 69 | $Comp 70 | L SW_Push SW1 71 | U 1 1 59EECD23 72 | P 5700 4850 73 | F 0 "SW1" H 5750 4950 50 0000 L CNN 74 | F 1 "SW_Push" H 5700 4790 50 0000 C CNN 75 | F 2 "" H 5700 5050 50 0001 C CNN 76 | F 3 "" H 5700 5050 50 0001 C CNN 77 | 1 5700 4850 78 | 1 0 0 -1 79 | $EndComp 80 | $Comp 81 | L GND #PWR? 82 | U 1 1 59EECD81 83 | P 8300 2150 84 | F 0 "#PWR?" H 8300 1900 50 0001 C CNN 85 | F 1 "GND" H 8300 2000 50 0000 C CNN 86 | F 2 "" H 8300 2150 50 0001 C CNN 87 | F 3 "" H 8300 2150 50 0001 C CNN 88 | 1 8300 2150 89 | 1 0 0 -1 90 | $EndComp 91 | $Comp 92 | L GND #PWR? 93 | U 1 1 59EECDA4 94 | P 6400 5450 95 | F 0 "#PWR?" H 6400 5200 50 0001 C CNN 96 | F 1 "GND" H 6400 5300 50 0000 C CNN 97 | F 2 "" H 6400 5450 50 0001 C CNN 98 | F 3 "" H 6400 5450 50 0001 C CNN 99 | 1 6400 5450 100 | 1 0 0 -1 101 | $EndComp 102 | $Comp 103 | L GND #PWR? 104 | U 1 1 59EECDC7 105 | P 5050 4500 106 | F 0 "#PWR?" H 5050 4250 50 0001 C CNN 107 | F 1 "GND" H 5050 4350 50 0000 C CNN 108 | F 2 "" H 5050 4500 50 0001 C CNN 109 | F 3 "" H 5050 4500 50 0001 C CNN 110 | 1 5050 4500 111 | 1 0 0 -1 112 | $EndComp 113 | $Comp 114 | L GND #PWR? 115 | U 1 1 59EECDEA 116 | P 2500 3050 117 | F 0 "#PWR?" H 2500 2800 50 0001 C CNN 118 | F 1 "GND" H 2500 2900 50 0000 C CNN 119 | F 2 "" H 2500 3050 50 0001 C CNN 120 | F 3 "" H 2500 3050 50 0001 C CNN 121 | 1 2500 3050 122 | 1 0 0 -1 123 | $EndComp 124 | $Comp 125 | L nRF24L01+ Radio1 126 | U 1 1 59EEE67C 127 | P 7500 1700 128 | F 0 "Radio1" H 7450 2100 60 0000 C CNN 129 | F 1 "nRF24L01+" H 7800 1300 60 0000 C CNN 130 | F 2 "" H 7850 1900 60 0001 C CNN 131 | F 3 "" H 7850 1900 60 0001 C CNN 132 | 1 7500 1700 133 | 1 0 0 -1 134 | $EndComp 135 | $Comp 136 | L ArduinoNano PC1 137 | U 1 1 59EEC47E 138 | P 5700 3600 139 | F 0 "PC1" H 5700 4500 60 0000 C CNN 140 | F 1 "Arduino Nano" H 5700 2700 60 0000 C CNN 141 | F 2 "" H 5700 2700 60 0001 C CNN 142 | F 3 "" H 5700 2700 60 0001 C CNN 143 | 1 5700 3600 144 | 1 0 0 -1 145 | $EndComp 146 | Wire Wire Line 147 | 4700 2900 4700 3700 148 | Wire Wire Line 149 | 4700 3700 5150 3700 150 | Wire Wire Line 151 | 3400 2800 4800 2800 152 | Wire Wire Line 153 | 4800 2800 4800 3800 154 | Wire Wire Line 155 | 4800 3800 5150 3800 156 | Wire Wire Line 157 | 3300 2700 4900 2700 158 | Wire Wire Line 159 | 4900 2700 4900 4850 160 | Wire Wire Line 161 | 4900 4000 5150 4000 162 | Wire Wire Line 163 | 4900 4850 5500 4850 164 | Connection ~ 4900 4000 165 | Wire Wire Line 166 | 6400 3900 6250 3900 167 | Wire Wire Line 168 | 6400 3900 6400 4950 169 | Wire Wire Line 170 | 6400 4850 5900 4850 171 | Connection ~ 6400 4850 172 | Wire Wire Line 173 | 3200 3400 3200 2700 174 | Wire Wire Line 175 | 3200 2700 2500 2700 176 | Wire Wire Line 177 | 2500 2700 2500 3050 178 | Wire Wire Line 179 | 3300 3400 3300 2700 180 | Wire Wire Line 181 | 3400 2800 3400 3400 182 | Wire Wire Line 183 | 4700 2900 3500 2900 184 | Wire Wire Line 185 | 3500 2900 3500 3400 186 | Wire Wire Line 187 | 7100 2200 7100 2400 188 | Wire Wire Line 189 | 7100 2400 5000 2400 190 | Wire Wire Line 191 | 5000 2400 5000 2900 192 | Wire Wire Line 193 | 5000 2900 5150 2900 194 | Wire Wire Line 195 | 7000 2200 7000 3100 196 | Wire Wire Line 197 | 7000 3100 6250 3100 198 | Wire Wire Line 199 | 7200 2200 7200 3000 200 | Wire Wire Line 201 | 7200 3000 6250 3000 202 | Wire Wire Line 203 | 6250 2900 7300 2900 204 | Wire Wire Line 205 | 7300 2900 7300 2200 206 | Wire Wire Line 207 | 5150 3000 5100 3000 208 | Wire Wire Line 209 | 5100 3000 5100 2500 210 | Wire Wire Line 211 | 5100 2500 8600 2500 212 | Wire Wire Line 213 | 8600 2500 8600 1800 214 | Wire Wire Line 215 | 8600 1800 8200 1800 216 | Wire Wire Line 217 | 8300 2150 8300 1900 218 | Wire Wire Line 219 | 8300 1900 8200 1900 220 | Wire Wire Line 221 | 5150 4200 5050 4200 222 | Wire Wire Line 223 | 5050 4200 5050 4500 224 | $Comp 225 | L Battery BT1 226 | U 1 1 59EEF90B 227 | P 4300 5200 228 | F 0 "BT1" H 4400 5300 50 0000 L CNN 229 | F 1 "Battery" H 4400 5200 50 0000 L CNN 230 | F 2 "" V 4300 5260 50 0001 C CNN 231 | F 3 "" V 4300 5260 50 0001 C CNN 232 | 1 4300 5200 233 | 1 0 0 -1 234 | $EndComp 235 | Wire Wire Line 236 | 5150 4300 4300 4300 237 | Wire Wire Line 238 | 4300 4300 4300 5000 239 | $Comp 240 | L GND #PWR? 241 | U 1 1 59EEF9D1 242 | P 4300 5650 243 | F 0 "#PWR?" H 4300 5400 50 0001 C CNN 244 | F 1 "GND" H 4300 5500 50 0000 C CNN 245 | F 2 "" H 4300 5650 50 0001 C CNN 246 | F 3 "" H 4300 5650 50 0001 C CNN 247 | 1 4300 5650 248 | 1 0 0 -1 249 | $EndComp 250 | Wire Wire Line 251 | 4300 5400 4300 5650 252 | Text Label 3800 5200 0 60 ~ 0 253 | CR2032 254 | Text Label 3900 5300 0 60 ~ 0 255 | (2x) 256 | Wire Wire Line 257 | 6400 5250 6400 5450 258 | $EndSCHEMATC 259 | -------------------------------------------------------------------------------- /diagrams/schematic/FireflyComponents.dcm: -------------------------------------------------------------------------------- 1 | EESchema-DOCLIB Version 2.0 2 | # 3 | #End Doc Library 4 | -------------------------------------------------------------------------------- /diagrams/schematic/FireflyComponents.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.3 2 | #encoding utf-8 3 | # 4 | # ArduinoNano 5 | # 6 | DEF ArduinoNano PC 0 40 Y Y 1 F N 7 | F0 "PC" 0 900 60 H V C CNN 8 | F1 "ArduinoNano" 0 -900 60 H V C CNN 9 | F2 "" 0 -900 60 H I C CNN 10 | F3 "" 0 -900 60 H I C CNN 11 | DRAW 12 | S 350 -800 -350 800 0 1 0 N 13 | X D13 1 -550 700 200 R 50 50 1 1 B 14 | X 3V3 2 -550 600 200 R 50 50 1 1 w 15 | X AREF 3 -550 500 200 R 50 50 1 1 W 16 | X A0 4 -550 400 200 R 50 50 1 1 I 17 | X A1 5 -550 300 200 R 50 50 1 1 I 18 | X A2 6 -550 200 200 R 50 50 1 1 I 19 | X A2 7 -550 100 200 R 50 50 1 1 I 20 | X A3 8 -550 0 200 R 50 50 1 1 I 21 | X A4 9 -550 -100 200 R 50 50 1 1 I 22 | X A5 10 -550 -200 200 R 50 50 1 1 I 23 | X D2 20 550 -300 200 L 50 50 1 1 B 24 | X D12 30 550 700 200 L 50 50 1 1 B 25 | X A6 11 -550 -300 200 R 50 50 1 1 I 26 | X D3 21 550 -200 200 L 50 50 1 1 B 27 | X 5V 12 -550 -400 200 R 50 50 1 1 w 28 | X D4 22 550 -100 200 L 50 50 1 1 B 29 | X RST 13 -550 -500 200 R 50 50 1 1 I 30 | X D5 23 550 0 200 L 50 50 1 1 B 31 | X GND 14 -550 -600 200 R 50 50 1 1 P 32 | X D6 24 550 100 200 L 50 50 1 1 B 33 | X VIN 15 -550 -700 200 R 50 50 1 1 W 34 | X D7 25 550 200 200 L 50 50 1 1 B 35 | X TX 16 550 -700 200 L 50 50 1 1 O 36 | X D8 26 550 300 200 L 50 50 1 1 B 37 | X RX 17 550 -600 200 L 50 50 1 1 I 38 | X D9 27 550 400 200 L 50 50 1 1 B 39 | X RST 18 550 -500 200 L 50 50 1 1 I 40 | X D10 28 550 500 200 L 50 50 1 1 B 41 | X GND 19 550 -400 200 L 50 50 1 1 P 42 | X D11 29 550 600 200 L 50 50 1 1 B 43 | ENDDRAW 44 | ENDDEF 45 | # 46 | # Display 47 | # 48 | DEF Display OLED 0 40 Y Y 1 F N 49 | F0 "OLED" 0 0 60 H V C CNN 50 | F1 "Display" 350 -600 60 H V C CNN 51 | F2 "" 0 -100 60 H I C CNN 52 | F3 "" 0 -100 60 H I C CNN 53 | DRAW 54 | S 500 -500 -500 500 0 1 0 N 55 | X GND 1 -150 700 200 D 50 50 1 1 I 56 | X VCC 2 -50 700 200 D 50 50 1 1 I 57 | X SCL 3 50 700 200 D 50 50 1 1 I 58 | X SDA 4 150 700 200 D 50 50 1 1 I 59 | ENDDRAW 60 | ENDDEF 61 | # 62 | # nRF24L01+ 63 | # 64 | DEF nRF24L01+ Radio 0 40 Y Y 1 F N 65 | F0 "Radio" -50 400 60 H V C CNN 66 | F1 "nRF24L01+" 300 -400 60 H V C CNN 67 | F2 "" 350 200 60 H I C CNN 68 | F3 "" 350 200 60 H I C CNN 69 | DRAW 70 | S 500 -300 -600 300 0 1 0 N 71 | X GND 1 700 -200 200 L 50 50 1 1 I 72 | X VCC 2 700 -100 200 L 50 50 1 1 I 73 | X CE 3 700 100 200 L 50 50 1 1 I 74 | X CSN 4 -500 -500 200 U 50 50 1 1 I 75 | X SCK 5 -400 -500 200 U 50 50 1 1 I 76 | X MOSI 6 -300 -500 200 U 50 50 1 1 I 77 | X MISO 7 -200 -500 200 U 50 50 1 1 I 78 | X IRQ 8 700 200 200 L 50 50 1 1 I 79 | ENDDRAW 80 | ENDDEF 81 | # 82 | #End Library 83 | -------------------------------------------------------------------------------- /source/firefly/firefly.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * Copyright (c) 2017 Yuet Loo Wong 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | 27 | /** 28 | * High-Level Information 29 | * 30 | * Scope Using Functions 31 | * 32 | * We move a lot of memory allocation-heavy operations into functions to ensure 33 | * the compiler correctly generates code that cleans up after itself and releaeses 34 | * memory once is it no longer needed. We need every ounce of memory and storage 35 | * we can get our hands on. 36 | * 37 | * 38 | * Malloc vs stack 39 | * 40 | * Each instance of malloc/free vs using stack space has been carefully examined 41 | * as assembly and comparing generated code. In some instances, the code is inlined 42 | * resulting in smaller code (odd, but poke around in the obj-dump to see weird 43 | * reasons why, such as how smallish (less than the number of registers) arrays get 44 | * passed in as individual parameters. 45 | * 46 | * I suspect this will change over time, and once a newer version of the compiler 47 | * is used, with better -Os support, we can move more of these to use the stack. 48 | * 49 | * 50 | * Available Trade-offs 51 | * 52 | * As we add more features, we may need to slim down the Storage or SRAM, depending on 53 | * what we are low on. Some features can trade one for the other and some features we 54 | * may be able to cripple in subtle ways to gain the space we need. Here is a list of 55 | * current options: 56 | * 57 | * - The QR code may have penalty scores removed and always choose mask 0; less 58 | * standards compliant but gains 1318 bytes of storage. 59 | * - Use uECC Optimiation level 1 (instead of 2); increases signing from 6s to 7.2s but 60 | * gains 2168 bytes of storage. 61 | */ 62 | 63 | 64 | 65 | /** 66 | * Library Includes 67 | * 68 | * Each of these includes are in the ../libs/ folder and should be added to your 69 | * Arduino `libraries` folder via a symlink. 70 | * 71 | * e.g. ln -s /home/ricmoo/firefly/wallet/source/libs/ethers /home/ricmoo/Arduino/libraries/ethers 72 | */ 73 | 74 | 75 | /** 76 | * Private Key 77 | * 78 | * KEEP THIS SECRET! 79 | * 80 | * Anyone with this private key can steal your ether and any other crypto-assets associated with it. 81 | * 82 | * This is hard-coded, unencrypted into the version 0 (v0) protocol, but in the future versions 83 | * of this software will have much more clever ways to create and store your private key. 84 | * 85 | * To generate a new random private key, in the terminal type: 86 | * 87 | * /home/ethers> python -c 'import os; print ", ".join([ str(ord(c)) for c in bytes(os.urandom(32)) ])' 88 | * 89 | */ 90 | 91 | static const uint8_t privateKey[] = { 92 | #error You MUST generate a new private key and add it here, then remove this error line 93 | }; 94 | 95 | 96 | // For debugging, we want to enable Serial dumping, but in the final product 97 | // removing the Serial printing squeaks in a bit more room for larger transactions 98 | // received over BLECast. 99 | #define DEBUG_SERIAL 0 100 | 101 | 102 | // The Ethereum library (signing, parsing transactions and cryptographic hashes) 103 | #include 104 | 105 | // A customied version of my QR code library, to improve memory, flash and storage space as needed 106 | #include 107 | 108 | // A customized version of my BLECast library, to improve memory, flash and storage space as needed 109 | // In the future this will also be used for its AES implementation for encrypted private keys 110 | #include 111 | 112 | // Our custom zero-memory display driver 113 | #include "firefly_display.h" 114 | 115 | typedef enum ErrorCode { 116 | ErrorCodeNone = 0, 117 | ErrorCodeOutOfMemory = 31, 118 | ErrorCodeSigningError = 41, 119 | ErrorCodeInvalidKey = 42, 120 | ErrorCodeStorageFailed = 51, 121 | } ErrorCode; 122 | 123 | 124 | // The keccak256(privateKey) 125 | #define EEPROM_DATA_OFFSET_CHECKSUM (0) 126 | #define EEPROM_DATA_LENGTH_CHECKSUM (32) 127 | 128 | // The secret key (symmetric) used for phone to send encrypted messages 129 | #define EEPROM_DATA_OFFSET_PAIR_SECRET (EEPROM_DATA_OFFSET_CHECKSUM + EEPROM_DATA_LENGTH_CHECKSUM) 130 | #define EEPROM_DATA_LENGTH_PAIR_SECRET (16) 131 | 132 | // The address of the private key 133 | #define EEPROM_DATA_OFFSET_ADDRESS (EEPROM_DATA_OFFSET_PAIR_SECRET + EEPROM_DATA_LENGTH_PAIR_SECRET) 134 | #define EEPROM_DATA_LENGTH_ADDRESS (20) 135 | 136 | // The wallet URI (checksummed address with "ethereum:" scheme) 137 | #define EEPROM_DATA_OFFSET_ADDRESS_URI (EEPROM_DATA_OFFSET_ADDRESS + EEPROM_DATA_LENGTH_ADDRESS) 138 | #define EEPROM_DATA_LENGTH_ADDRESS_URI (9 + ETHERS_CHECKSUM_ADDRESS_LENGTH) 139 | 140 | 141 | extern unsigned int __heap_start; 142 | extern void *__brkval; 143 | 144 | /* 145 | * The free list structure as maintained by the 146 | * avr-libc memory allocation routines. 147 | */ 148 | struct __freelist { 149 | size_t sz; 150 | struct __freelist *nx; 151 | }; 152 | 153 | /* The head of the free list structure */ 154 | extern struct __freelist *__flp; 155 | 156 | 157 | /* Calculates the size of the free list */ 158 | int freeListSize() { 159 | struct __freelist* current; 160 | int total = 0; 161 | for (current = __flp; current; current = current->nx) { 162 | total += 2; /* Add two bytes for the memory block's header */ 163 | total += (int) current->sz; 164 | } 165 | return total; 166 | } 167 | 168 | int freeMemory() { 169 | int free_memory; 170 | if ((int)__brkval == 0) { 171 | free_memory = ((int)&free_memory) - ((int)&__heap_start); 172 | } else { 173 | free_memory = ((int)&free_memory) - ((int)__brkval); 174 | free_memory += freeListSize(); 175 | } 176 | return free_memory; 177 | } 178 | 179 | 180 | /** 181 | * Our Hardware Configuration 182 | */ 183 | 184 | #define BUTTON_PIN (2) 185 | 186 | // ATmega 328 187 | #define RADIO_PIN_CE (9) 188 | #define RADIO_PIN_CSN (10) 189 | 190 | // ATmega 2560 191 | //#define RADIO_PIN_CE (40) 192 | //#define RADIO_PIN_CSN (53) 193 | 194 | #define DISPLAY_ADDRESS (0x3c) 195 | 196 | 197 | // Dumps a buffer 198 | static void dumpBuffer(uint8_t *buffer, uint16_t length) { 199 | #if DEBUG_SERIAL == 1 200 | //Serial.print("0x"); 201 | for(uint16_t i = 0; i < length; i++) { 202 | // Prefix single nibble with a "0" 203 | if (buffer[i] < 0x10) { Serial.print("0"); } 204 | Serial.print((uint8_t)(buffer[i]), HEX); 205 | Serial.print(' '); 206 | } 207 | Serial.println(""); 208 | #endif 209 | } 210 | 211 | void dumpFreeMemory() { 212 | #if DEBUG_SERIAL == 1 213 | display_debug_char(DISPLAY_ADDRESS, 'M'); 214 | display_debug_int(DISPLAY_ADDRESS, freeMemory()); 215 | display_debug_char(DISPLAY_ADDRESS, 'H'); 216 | display_debug_int(DISPLAY_ADDRESS, (uint32_t)(&__heap_start)); 217 | display_debug_char(DISPLAY_ADDRESS, 'B'); 218 | display_debug_int(DISPLAY_ADDRESS, (uint32_t)(&__brkval)); 219 | waitForButton(); 220 | display_clear(DISPLAY_ADDRESS); 221 | #endif 222 | } 223 | 224 | static char getHexNibble(uint8_t value) { 225 | value &= 0x0f; 226 | if (value <= 9) { return '0' + value; } 227 | return 'A' + (value - 10); 228 | } 229 | 230 | // If something goes wrong, indicate the error code and halt 231 | static void crash(ErrorCode errorCode, uint16_t lineNo) { 232 | if (errorCode != ErrorCodeNone) { 233 | display_invert(DISPLAY_ADDRESS, true); 234 | display_clear(DISPLAY_ADDRESS); 235 | 236 | display_debug_text(DISPLAY_ADDRESS, (const char*)"ERR"); 237 | display_debug_int(DISPLAY_ADDRESS, errorCode); 238 | 239 | display_debug_char(DISPLAY_ADDRESS, 'L'); 240 | display_debug_int(DISPLAY_ADDRESS, lineNo); 241 | } 242 | 243 | while (1) { delay(0x7fffffff); } 244 | } 245 | 246 | const char addressURIPrefix[] PROGMEM = { 247 | 'e', 't', 'h', 'e', 'r', 'e', 'u', 'm', ':' 248 | }; 249 | 250 | const char messageHeaderPrefix[] PROGMEM = { 251 | 0x19, 'E', 't', 'h', 'e', 'r', 'e', 'u', 'm', ' ', 'S', 'i', 'g', 'n', 'e', 'd', ' ', 'M', 'e', 's', 's', 'a', 'g', 'e', ':', '\n' 252 | }; 253 | 254 | static bool equalsStorage(uint16_t offset, uint16_t length, uint8_t *buffer) { 255 | for (uint16_t i = 0; i < length; i++) { 256 | if (buffer[i] != eeprom_read_byte((uint8_t*)(offset + i))) { 257 | return false; 258 | } 259 | } 260 | return true; 261 | } 262 | 263 | static void readStorage(uint16_t offset, uint16_t length, uint8_t *buffer) { 264 | for (uint16_t i = 0; i < length; i++) { 265 | buffer[i] = eeprom_read_byte((uint8_t*)(offset + i)); 266 | } 267 | } 268 | 269 | static void writeStorage(uint16_t offset, uint16_t length, uint8_t *buffer) { 270 | for (uint16_t i = 0; i < length; i++) { 271 | eeprom_write_byte((uint8_t*)(offset + i), buffer[i]); 272 | } 273 | 274 | delay(100); 275 | 276 | // Make sure the data was written correctly 277 | if (!equalsStorage(offset, length, buffer)) { 278 | crash(ErrorCodeStorageFailed, __LINE__); 279 | } 280 | } 281 | 282 | static void generateCache() { 283 | uint8_t checksum[32]; 284 | ethers_keccak256(privateKey, 32, checksum); 285 | 286 | // Already generated all the cached data (note: this is written last like a journal commit) 287 | if (equalsStorage(EEPROM_DATA_OFFSET_CHECKSUM, EEPROM_DATA_LENGTH_CHECKSUM, checksum)) { 288 | return; 289 | } 290 | 291 | // The largest amount of memory we need for anything we cache 292 | uint8_t scratch[9 + ETHERS_CHECKSUM_ADDRESS_LENGTH]; 293 | 294 | // ********* 295 | // Compute the address (leave some space at the beginning for the URI scheme in the next part) 296 | bool success = ethers_privateKeyToAddress(privateKey, &scratch[9]); 297 | if (!success) { crash(ErrorCodeInvalidKey, __LINE__); } 298 | writeStorage(EEPROM_DATA_OFFSET_ADDRESS, EEPROM_DATA_LENGTH_ADDRESS, &scratch[9]); 299 | 300 | // ********* 301 | // Generate the wallet URI ("ethereum:" + checksumAddress); 302 | memcpy_P(scratch, addressURIPrefix, 9); 303 | 304 | // Place the null-terminated, checksum address into the string after the "ethereum:" 305 | ethers_addressToChecksumAddress(&scratch[9], (char*)&scratch[9]); 306 | 307 | writeStorage(EEPROM_DATA_OFFSET_ADDRESS_URI, EEPROM_DATA_LENGTH_ADDRESS_URI, scratch); 308 | 309 | // ********* 310 | // Compute the pairing secret keccak(0x00 || keccak(privateKey))[:16] 311 | ethers_keccak256(checksum, 32, &scratch[33]); 312 | scratch[32] = 0; 313 | ethers_keccak256(&scratch[32], 33, scratch); 314 | 315 | writeStorage(EEPROM_DATA_OFFSET_PAIR_SECRET, EEPROM_DATA_LENGTH_PAIR_SECRET, scratch); 316 | 317 | // ********* 318 | // Compute the pairing secret keccak(0x00 || keccak(privateKey))[:16] 319 | writeStorage(EEPROM_DATA_OFFSET_CHECKSUM, EEPROM_DATA_LENGTH_CHECKSUM, checksum); 320 | } 321 | 322 | static void showAddress() { 323 | 324 | // We use this scratch space for (QR Code module data) ("ethereum:" + checkSumAddress) 325 | uint8_t qrCodeBufferSize = qrcode_getBufferSize(3); 326 | uint8_t *scratch = (uint8_t*)malloc(qrCodeBufferSize + (9 + ETHERS_CHECKSUM_ADDRESS_LENGTH)); 327 | 328 | // Load the cached URI from EEPROM 329 | readStorage(EEPROM_DATA_OFFSET_ADDRESS_URI, EEPROM_DATA_LENGTH_ADDRESS_URI, &scratch[qrCodeBufferSize]); 330 | 331 | QRCode qrcode; 332 | qrcode_initText(&qrcode, &scratch[0], 3, 0, (char*)(&scratch[qrCodeBufferSize])); 333 | display_qrcodes(DISPLAY_ADDRESS, &qrcode, NULL); 334 | 335 | free(scratch); 336 | } 337 | 338 | static void expandHexNibbles(uint8_t *buffer, uint8_t length) { 339 | for (int8_t i = length - 1; i >= 0; i--) { 340 | buffer[2 * i + 1] = getHexNibble(buffer[i]); 341 | buffer[2 * i + 0] = getHexNibble(buffer[i] >> 4); 342 | } 343 | } 344 | 345 | static void showPairingScreen() { 346 | // We use this scratch space for (QR Code Address) (temporary space to generate string) 347 | uint8_t qrCodeBufferSize = qrcode_getBufferSize(3); 348 | uint8_t *scratch = (uint8_t*)malloc(qrCodeBufferSize + 77); 349 | 350 | uint8_t *pairString = (&scratch[qrCodeBufferSize]); 351 | pairString[0] = 'V'; 352 | pairString[1] = '0'; 353 | pairString[2] = '/'; 354 | pairString[43] = '/'; 355 | pairString[76] = 0; 356 | 357 | // Put the raw binary in the pairing string and expand it into nibbles 358 | readStorage(EEPROM_DATA_OFFSET_ADDRESS, EEPROM_DATA_LENGTH_ADDRESS, &pairString[3]); 359 | expandHexNibbles(&pairString[3], 20); 360 | 361 | // Put the secret key (for transmission) in the pairing string and expand it into nibbles 362 | readStorage(EEPROM_DATA_OFFSET_PAIR_SECRET, EEPROM_DATA_LENGTH_PAIR_SECRET, &pairString[44]); 363 | expandHexNibbles(&pairString[44], 16); 364 | 365 | // Show the QR code 366 | QRCode qrcode; 367 | qrcode_initText(&qrcode, &scratch[0], 3, 0, (char*)pairString); 368 | display_qrcodes(DISPLAY_ADDRESS, &qrcode, NULL); 369 | 370 | free(scratch); 371 | } 372 | 373 | #define SHOW_PAIRIING_SCREEN_DURATION 3000 374 | 375 | // Note: Messages over BLECase are sent as 16 byte chunks, where each chunk contains a 4 byte 376 | // preamble (3 bytes checksum, 1 bit END_FLAG, 1 bit PARTIAL_FLAG, 6 bits chunk index). 377 | // Blocks are repeated continuously since the protocol is one-directional, there is no 378 | // way for the sender to know a chunk has been received. The total message from the 379 | // driver is a 1 byte command followed by the payload 380 | // 381 | // Command 0: The payload (up to 779 bytes) is an unsigned Ethereum transacrtion 382 | // Command 1: The payload (up to 128 bytes) is a printable-ASCII (plus \n) message 383 | // Command 2: The payload (up to 48 bytes) is a binary message 384 | // 385 | // The MSB of the command must be 0. A top bit of 1 may be used in the future to 386 | // indicate a multi-byte command 387 | 388 | static void waitForTransaction(uint8_t unsignedTransactionHash[32]) { 389 | 390 | BLECastMessage message; 391 | message.radioPinCE = RADIO_PIN_CE; 392 | message.radioPinCSN = RADIO_PIN_CSN; 393 | 394 | #if DEBUG_SERIAL == 1 395 | // The Serial library takes up extra space (buffers, etc), so we cannot have as large a message 396 | const uint16_t messageDataSize = 256; 397 | #else 398 | // 780 will hold 60 payloads (12 bytes data + 1 byte marker) for a total payload of 720 bytes 399 | const uint16_t messageDataSize = 780; 400 | #endif 401 | 402 | // Allocate a buffer to receive the message (payload) 403 | uint8_t *messageData = (uint8_t*)(malloc(messageDataSize)); 404 | if (!messageData) { crash(ErrorCodeOutOfMemory, __LINE__); } 405 | 406 | // The secret key to listen for; Anyone with this key can send transactions to your 407 | // Firefly when on and can listen to ransactions sent from your phone 408 | uint8_t pairSecret[EEPROM_DATA_LENGTH_PAIR_SECRET]; 409 | readStorage(EEPROM_DATA_OFFSET_PAIR_SECRET, EEPROM_DATA_LENGTH_PAIR_SECRET, pairSecret); 410 | blecast_init(&message, pairSecret, messageData, messageDataSize); 411 | 412 | // We use this to track how long the button has been held down 413 | uint16_t buttonOn = 0; 414 | 415 | while (true) { 416 | 417 | // Long-hold Button? Trigger pairing screen 418 | if (digitalRead(BUTTON_PIN)) { 419 | if (buttonOn == 0) { 420 | display_invert(DISPLAY_ADDRESS, true); 421 | } else if (buttonOn > (SHOW_PAIRIING_SCREEN_DURATION + 49) / 50) { 422 | display_invert(DISPLAY_ADDRESS, false); 423 | break; 424 | } 425 | 426 | buttonOn++; 427 | 428 | delay(50); 429 | continue; 430 | 431 | } else if (buttonOn) { 432 | display_invert(DISPLAY_ADDRESS, false); 433 | buttonOn = 0; 434 | } 435 | 436 | bool foundMessage = blecast_poll(&message); 437 | if (!foundMessage) { continue; } 438 | 439 | Transaction transaction; 440 | 441 | // Check if this message is a valid transaction 442 | bool isValidTransaction = false; 443 | if (message.data[0] == 0x00) { 444 | isValidTransaction = ethers_decodeTransaction(&transaction, &message.data[1], message.size - 1); 445 | } 446 | 447 | // Valid transaction! 448 | if (isValidTransaction) { 449 | 450 | // First compute the hash, so we can clobber the data portion of the transaction (to use as the value string) 451 | ethers_keccak256(&message.data[1], message.size - 1, unsignedTransactionHash); 452 | 453 | // Hijack the data space to convert the value into a base-10 string (show up to 5 decimal places) 454 | ethers_toString(transaction.value, transaction.valueLength, (18 - 5), (char*)transaction.data); 455 | 456 | display_transaction(DISPLAY_ADDRESS, &transaction, (char*)transaction.data); 457 | 458 | break; 459 | 460 | // Valid message for signing 461 | } else if ((message.data[0] == 0x01 && message.size <= (128 + 1)) || (message.data[0] == 0x02 && message.size <= (48 + 1))) { 462 | 463 | uint8_t *messageData = (&message.data[1]); 464 | uint8_t messageLength = message.size - 1; 465 | 466 | // Null terminate the message 467 | messageData[messageLength] = 0; 468 | 469 | display_message(DISPLAY_ADDRESS, (message.data[0] == 0x02), messageData, messageLength); 470 | 471 | // Move the data forward 32 bytes so we have room to inject the message header 472 | for (int8_t i = messageLength; i >= 0; i--) { 473 | messageData[i + 32] = messageData[i]; 474 | } 475 | 476 | uint8_t offset = 32; 477 | 478 | // Prepend the length (in base 10, as ascii) 479 | if (messageLength == 0) { 480 | messageData[--offset] = '0'; 481 | messageLength++; 482 | } else { 483 | uint8_t convertLength = messageLength; 484 | while (convertLength) { 485 | messageData[--offset] = '0' + (convertLength % 10); 486 | messageLength++; 487 | convertLength /= 10; 488 | } 489 | } 490 | 491 | // Copy the message prefix 492 | offset -= 26; 493 | memcpy_P(&messageData[offset], messageHeaderPrefix, 26); 494 | messageLength += 26; 495 | 496 | // Compute the hash of the string with the message prefix prepended 497 | ethers_keccak256(&messageData[offset], messageLength, unsignedTransactionHash); 498 | 499 | break; 500 | } 501 | 502 | // Bad message; listen for another one 503 | blecast_reset(&message); 504 | } 505 | 506 | blecast_shutdown(&message); 507 | 508 | free(messageData); 509 | 510 | if (buttonOn) { 511 | showPairingScreen(); 512 | crash(ErrorCodeNone, __LINE__); 513 | } 514 | } 515 | 516 | 517 | // Note: The URI is generated entirely using upper-case letters and symbols available 518 | // in the QR code standard for Alphanumeric types, so that everything fits in a 519 | // version 3 QR code (of 77 available characters, this uses 71) 520 | static void generateSignatureURI(char component, uint8_t *signature, char *text) { 521 | // URI Scheme; i.e. "SIG:" 522 | text[0] = 'S'; 523 | text[1] = 'I'; 524 | text[2] = 'G'; 525 | text[3] = ':'; 526 | 527 | // URI path prefix; i.e. "(R|S)/" 528 | text[4] = component; 529 | text[5] = '/'; 530 | 531 | // Null termination 532 | text[7 + 64 - 1] = 0; 533 | 534 | // URI path; i.e. "[0-9A-F]{64}" 535 | uint8_t offset = 6; 536 | for (uint8_t i = 0; i < 32; i++) { 537 | text[offset++] = getHexNibble(signature[i] >> 4); 538 | text[offset++] = getHexNibble(signature[i]); 539 | } 540 | } 541 | 542 | 543 | static void showSignedTransaction(uint8_t *signature) { 544 | uint8_t qrCodeBufferSize = qrcode_getBufferSize(3); 545 | 546 | // We use this scratch space for: (QR Code R) (QR Code S) (temporary space to generate string) 547 | uint8_t *scratch = (uint8_t*)malloc(2 * qrCodeBufferSize + (7 + 64)); 548 | if (!scratch) { crash(ErrorCodeOutOfMemory, __LINE__); } 549 | 550 | char *text = (char*)(&scratch[2 * qrCodeBufferSize]); 551 | 552 | // SIG:R/XXXX 553 | generateSignatureURI('R', &signature[0], text); 554 | 555 | QRCode qrcodeR; 556 | qrcode_initText(&qrcodeR, &scratch[0], 3, 0, text); 557 | 558 | // SIG:S/XXXX 559 | generateSignatureURI('S', &signature[32], text); 560 | 561 | QRCode qrcodeS; 562 | qrcode_initText(&qrcodeS, &scratch[qrCodeBufferSize], 3, 0, text); 563 | 564 | display_qrcodes(DISPLAY_ADDRESS, &qrcodeR, &qrcodeS); 565 | 566 | free(scratch); 567 | } 568 | 569 | 570 | static void signAndShowTransaction(uint8_t *unsignedTransactionHash) { 571 | 572 | uint8_t signature[ETHERS_SIGNATURE_LENGTH]; 573 | bool success = ethers_sign(privateKey, unsignedTransactionHash, signature); 574 | 575 | if (!success) { crash(ErrorCodeSigningError, __LINE__); } 576 | 577 | showSignedTransaction(signature); 578 | } 579 | 580 | static void waitForButton() { 581 | // Wait for the button down 582 | while (!digitalRead(BUTTON_PIN)) { delay(50); } 583 | 584 | // De-bounce 585 | delay(50); 586 | 587 | // wait for the button up 588 | while (digitalRead(BUTTON_PIN)) { delay(50); } 589 | } 590 | 591 | // Execution begins here 592 | void setup() { 593 | 594 | #if DEBUG_SERIAL == 1 595 | Serial.begin(115200); 596 | while (!Serial) { } 597 | #endif 598 | 599 | // Make sure we have cached things that take a while to compute 600 | generateCache(); 601 | 602 | // Disable all interrupts (we don't need them and makes verifying the code execution easier) 603 | //SREG |= (1 << 7); 604 | //SREG &= ~(1 << 7); 605 | 606 | // Initialize the I2C OLED display 607 | display_init(DISPLAY_ADDRESS); 608 | 609 | // Show the wallet address 610 | showAddress(); 611 | 612 | // Wait for a valid transaction over BLECast and compute its hash 613 | uint8_t unsignedTransactionHash[ETHERS_KECCAK256_LENGTH]; 614 | waitForTransaction(unsignedTransactionHash); 615 | 616 | // Wait for the user to accept the transaction 617 | waitForButton(); 618 | 619 | // Show the hour-glass waiting screen 620 | display_hourglass(DISPLAY_ADDRESS); 621 | 622 | // Sign the transaction and show the QR codes once read 623 | signAndShowTransaction(unsignedTransactionHash); 624 | 625 | // Done; spin forever and dream of turtles 626 | crash(ErrorCodeNone, __LINE__); 627 | } 628 | 629 | 630 | void loop() { 631 | // Never entered 632 | } 633 | -------------------------------------------------------------------------------- /source/firefly/firefly_display.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * Copyright (c) 2017 Yuet Loo Wong 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | 27 | // See: 28 | // http://www.ermicro.com/blog/?p=744 29 | // http://playground.arduino.cc/Code/ATMELTWI 30 | 31 | 32 | #include "firefly_display.h" 33 | 34 | // Constants for I2C (see: http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__twi.html) 35 | #include 36 | 37 | // Font Data 38 | #include "firefly_fonts.h" 39 | 40 | // Add attributes (i.e. packed) to an enum 41 | // https://stackoverflow.com/questions/2791739/c-packing-a-typedef-enum 42 | #ifdef __GNUC__ 43 | #define attribute(x) __attribute__((x)); 44 | #else 45 | #define attribute(x) 46 | #endif 47 | 48 | 49 | // We use this to make sure the packing worked 50 | // https://www.pixelbeat.org/programming/gcc/static_assert.html 51 | #define ASSERT_CONCAT_(a, b) a##b 52 | #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) 53 | /* These can't be used after statements in c89. */ 54 | #ifdef __COUNTER__ 55 | #define STATIC_ASSERT(e,m) \ 56 | ;enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(int)(!!(e)) } 57 | #else 58 | /* This can't be used twice on the same line so ensure if using in headers 59 | * that the headers are not included twice (by wrapping in #ifndef...#endif) 60 | * Note it doesn't cause an issue when used on same line of separate modules 61 | * compiled with gcc -combine -fwhole-program. */ 62 | #define STATIC_ASSERT(e,m) \ 63 | ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!!(e)) } 64 | #endif 65 | 66 | // SSD1306 Command Codes 67 | // See: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf 68 | enum DisplayCommand { 69 | 70 | // Set Contrast Control (1 byte operand follows) 71 | DisplayCommandSetContrast = 0x81, // Reset: 0x7f 72 | 73 | // Set Entire Display On 74 | //DisplayCommandSetFollowsRAM = 0xa4, // Reset 75 | //DisplayCommandSetIgnoresRAM = 0xa5, 76 | 77 | // Set Normal/Inverse Display 78 | DisplayCommandSetNormal = 0xa6, // Reset 79 | DisplayCommandSetInvert = 0xa7, 80 | 81 | // Set Display On/Off 82 | DisplayCommandOff = 0xae, // Reset 83 | DisplayCommandOn = 0xaf, 84 | 85 | // Set memory address mode (1 byte operand follows; see below) 86 | DisplayCommandMemoryAddressMode = 0x20, 87 | DisplayCommandMemoryAddressModeHorizontal = 0x00, 88 | DisplayCommandMemoryAddressModeVertical = 0x01, 89 | DisplayCommandMemoryAddressModePage = 0x02, // Reset 90 | 91 | // Set Column Address (3 byte operation follows; start and end; Reset: 0-127) 92 | DisplayCommandSetColumnAddress = 0x21, 93 | 94 | // Set Page Address (3 byte operation follows; start and end; Reset: 0-7) 95 | DisplayCommandSetPageAddress = 0x22, 96 | 97 | // Set Segment Re-map 98 | DisplayCommandSegmentRemapNone = 0xa0, // Reset 99 | DisplayCommandSegmentRemap = 0xa1, 100 | 101 | // Set COM Output Scan Direction 102 | DisplayCommandCOMScanIncrement = 0xc0, // Reset 103 | DisplayCommandCOMScanDecrement = 0xc8, 104 | 105 | // Set Charge Pump Settings (See SSD1306 Application Notes; Section 2.1) 106 | // NOTE: A DisplayCommandOn (0xaf) must be issued afterward to activate 107 | DisplayCommandSetChargePump = 0x8d, 108 | DisplayCommandSetChargePumpDisabled = 0x10, // Reset 109 | DisplayCommandSetChargePumpEnabled = 0x14, 110 | 111 | // Set Pre-charge Period (1 byte operand follows; Reset 0x22) 112 | // A[3:0] - Phase 1 period of up to 15 DCLK clocks; 0 is invalid 113 | // A[7:4] - Phase 2 period of up to 15 DCLK clocks; 0 is invalid 114 | DisplayCommandSetPrechargePeriod = 0xd9, 115 | 116 | // Set the V_COMH Deselecty Level (1 byte follows; Reset: 0x20) 117 | // A[6:4] - 0x00 = 0.65 x Vcc; 0x20 = 0.77 x Vcc ; 0x30 = 0.83 x Vcc 118 | DisplayCommandSetVCOMHDeselectLevel = 0xdb, 119 | 120 | } attribute(packed); 121 | typedef enum DisplayCommand DisplayCommand; 122 | 123 | STATIC_ASSERT( sizeof ( DisplayCommand ) == 1, "DisplayCommand is incorrect width"); 124 | 125 | 126 | // Display Context 127 | typedef struct DisplayContext { 128 | // We need 16 bytes per page + the 0x40 control byte per chunk 129 | uint8_t buffer[17]; 130 | uint8_t length; 131 | uint8_t address; 132 | } DisplayContext; 133 | 134 | 135 | typedef enum ControlType { 136 | ControlTypeStart, 137 | ControlTypeData, 138 | ControlTypeStop, 139 | } ControlType; 140 | 141 | // We are trying a higher I2C frequency of 400kHz; if this is a bugging out your display, 100kHz is safe 142 | #define F_I2C 400000L 143 | 144 | 145 | static void i2c_init(DisplayContext *context) { 146 | 147 | // Enable the I2C bus on the chip pins 148 | digitalWrite(SDA, 1); 149 | digitalWrite(SCL, 1); 150 | 151 | // Set Pre-Scalar bits in the Status register and the Bit-Rate generator 152 | TWSR &= ~(TWPS0 | TWPS1); 153 | TWBR = ((F_CPU / F_I2C) - 16) / 2; 154 | 155 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); 156 | 157 | // Set the address 158 | TWAR = (context->address << 1); 159 | } 160 | 161 | 162 | static uint8_t i2c_setControl(ControlType controlType) { 163 | switch(controlType) { 164 | case ControlTypeStart: 165 | TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); 166 | break; 167 | case ControlTypeData: 168 | TWCR = (1 << TWINT) | (1 << TWEN); 169 | break; 170 | case ControlTypeStop: 171 | TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); 172 | // Stop does not wait 173 | return 0; 174 | } 175 | 176 | while (!(TWCR & (1 << TWINT))); 177 | 178 | // Mask out the prescaler and reserved bits 179 | return (TWSR & 0xF8); 180 | } 181 | 182 | static void i2c_begin(DisplayContext *context) { 183 | context->length = 0; 184 | } 185 | 186 | static void i2c_push(DisplayContext *context, uint8_t data) { 187 | context->buffer[context->length++] = data; 188 | } 189 | 190 | static void i2c_flush(DisplayContext *context) { 191 | uint8_t status; 192 | 193 | // Start 194 | status = i2c_setControl(ControlTypeStart); 195 | 196 | // i.e. status == TW_START 197 | 198 | // Set the slave for write 199 | TWDR = (context->address << 1) | TW_WRITE; 200 | i2c_setControl(ControlTypeData); 201 | 202 | // i.e. status == TW_MT_SLA_ACK 203 | 204 | // Write data 205 | for (uint8_t i = 0; i < context->length; i++) { 206 | TWDR = context->buffer[i]; 207 | status = i2c_setControl(ControlTypeData); 208 | 209 | // i.e. status == TW_MT_DATA_ACK 210 | } 211 | 212 | // Stop 213 | status = i2c_setControl(ControlTypeStop); 214 | 215 | // i.e. status == 0 (returned explicitly in i2cSetControl) 216 | 217 | context->length = 0; 218 | } 219 | 220 | static void ssd1306_command(DisplayContext *context, uint8_t command) { 221 | i2c_begin(context); 222 | i2c_push(context, 0x00); 223 | i2c_push(context, command); 224 | i2c_flush(context); 225 | } 226 | 227 | static void display_begin(DisplayContext *context, uint8_t address, uint8_t dim) { 228 | context->address = address; 229 | 230 | ssd1306_command(context, DisplayCommandSetContrast); 231 | ssd1306_command(context, dim ? 0x00: 0xcf); 232 | 233 | context->length = 0; 234 | } 235 | 236 | // This should be called against the same DisplayContext 16 times in a row 237 | static void display_chunk(DisplayContext *context, uint8_t data) { 238 | if (context->length == 0) { 239 | i2c_begin(context); 240 | i2c_push(context, 0x40); 241 | } 242 | 243 | i2c_push(context, data); 244 | 245 | if (context->length == 17) { 246 | i2c_flush(context); 247 | i2c_begin(context); 248 | } 249 | } 250 | 251 | static void display_chunks(DisplayContext *context, uint8_t data, uint16_t count) { 252 | while (count-- != 0) { display_chunk(context, data); } 253 | } 254 | 255 | static void display_bigChar(DisplayContext *context, uint8_t charIndex, bool topAlign, bool topHalf) { 256 | //uint32_t *bigChar = ; 257 | //getBigFont(charIndex, bigChar); 258 | 259 | for (uint8_t x = 0; x < 8; x++) { 260 | uint16_t col = 0; 261 | for (uint8_t y = 0; y < 12; y++) { 262 | //uint32_t c4 = bigChar[y / 4]; 263 | uint32_t c4 = pgm_read_dword(&font_big[3 * charIndex + y / 4]); 264 | c4 >>= (y % 4) * 8; 265 | if (c4 & (1 << x)) { 266 | col |= (1 << y); 267 | } 268 | } 269 | 270 | if (!topAlign) { col <<= 4; } 271 | 272 | if (!topHalf) { col = (col >> 8); } 273 | 274 | display_chunk(context, col & 0xff); 275 | } 276 | } 277 | 278 | static void display_smallChar(DisplayContext *context, uint8_t charIndex) { 279 | uint32_t smallChar = pgm_read_dword(&font_small[charIndex]); 280 | for (int8_t c = 24; c >= 0; c -= 8) { 281 | display_chunk(context, (smallChar >> c) & 0xff); 282 | } 283 | } 284 | 285 | static void display_tinyChar(DisplayContext *context, uint8_t charIndex) { 286 | // Space 287 | if (charIndex == 32) { 288 | display_chunks(context, 0, 3); 289 | return; 290 | } 291 | 292 | if (charIndex == 10 || charIndex == 13) { 293 | // We put a NL/CR symbol in the last index 294 | charIndex = 127; 295 | } else if (charIndex < 32 || charIndex > 126) { 296 | // Non-printable character; solid box 297 | display_chunks(context, (31 << 1), 3); 298 | return; 299 | } 300 | 301 | 302 | // We have a glyph, so adjust to our font table 303 | charIndex -= 33; 304 | 305 | uint32_t tinyChar = 0; 306 | uint16_t startBitIndex = charIndex * 15; 307 | for (uint8_t i = 0; i < 3; i++) { 308 | tinyChar <<= 8; 309 | tinyChar |= pgm_read_byte(&font_tiny[(startBitIndex >> 3) + i]); 310 | } 311 | 312 | tinyChar >>= (9 - (startBitIndex % 8)); 313 | 314 | for (int8_t c = 0; c < 15; c += 5) { 315 | uint8_t chunk = ((tinyChar >> c) << 1) & 0x3e; 316 | 317 | // Add decents on certain characters 318 | if (c == 0 && charIndex == ('p' - 33)) { 319 | chunk |= 0x40; 320 | } else if (c == 5 && (charIndex == ('g' - 33) || charIndex == ('j' - 33) || charIndex == ('y' - 33))) { 321 | chunk |= 0x40; 322 | } else if (c == 10 && (charIndex == ('q' - 33) || charIndex == (127 - 33))) { 323 | chunk |= 0x40; 324 | } 325 | 326 | display_chunk(context, chunk); 327 | } 328 | } 329 | 330 | void display_debug_int(uint8_t address, uint32_t value) { 331 | DisplayContext context; 332 | display_begin(&context, address, 0); 333 | 334 | uint8_t offset = 0; 335 | uint8_t base10[6]; 336 | while (value) { 337 | base10[offset++] = value % 10; 338 | value /= 10; 339 | } 340 | 341 | if (offset == 0) { 342 | base10[0] = 0; 343 | offset++; 344 | } 345 | 346 | for (int8_t i = offset - 1; i >= 0; i--) { 347 | display_smallChar(&context, base10[i]); 348 | display_chunk(&context, 0); 349 | } 350 | 351 | while (context.length != 0) { 352 | display_chunk(&context, 0); 353 | } 354 | } 355 | 356 | void display_debug_char(uint8_t address, char chr) { 357 | DisplayContext context; 358 | display_begin(&context, address, 0); 359 | 360 | display_tinyChar(&context, (uint8_t)chr); 361 | display_chunk(&context, 0); 362 | 363 | while (context.length != 0) { 364 | display_chunk(&context, 0); 365 | } 366 | } 367 | 368 | void display_debug_text(uint8_t address, const char *text) { 369 | DisplayContext context; 370 | display_begin(&context, address, 0); 371 | 372 | while (1) { 373 | uint8_t chr = (*text++); 374 | if (!chr) { break; } 375 | display_tinyChar(&context, (uint8_t)chr); 376 | display_chunk(&context, 0); 377 | } 378 | 379 | while (context.length != 0) { 380 | display_chunk(&context, 0); 381 | } 382 | } 383 | 384 | static uint8_t getHexNibble(uint8_t value) { 385 | value &= 0x0f; 386 | if (value <= 9) { return '0' + value; } 387 | return 'a' + value - 10; 388 | } 389 | 390 | void display_debug_buffer(uint8_t address, uint8_t *value, uint16_t length) { 391 | DisplayContext context; 392 | display_begin(&context, address, 0); 393 | 394 | display_chunks(&context, 0, 16); 395 | 396 | for (int8_t i = 0; i < length; i++) { 397 | display_tinyChar(&context, getHexNibble(value[i] >> 4)); 398 | display_chunk(&context, 0); 399 | display_tinyChar(&context, getHexNibble(value[i])); 400 | display_chunks(&context, 0, 4); 401 | } 402 | 403 | while (context.length != 0) { 404 | display_chunk(&context, 0); 405 | } 406 | 407 | display_chunks(&context, 0, 16); 408 | } 409 | 410 | 411 | void display_qrcode(DisplayContext *context, QRCode *qrcode, uint8_t py) { 412 | for (uint8_t x = 0; x < 64; x++) { 413 | uint8_t by = py * 8; 414 | uint8_t col = 0; 415 | 416 | for (uint8_t y = 0; y < 8; y++) { 417 | if (x < 3 || x >= 64 - 3 || by + y < 3 || by + y >= 64 -3 || !qrcode_getModule(qrcode, (x - 3) / 2, (by + y - 3) / 2)) { 418 | col |= (1 << y); 419 | } 420 | } 421 | display_chunk(context, col); 422 | } 423 | } 424 | 425 | void display_qrcodes(uint8_t address, QRCode *leftOrCenter, QRCode *right) { 426 | DisplayContext context; 427 | display_begin(&context, address, 1); 428 | 429 | QRCode *qrcode = NULL; 430 | 431 | for (uint8_t py = 0; py < 8; py++) { 432 | if (right == NULL) { 433 | display_chunks(&context, 0, 32); 434 | } 435 | 436 | display_qrcode(&context, leftOrCenter, py); 437 | 438 | if (right == NULL) { 439 | display_chunks(&context, 0, 32); 440 | } else { 441 | display_qrcode(&context, right, py); 442 | } 443 | } 444 | } 445 | 446 | void display_hourglass(uint8_t address) { 447 | DisplayContext context; 448 | display_begin(&context, address, 0); 449 | 450 | display_chunks(&context, 0, 3 * 128); 451 | 452 | for (int8_t top = 1; top >= 0; top--) { 453 | display_chunks(&context, 0, 64 - 8 / 2); 454 | display_bigChar(&context, 18, true, top); 455 | display_chunks(&context, 0, 64 - 8 / 2); 456 | } 457 | 458 | display_chunks(&context, 0, 3 * 128); 459 | } 460 | 461 | static void display_transaction_size(DisplayContext *context, uint16_t dataSize) { 462 | const uint8_t width = 128 - 32; 463 | 464 | if (dataSize == 0) { 465 | display_chunks(context, 0, width); 466 | return; 467 | } 468 | 469 | uint8_t offset = 0; 470 | uint8_t base10[6]; 471 | while (dataSize) { 472 | base10[offset++] = dataSize % 10; 473 | dataSize /= 10; 474 | } 475 | 476 | uint8_t len = (offset + 6); 477 | uint8_t w = (len * 5 - 1); 478 | uint8_t padding = (width - w) / 2; 479 | 480 | display_chunks(context, 0, padding); 481 | 482 | for (int8_t i = offset - 1; i >= 0; i--) { 483 | display_smallChar(context, base10[i]); 484 | display_chunk(context, 0); 485 | } 486 | 487 | display_chunks(context, 0, 5); 488 | 489 | for (uint8_t i = 10; i < 15; i++) { 490 | display_smallChar(context, i); 491 | display_chunk(context, 0); 492 | } 493 | 494 | display_chunks(context, 0, width - w - padding - 1); 495 | } 496 | 497 | static void display_transaction_address(DisplayContext *context, uint8_t *address) { 498 | 499 | // Show the transaction "to address" 500 | for (int8_t top = 1; top >= 0; top--) { 501 | display_chunks(context, 0, 4); 502 | 503 | display_bigChar(context, 0, true, top); 504 | display_chunk(context, 0); 505 | display_bigChar(context, 16, true, top); 506 | display_chunk(context, 0); 507 | 508 | for (uint8_t i = 0; i < 5; i++) { 509 | uint8_t c = i; 510 | if (c >= 3) { c = 20 - 2 + (i - 3); } 511 | 512 | display_bigChar(context, address[c] >> 4, true, top); 513 | display_chunk(context, 0); 514 | 515 | display_bigChar(context, address[c] & 0x0f, true, top); 516 | display_chunk(context, 0); 517 | 518 | if (i == 2) { 519 | for (uint8_t j = 0; j < 3; j++) { 520 | display_chunks(context, (top ? 0: 0x0c), 2); 521 | display_chunks(context, 0, (j == 2) ? 1: 2); 522 | } 523 | } 524 | } 525 | 526 | display_chunks(context, 0, 128 - (9 * 12) - 11 - 4); 527 | } 528 | } 529 | 530 | static void display_transaction_value(DisplayContext *context, char* value) { 531 | uint8_t len = strlen(value); 532 | 533 | // String too long; should not happen, but if the transaction is for over 534 | // 10 billion ether, at least show as much of the value as possible. 535 | // @TODO: We should throw an error and crash? If they attempt 100 billion 536 | // ether, we will only display 10 billion... :s 537 | if (len > 14) { len = 14; } 538 | 539 | uint8_t width = (len - 1) * 8 + 6 + (len - 1) ; 540 | uint8_t padding = (128 - width) / 2; 541 | 542 | for (int8_t top = 1; top >= 0; top--) { 543 | display_chunks(context, 0, padding); 544 | 545 | for (uint8_t i = 0; i < len; i++) { 546 | char c = value[i]; 547 | if (c == '.') { 548 | display_chunks(context, 0, 2); 549 | display_chunks(context, (top ? 0: 0xc0), 2); 550 | display_chunks(context, 0, 2); 551 | } else { 552 | display_bigChar(context, c - '0', false, top); 553 | } 554 | display_chunks(context, 0, 1); 555 | } 556 | display_chunks(context, 0, 128 - width - padding - 1); 557 | } 558 | } 559 | 560 | static void display_transaction_ok(DisplayContext *context) { 561 | uint8_t len = 3; 562 | uint8_t width = 4 * len + (len - 1); 563 | uint8_t padding = (128 - width) / 2; 564 | 565 | display_chunks(context, 0, padding); 566 | 567 | for (uint8_t i = 15; i < 18; i++) { 568 | display_smallChar(context, i); 569 | display_chunk(context, 0); 570 | } 571 | 572 | display_chunks(context, 0, 128 - padding); 573 | } 574 | 575 | void display_transaction(uint8_t address, Transaction *transaction, char* value) { 576 | DisplayContext context; 577 | display_begin(&context, address, 0); 578 | 579 | // The top strip (indicates gas limit, data byte count and gas price) 580 | 581 | // @TODO Contract? High gas limit? 582 | display_chunks(&context, 0, 16); 583 | 584 | // Data size (e.g. "354 bytes") 585 | display_transaction_size(&context, transaction->dataLength); 586 | 587 | // @TODO Gas price is low or high? 588 | display_chunks(&context, 0, 16); 589 | 590 | // Gap 591 | display_chunks(&context, 0, 128); 592 | 593 | // Transaction to address 594 | display_transaction_address(&context, transaction->address); 595 | 596 | // Transaction value 597 | display_transaction_value(&context, value); 598 | 599 | // Gap 600 | display_chunks(&context, 0, 128); 601 | 602 | // "ok?" 603 | display_transaction_ok(&context); 604 | } 605 | 606 | uint16_t display_text(DisplayContext *context, const char *message) { 607 | 608 | uint16_t count = 0; 609 | while (*message) { 610 | display_tinyChar(context, *message++); 611 | display_chunk(context, 0); 612 | count += 4; 613 | } 614 | 615 | while (context->length != 0) { 616 | display_chunk(context, 0); 617 | count++; 618 | } 619 | 620 | return count; 621 | } 622 | 623 | void display_message(uint8_t address, bool binary, const uint8_t *message, uint8_t length) { 624 | DisplayContext context; 625 | display_begin(&context, address, 0); 626 | 627 | // The top strip (indicates "SIGN", data byte count and binary/ascii) 628 | 629 | // "SIGN" in the top left 630 | display_text(&context, (const char*)"SIGN"); 631 | 632 | // Data size (e.g. "354 bytes") 633 | display_transaction_size(&context, length); 634 | 635 | if (binary) { 636 | // "HEX" in the top left 637 | display_text(&context, (const char*)" HEX"); 638 | 639 | } else { 640 | // "Text" in the top left 641 | display_text(&context, (const char*)"Text"); 642 | } 643 | 644 | // Gap 645 | display_chunks(&context, 0, 128); 646 | 647 | uint16_t count = 0; 648 | if (binary) { 649 | uint8_t index = 0; 650 | while (1) { 651 | uint8_t c = (uint8_t)(message[index++]); 652 | if (!c) { break; } 653 | display_tinyChar(&context, getHexNibble(c >> 4)); 654 | display_chunk(&context, 0); 655 | display_tinyChar(&context, getHexNibble(c)); 656 | count += 7; 657 | if (index % 12) { 658 | display_chunks(&context, 0, 4); 659 | count += 4; 660 | } 661 | } 662 | } else { 663 | count = display_text(&context, (const char*)message); 664 | } 665 | 666 | display_chunks(&context, 0, (4 * 128) - count); 667 | 668 | // Gap 669 | display_chunks(&context, 0, 128); 670 | 671 | // "ok?" 672 | display_transaction_ok(&context); 673 | } 674 | 675 | void display_init(uint8_t address) { 676 | DisplayContext context; 677 | context.address = address; 678 | 679 | i2c_init(&context); 680 | 681 | // Initialization sequence 682 | ssd1306_command(&context, DisplayCommandOff); 683 | 684 | // Horizontal drawing mode 685 | ssd1306_command(&context, DisplayCommandMemoryAddressMode); 686 | ssd1306_command(&context, DisplayCommandMemoryAddressModeHorizontal); 687 | 688 | // Draw from left-to-right 689 | ssd1306_command(&context, DisplayCommandSegmentRemap); 690 | 691 | // Draw top-to-bottom 692 | ssd1306_command(&context, DisplayCommandCOMScanDecrement); 693 | 694 | // Setting the follow provide slightly better brightness range 695 | 696 | // Set VCOMH Deselect Level 697 | // 0x00 => 0.65 * Vcc 698 | // 0x20 => 0.77 * Vcc (RESET) 699 | // 0x30 => 0.83 * Vcc 700 | ssd1306_command(&context, DisplayCommandSetVCOMHDeselectLevel); 701 | ssd1306_command(&context, 0x00); 702 | 703 | // Set Pre-charge Period (Reset: 0x22) 704 | // A[3:0] => Phase 1 period of up to 15 DCLK clocks 0 is invalid entry 705 | // A[7:4] => Phase 2 period of up to 15 DCLK clocks 0 is invalid entry 706 | ssd1306_command(&context, DisplayCommandSetPrechargePeriod); 707 | ssd1306_command(&context, 0xF1); 708 | 709 | // Enable the Charge-Pump 710 | ssd1306_command(&context, DisplayCommandSetChargePump); 711 | ssd1306_command(&context, DisplayCommandSetChargePumpEnabled); 712 | 713 | // Turn on the screen 714 | ssd1306_command(&context, DisplayCommandOn); 715 | 716 | // Make sure we clear any invert 717 | ssd1306_command(&context, DisplayCommandSetNormal); 718 | 719 | delay(5); 720 | 721 | display_clear(address); 722 | } 723 | 724 | void display_invert(uint8_t address, bool invert) { 725 | DisplayContext context; 726 | display_begin(&context, address, 0); 727 | 728 | ssd1306_command(&context, invert ? DisplayCommandSetInvert: DisplayCommandSetNormal); 729 | } 730 | 731 | 732 | void display_clear(uint8_t address) { 733 | DisplayContext context; 734 | context.address = address; 735 | 736 | ssd1306_command(&context, DisplayCommandSetColumnAddress); 737 | ssd1306_command(&context, 0); 738 | ssd1306_command(&context, 127); 739 | 740 | ssd1306_command(&context, DisplayCommandSetPageAddress); 741 | ssd1306_command(&context, 0); 742 | ssd1306_command(&context, 7); 743 | 744 | display_chunks(&context, 0, 128 * 8); 745 | } 746 | 747 | -------------------------------------------------------------------------------- /source/firefly/firefly_display.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * Copyright (c) 2017 Yuet Loo Wong 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /** 27 | * A custom video driver for the firefly. 28 | * 29 | * The main reason for this driver is for saving memory. The official 30 | * driver creates a large video buffer, which consumes 50% (1024 bytes) 31 | * on the ATmega328P. 32 | * 33 | * By contrast, this driver consumes zero bytes of memory beyond its call-stack, 34 | * and instead constructs the page byte values from the value of the computed 35 | * (x, y) for the objects passed in. 36 | * 37 | * See: https://github.com/adafruit/Adafruit_SSD1306 38 | * 39 | */ 40 | 41 | 42 | #include "Arduino.h" 43 | 44 | #include 45 | #include 46 | 47 | 48 | #define TX_OPTIONS_GAS_LOW (0x01 << 0) 49 | #define TX_OPTIONS_GAS_MEDIUM (0x10 << 0) 50 | #define TX_OPTIONS_GAS_HIGH (0x11 << 0) 51 | 52 | #define TX_OPTIONS_EXPENSIVE (1 << 1) 53 | 54 | #define SSD1306_LCDWIDTH 128 55 | #define SSD1306_LCDHEIGHT 64 56 | 57 | 58 | 59 | 60 | #ifdef __cplusplus 61 | extern "C" { 62 | #endif /* __cplusplus */ 63 | 64 | 65 | // Configure the display 66 | void display_init(uint8_t address); 67 | 68 | // Displays 1 centered QR code (if right is NULL) or 2 QR codes side-by-side. 69 | void display_qrcodes(uint8_t address, QRCode *leftOrCenter, QRCode *right); 70 | 71 | // Displays an hour glass in the center of the screen 72 | void display_hourglass(uint8_t address); 73 | 74 | // Displays a transaction, with the address above the value 75 | void display_transaction(uint8_t address, Transaction *transaction, char *value); 76 | 77 | void display_message(uint8_t address, bool binary, const uint8_t *message, uint8_t length); 78 | 79 | void display_clear(uint8_t address); 80 | 81 | void display_invert(uint8_t address, bool invert); 82 | 83 | void display_debug_int(uint8_t address, uint32_t value); 84 | void display_debug_char(uint8_t address, char chr); 85 | void display_debug_buffer(uint8_t address, uint8_t *value, uint16_t length); 86 | void display_debug_text(uint8_t address, const char *text); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif /* __cplusplus */ 91 | 92 | -------------------------------------------------------------------------------- /source/firefly/firefly_fonts.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const uint32_t font_big[] PROGMEM = { 4 | 0xc3c77e3c, 0xcfdbf3e3, 0x3c7ee3c7, 0x1e1e1c18, 5 | 0x18181818, 0xffff1818, 0xc0e67e3c, 0x1c3060c0, 6 | 0xffff060e, 0xc0c37f3e, 0xe0783860, 0x3e7fc3c0, 7 | 0x1c183030, 0x6366660c, 0x6060ffff, 0x0303ffff, 8 | 0xc0e07f3f, 0x3e7fe3c0, 0x03c6fe7c, 0xc3ff7f03, 9 | 0x3c7ec3c3, 0xc0c3ffff, 0x30306060, 0x18181818, 10 | 0xc3c37e3c, 0xc3663c66, 0x3c7ec3c3, 0xc3c37e3c, 11 | 0xc0fcfec3, 0x3c7ec6c0, 0x663c3c18, 0xffff6666, 12 | 0xc3c3c3c3, 0xc6c67f3f, 0xc67e7ec6, 0x3f7fc6c6, 13 | 0x03c7fe7c, 0x03030303, 0x7cfec703, 0xc3e37f1f, 14 | 0xc3c3c3c3, 0x3f7fe3c3, 0x0303ffff, 0x033f3f03, 15 | 0xffff0303, 0x0303ffff, 0x033f3f03, 0x03030303, 16 | 0x00000000, 0x3c7ee7c3, 0xc3e77e3c, 0x00c3ff00, 17 | 0x007e0000, 0xffc30000, 0x428181ff, 0x24181824, 18 | 0xffb59942 19 | }; 20 | 21 | 22 | const uint32_t font_small[] PROGMEM = { 23 | 0x7e91897e, 0x8486ff80, 0xc2a1918e, 0x42898976, 24 | 0x1e10ff10, 0x4f898971, 0x7e898972, 0x0101f10f, 25 | 0x76898976, 0x4e91917e, 0xff898976, 0x0f08f80f, 26 | 0x0101ff01, 0xff898981, 0x46898972, 0x78848478, 27 | 0xff205088, 0x02b10906 28 | }; 29 | 30 | 31 | const uint8_t font_tiny[] PROGMEM = { 32 | 0x05, 0xc0, 0x30, 0x0f, 0xea, 0xf9, 0x7e, 0xa9, 0x11, 0x3c, 33 | 0xbb, 0xc0, 0x30, 0x45, 0xc0, 0x03, 0xa2, 0x51, 0x14, 0x8e, 34 | 0x20, 0x11, 0x02, 0x10, 0x80, 0x80, 0x06, 0x4c, 0x3e, 0x3e, 35 | 0x07, 0xc5, 0x2a, 0xe5, 0x55, 0x8f, 0xc8, 0x74, 0xd6, 0xfd, 36 | 0xaf, 0x86, 0x5c, 0xfe, 0xbf, 0x7d, 0x6e, 0x05, 0x00, 0x0a, 37 | 0x84, 0x54, 0x45, 0x29, 0x44, 0x54, 0x47, 0x50, 0xda, 0xae, 38 | 0xf1, 0x7c, 0xaa, 0xfe, 0x31, 0x73, 0xa3, 0xfa, 0xd7, 0xe5, 39 | 0x2f, 0xfb, 0x57, 0x7c, 0x9f, 0x8f, 0xe2, 0xf8, 0x23, 0x64, 40 | 0xfc, 0x21, 0xff, 0x9b, 0xff, 0x77, 0xdd, 0x17, 0x08, 0xbf, 41 | 0xf6, 0x5d, 0x66, 0xfd, 0x35, 0x90, 0x7e, 0x1f, 0xc1, 0xe7, 42 | 0xc1, 0xfe, 0xcf, 0xec, 0x9b, 0x1f, 0x07, 0x3a, 0xe6, 0x31, 43 | 0xfa, 0x08, 0x2f, 0xc6, 0x22, 0x08, 0xa1, 0x08, 0x00, 0x41, 44 | 0xe5, 0xb4, 0xc9, 0x7e, 0x52, 0x67, 0xe4, 0xcb, 0x69, 0x85, 45 | 0xf1, 0x3c, 0xa6, 0x70, 0x5f, 0x07, 0x41, 0xd0, 0x42, 0x4c, 46 | 0xfc, 0x3f, 0x1f, 0x3b, 0xdc, 0x17, 0x99, 0x26, 0x32, 0x5e, 47 | 0xf4, 0x98, 0x21, 0x71, 0x5e, 0xa4, 0xbe, 0x2f, 0x41, 0xce, 48 | 0xc3, 0xbd, 0xcf, 0x49, 0x92, 0xf2, 0x0d, 0x6f, 0x6a, 0x3b, 49 | 0x20, 0x36, 0x02, 0x6e, 0x21, 0x18, 0xbf, 0x84, 0x00 50 | }; 51 | -------------------------------------------------------------------------------- /source/libs/ethers/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Richard Moore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /source/libs/ethers/README.md: -------------------------------------------------------------------------------- 1 | Firefly 2 | ======= 3 | 4 | A simple, low-cost Ethereum wallet. 5 | 6 | **Features:** 7 | 8 | - **Line-of-sight** - Transactions can only be signed if the Firefly can physically be seen 9 | - **Cheap** - $12 worth of components 10 | - **Secure** - 11 | - **Simple to Use** - 12 | 13 | Additional Documentation 14 | ------------------------ 15 | 16 | [How to Build](docs.ethers.io w/ embedded YouTube video) 17 | 18 | License 19 | ------- 20 | 21 | BSD + MIT License. See individual files. 22 | -------------------------------------------------------------------------------- /source/libs/ethers/library.properties: -------------------------------------------------------------------------------- 1 | name=Ethers 2 | version=0.0.1 3 | author=Richard Moore 4 | maintainer=Richard Moore 5 | sentence=A very basic library for handling Ethereum transactions. 6 | paragraph=A very basic library for handling Ethereum transactions. 7 | category=Other 8 | url=https://github.com/firefly/wallet 9 | architectures=* 10 | includes=ethers.h 11 | -------------------------------------------------------------------------------- /source/libs/ethers/src/ethers.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * Copyright (c) 2017 Yuet Loo Wong 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include "ethers.h" 27 | 28 | #include 29 | 30 | #include "./keccak256.h" 31 | //#include "./sha2.h" 32 | 33 | #include "./types.h" 34 | #include "./uECC.h" 35 | 36 | 37 | static uint32_t readbe(uint8_t *data, uint16_t offset, uint16_t length) { 38 | data += offset; 39 | uint32_t result = 0; 40 | for (uint16_t i = 0; i < length; i++) { 41 | result <<= 8; 42 | result += *(data++); 43 | } 44 | return result; 45 | } 46 | 47 | static bool _setField(Transaction *transaction, uint8_t *data, uint16_t offset, uint16_t length, uint8_t *index) { 48 | 49 | switch (*index) { 50 | 51 | // nonce 52 | case 0: 53 | // Nonce is allowed to be 32 bytes, but if it is non-practical to be over 4B 54 | if (length > 4) { return false; } 55 | transaction->nonce = readbe(data, offset, length); 56 | break; 57 | 58 | // gasPrice 59 | case 1: 60 | // Gas Price is allowed to be 32 bytes, but if it is non-practical to be over 1T 61 | if (length > 5) { return false; } 62 | if (length > 2) { 63 | transaction->gasPrice = readbe(data, offset, length - 2); 64 | transaction->gasPriceLow = readbe(data, offset + length - 2, 2); 65 | } else { 66 | transaction->gasPrice = 0; 67 | transaction->gasPriceLow = readbe(data, offset, length); 68 | } 69 | break; 70 | 71 | // gasLimit 72 | case 2: 73 | // Gas limit is allowed to be 32 bytes, but if it is non-practical to be over 4B 74 | if (length > 4) { return false; } 75 | transaction->gasLimit = readbe(data, offset, length); 76 | break; 77 | 78 | // to 79 | case 3: 80 | if (length != 0 && length != 20) { return false; } 81 | transaction->address = &data[offset]; 82 | transaction->hasAddress = (length == 20); 83 | break; 84 | 85 | // value 86 | case 4: 87 | if (length > 32) { return false; } 88 | transaction->value = &data[offset]; 89 | transaction->valueLength = length; 90 | break; 91 | 92 | // data 93 | case 5: 94 | transaction->data = &data[offset]; 95 | transaction->dataLength = length; 96 | break; 97 | 98 | // v, r, s 99 | case 6: 100 | if (length == 1) { 101 | int16_t v = (((int16_t)(data[offset])) - 35) / 2; 102 | if (v < 0) { v = 0; } 103 | transaction->chainId = v; 104 | } else { 105 | transaction->chainId = 0; 106 | } 107 | break; 108 | 109 | case 7: 110 | case 8: 111 | return true; 112 | 113 | // Transactions only have 9 fields 114 | default: 115 | return false; 116 | } 117 | 118 | (*index)++; 119 | 120 | return true; 121 | } 122 | 123 | static bool _decode(Transaction *transaction, uint8_t *data, uint16_t length, uint16_t offset, uint16_t *consumed, uint8_t *index) { 124 | if (length == 0) { return false; } 125 | 126 | if (data[offset] >= 0xf8) { 127 | // Array with extra length prefix 128 | 129 | if (*index != 255) { return false; } 130 | *index = 0; 131 | 132 | uint16_t ll = (data[offset] - 0xf7); 133 | if (offset + 1 + ll > length) { return false; } 134 | 135 | uint16_t l = readbe(data, offset + 1, ll); 136 | if (offset + 1 + ll + l > length) { return false; } 137 | 138 | uint16_t childOffset = offset + 1 + ll; 139 | while (childOffset < offset + 1 + ll + l) { 140 | uint16_t childConsumed = 0; 141 | bool success = _decode(transaction, data, length, childOffset, &childConsumed, index); 142 | if (!success) { return false; } 143 | 144 | childOffset += childConsumed; 145 | if (childOffset > offset + 1 + ll + l) { return false; } 146 | } 147 | 148 | *consumed = 1 + ll + l; 149 | return true; 150 | 151 | } else if (data[offset] >= 0xc0) { 152 | // Short-ish array 153 | 154 | if (*index != 255) { return false; } 155 | *index = 0; 156 | 157 | uint16_t l = (data[offset] - 0xc0); 158 | if (offset + 1 + l > length) { return false; } 159 | 160 | uint16_t childOffset = offset + 1; 161 | while (childOffset < offset + 1 + l) { 162 | uint16_t childConsumed = 0; 163 | bool success = _decode(transaction, data, length, childOffset, &childConsumed, index); 164 | if (!success) { return false; } 165 | 166 | childOffset += childConsumed; 167 | if (childOffset > offset + 1 + l) { return false; } 168 | } 169 | 170 | *consumed = 1 + l; 171 | return true; 172 | 173 | } else if (data[offset] >= 0xb8) { 174 | if (*index == 255) { return false; } 175 | 176 | uint16_t ll = (data[offset] - 0xb7); 177 | if (offset + 1 + ll > length) { return false; } 178 | 179 | uint16_t l = readbe(data, offset + 1, ll); 180 | if (offset + 1 + ll + l > length) { return false; } 181 | 182 | bool success = _setField(transaction, data, offset + 1 + ll, l, index); 183 | if (!success) { return false; } 184 | *consumed = 1 + ll + l; 185 | 186 | return true; 187 | 188 | } else if (data[offset] >= 0x80) { 189 | if (*index == 255) { return false; } 190 | 191 | uint16_t l = (data[offset] - 0x80); 192 | if (offset + 1 + l> length) { return false; } 193 | 194 | bool success = _setField(transaction, data, offset + 1, l, index); 195 | if (!success) { return false; } 196 | *consumed = 1 + l; 197 | 198 | return true; 199 | } 200 | 201 | if (*index == 255) { return false; } 202 | 203 | bool success = _setField(transaction, data, offset, 1, index); 204 | if (!success) { return false; } 205 | *consumed = 1; 206 | return true; 207 | } 208 | 209 | bool ethers_decodeTransaction(Transaction* transaction, uint8_t * data, uint16_t length) { 210 | transaction->rawData = data; 211 | transaction->rawDataLength = length; 212 | 213 | // @TODO: get length and data from transaction in _decode 214 | 215 | uint8_t index = 255; 216 | uint16_t consumed = 0; 217 | bool success = _decode(transaction, data, length, 0, &consumed, &index); 218 | if (!success || consumed != length || index != 7) { return false; } 219 | return true; 220 | } 221 | 222 | bool ethers_privateKeyToAddress(const uint8_t *privateKey, uint8_t *address) { 223 | uint8_t publicKey[64]; 224 | 225 | bool success = uECC_compute_public_key(privateKey, publicKey, uECC_secp256k1()); 226 | if (!success) { return false; } 227 | 228 | uint8_t hashed[32]; 229 | ethers_keccak256(publicKey, 64, hashed); 230 | 231 | memcpy(address, &hashed[12], 20); 232 | 233 | return true; 234 | } 235 | 236 | static char getHexNibble(uint8_t value) { 237 | value &= 0x0f; 238 | if (value <= 9) { return '0' + value; } 239 | return 'a' + (value - 10); 240 | } 241 | 242 | void ethers_addressToChecksumAddress(const uint8_t *address, char *checksumAddress) { 243 | uint8_t end = ETHERS_CHECKSUM_ADDRESS_LENGTH - 1; 244 | 245 | // Null Termination 246 | checksumAddress[end--] = 0; 247 | 248 | // Expand the address into a lowercase-ascii-nibble representation 249 | // We go backwads, so don't clobber the address 250 | for (int8_t i = 20 - 1; i >= 0; i--) { 251 | checksumAddress[end--] = getHexNibble(address[i]); 252 | checksumAddress[end--] = getHexNibble(address[i] >> 4); 253 | } 254 | 255 | // "0x" prefix 256 | checksumAddress[end--] = 'x'; 257 | checksumAddress[end--] = '0'; 258 | 259 | // Compute the hash of the address 260 | uint8_t hashed[32]; 261 | ethers_keccak256(&checksumAddress[2], 40, hashed); 262 | 263 | // Do the checksum 264 | for (uint8_t i = 0; i < 40; i += 2) { 265 | if (checksumAddress[2 + i] >= 'a' && (hashed[i >> 1] >> 4) >= 8) { 266 | checksumAddress[2 + i] -= 0x20; 267 | } 268 | if (checksumAddress[2 + i + 1] >= 'a' && (hashed[i >> 1] & 0x0f) >= 8) { 269 | checksumAddress[2 + i + 1] -= 0x20; 270 | } 271 | } 272 | } 273 | 274 | bool ethers_privateKeyToChecksumAddress(const uint8_t *privateKey, char *address) { 275 | 276 | // Place the address (as bytes) into the address for now (scratch) 277 | bool success = ethers_privateKeyToAddress(privateKey, (uint8_t*)address); 278 | if (!success) { return false; } 279 | 280 | ethers_addressToChecksumAddress(address, address); 281 | 282 | return true; 283 | } 284 | /* 285 | uint8_t *ethers_debug() { 286 | return uECC_debug(); 287 | } 288 | */ 289 | void ethers_keccak256(const uint8_t *data, uint16_t length, uint8_t *result) { 290 | 291 | SHA3_CTX context; 292 | keccak_init(&context); 293 | keccak_update(&context, (const unsigned char*)data, (size_t)length); 294 | keccak_final(&context, (unsigned char*)result); 295 | 296 | // Clear out the contents of what we hashed (in case it was secret) 297 | memset((char*)&context, 0, sizeof(SHA3_CTX)); 298 | } 299 | 300 | /* 301 | void ethers_sha256(uint8_t *data, uint16_t length, uint8_t *result) { 302 | 303 | SHA256_CTX context; 304 | sha256_Init(&context); 305 | sha256_Update(&context, (const unsigned char*)data, (size_t)length); 306 | sha256_Final(&context, (unsigned char*)result); 307 | 308 | // Clear out the contents of what we hashed (in case it was secret) 309 | memset((char*)&context, 0, sizeof(SHA256_CTX)); 310 | } 311 | */ 312 | 313 | bool ethers_sign(const uint8_t *privateKey, const uint8_t *digest, uint8_t *result) { 314 | 315 | // Sign the digest 316 | int success = uECC_sign( 317 | (const uint8_t*)(privateKey), 318 | (const uint8_t*)(digest), 319 | 32, 320 | (uint8_t*)result, 321 | uECC_secp256k1() 322 | ); 323 | 324 | return (success == 1); 325 | } 326 | 327 | uint8_t ethers_getStringLength(uint8_t *value, uint8_t length) { 328 | // There is probably a better way to do this, but I just used the following 329 | // Python function: 330 | // 331 | // def check(a): return filter(lambda x: (x[1] < x[2]), a) 332 | // 333 | // And passed in: 334 | // 335 | // check([(i, ((i * 100) / Q) + 1, len(str(int('f' * i, 16)))) for i in xrange(1, 64)]) 336 | // 337 | // for various values of Q until I go something that worked; Q = 83 yeild only one value, 338 | // 22 bytes (44 nibbles) being over-estimated by 1 byte, for the worst case. 339 | 340 | // Add 1 for the null-termination (200 because we are measuring nibbles) 341 | return 2 + ((uint16_t)length * 200) / 83; 342 | } 343 | 344 | // Perform in-place division by 10 345 | static uint8_t idiv10(uint8_t *numerator, uint8_t *lengthPtr) { 346 | uint8_t quotient[*lengthPtr]; 347 | uint8_t quotientOffset = 0; 348 | 349 | // Divide by 10 350 | size_t length = *lengthPtr; 351 | for (size_t i = 0; i < length; ++i) { 352 | 353 | // How many input bytes to work with 354 | size_t j = i + 1 + (*lengthPtr) - length; 355 | if ((*lengthPtr) < j) { break; } 356 | 357 | // The next digit in the output (from numerator[0:j]) 358 | unsigned int value = readbe(numerator, 0, j); 359 | quotient[quotientOffset++] = value / 10; 360 | 361 | // Carry down the remainder 362 | uint8_t numeratorOffset = 0; 363 | numerator[numeratorOffset++] = value % 10; 364 | 365 | for (uint8_t k = j; k < *lengthPtr; k++) { 366 | numerator[numeratorOffset++] = numerator[k]; 367 | } 368 | 369 | *lengthPtr = numeratorOffset; 370 | } 371 | 372 | // Calculate the remainder 373 | unsigned int remainder = readbe(numerator, 0, *lengthPtr); 374 | 375 | // Find the first no`n-zero (so we can skip them during the copy) 376 | uint8_t firstNonZero = 0; 377 | while (firstNonZero < quotientOffset && quotient[firstNonZero] == 0) { 378 | firstNonZero++; 379 | } 380 | 381 | // Copy the quotient to the value (stripping leading zeros) 382 | for (uint8_t i = firstNonZero; i < quotientOffset; i++) { 383 | numerator[i - firstNonZero] = quotient[i]; 384 | } 385 | 386 | // New length 387 | *lengthPtr = quotientOffset - firstNonZero; 388 | 389 | return remainder; 390 | } 391 | 392 | uint8_t ethers_toString(uint8_t *amountWei, uint8_t amountWeiLength, uint8_t skip, char *result) { 393 | 394 | // The actual offset into the result string we are appending to 395 | uint8_t offset = 0; 396 | 397 | uint8_t scratch[amountWeiLength]; 398 | memcpy(scratch, amountWei, amountWeiLength); 399 | 400 | // The digit place we are into the base-10 number 401 | uint8_t place = 0; 402 | 403 | // Whether we have hit any non-zero value yet (so we can strip trailing zeros) 404 | bool nonZero = false; 405 | 406 | do { 407 | unsigned int remainder = idiv10(scratch, &amountWeiLength); 408 | 409 | // Only add characters if we are after truncation and not a trailing zero 410 | if (place >= skip && (nonZero || remainder != 0 || place >= 17)) { 411 | if (place == 18) { result[offset++] = '.'; } 412 | result[offset++] = '0' + remainder; 413 | nonZero = true; 414 | } 415 | 416 | place++; 417 | } while (amountWeiLength && !(amountWeiLength == 1 && scratch[0] == 0)); 418 | 419 | // Make sure we have at least 1 whole digit (with a decimal point) 420 | while (place <= 18) { 421 | if (place >= skip && (nonZero || place >= 17)) { 422 | if (place == 18) { result[offset++] = '.'; } 423 | result[offset++] = '0'; 424 | } 425 | place++; 426 | } 427 | 428 | // Reverse the digits 429 | for (uint8_t i = 0; i < offset / 2; i++) { 430 | char tmp = result[i]; 431 | result[i] = result[offset - i - 1]; 432 | result[offset - i - 1] = tmp; 433 | } 434 | 435 | // Null termination 436 | result[offset++] = 0; 437 | 438 | return offset - 1; 439 | } 440 | -------------------------------------------------------------------------------- /source/libs/ethers/src/ethers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * Copyright (c) 2017 Yuet Loo Wong 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #ifndef __ETHERS_H_ 27 | #define __ETHERS_H_ 28 | 29 | #include 30 | #include 31 | 32 | typedef struct Transaction { 33 | uint8_t *rawData; 34 | uint16_t rawDataLength; 35 | 36 | uint32_t nonce; 37 | 38 | uint32_t gasPrice; 39 | uint16_t gasPriceLow; 40 | 41 | uint32_t gasLimit; 42 | 43 | uint8_t *address; 44 | bool hasAddress; 45 | 46 | uint8_t *value; 47 | uint8_t valueLength; 48 | 49 | uint8_t *data; 50 | uint16_t dataLength; 51 | 52 | uint8_t chainId; 53 | } Transaction; 54 | 55 | #ifdef __cplusplus 56 | extern "C" { 57 | #endif /* __cplusplus */ 58 | 59 | 60 | bool ethers_decodeTransaction(Transaction* transaction, uint8_t * data, uint16_t length); 61 | 62 | 63 | #define ETHERS_PRIVATEKEY_LENGTH 32 64 | #define ETHERS_PUBLICKEY_LENGTH 64 65 | #define ETHERS_ADDRESS_LENGTH 20 66 | 67 | 68 | bool ethers_privateKeyToAddress(const uint8_t *privateKey, uint8_t *address); 69 | 70 | // "0x" + (40 bytes address) + "\0" 71 | #define ETHERS_CHECKSUM_ADDRESS_LENGTH (2 + 40 + 1) 72 | 73 | bool ethers_privateKeyToChecksumAddress(const uint8_t *privateKey, char *address); 74 | 75 | // NOTE: As long as the buffer is large enough, this can be called with both 76 | // parameters as the same buffer (it can compute it in-place) 77 | void ethers_addressToChecksumAddress(const uint8_t *address, char *checksumAddress); 78 | 79 | #define ETHERS_KECCAK256_LENGTH 32 80 | 81 | void ethers_keccak256(const uint8_t *data, uint16_t length, uint8_t *result); 82 | 83 | //void ethers_sha256(uint8_t *data, uint16_t length, uint8_t *result); 84 | 85 | uint8_t *ethers_debug(); 86 | 87 | #define ETHERS_SIGNATURE_LENGTH 64 88 | 89 | bool ethers_sign(const uint8_t *privateKey, const uint8_t *digest, uint8_t *result); 90 | 91 | 92 | uint8_t ethers_getStringLength(uint8_t *value, uint8_t length); 93 | uint8_t ethers_toString(uint8_t *amountWei, uint8_t amountWeiLength, uint8_t skipDecimal, char *result); 94 | 95 | 96 | uint16_t ethers_getHexStringLength(uint16_t length); 97 | void ethers_toHexString(uint8_t *value, uint16_t length, char *result); 98 | 99 | 100 | #ifdef __cplusplus 101 | } 102 | #endif /* __cplusplus */ 103 | 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /source/libs/ethers/src/keccak256.c: -------------------------------------------------------------------------------- 1 | /* sha3 - an implementation of Secure Hash Algorithm 3 (Keccak). 2 | * based on the 3 | * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 4 | * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche 5 | * 6 | * Copyright: 2013 Aleksey Kravchenko 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the "Software"), 10 | * to deal in the Software without restriction, including without limitation 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | * and/or sell copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so. 14 | * 15 | * This program is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17 | * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! 18 | */ 19 | 20 | #include "keccak256.h" 21 | 22 | //#include 23 | 24 | #include 25 | #include 26 | 27 | #define BLOCK_SIZE ((1600 - 256 * 2) / 8) 28 | 29 | #define I64(x) x##LL 30 | #define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) 31 | #define le2me_64(x) (x) 32 | #define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) 33 | #define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) 34 | 35 | /* constants */ 36 | 37 | //const uint8_t round_constant_info[] PROGMEM = { 38 | //const uint8_t constants[] PROGMEM = { 39 | const uint8_t constants[] = { 40 | 41 | 1, 26, 94, 112, 31, 33, 121, 85, 14, 12, 53, 38, 63, 79, 93, 83, 82, 72, 22, 102, 121, 88, 33, 116, 42 | //}; 43 | 44 | //const uint8_t pi_transform[] PROGMEM = { 45 | 1, 6, 9, 22, 14, 20, 2, 12, 13, 19, 23, 15, 4, 24, 21, 8, 16, 5, 3, 18, 17, 11, 7, 10, 46 | //}; 47 | 48 | //const uint8_t rhoTransforms[] PROGMEM = { 49 | 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14, 50 | }; 51 | 52 | #define TYPE_ROUND_INFO 0 53 | #define TYPE_PI_TRANSFORM 24 54 | #define TYPE_RHO_TRANSFORM 48 55 | 56 | uint8_t getConstant(uint8_t type, uint8_t index) { 57 | return constants[type + index]; 58 | //return pgm_read_byte(&constants[type + index]); 59 | } 60 | 61 | static uint64_t get_round_constant(uint8_t round) { 62 | uint64_t result = 0; 63 | 64 | //uint8_t roundInfo = pgm_read_byte(&round_constant_info[round]); 65 | uint8_t roundInfo = getConstant(TYPE_ROUND_INFO, round); 66 | if (roundInfo & (1 << 6)) { result |= ((uint64_t)1 << 63); } 67 | if (roundInfo & (1 << 5)) { result |= ((uint64_t)1 << 31); } 68 | if (roundInfo & (1 << 4)) { result |= ((uint64_t)1 << 15); } 69 | if (roundInfo & (1 << 3)) { result |= ((uint64_t)1 << 7); } 70 | if (roundInfo & (1 << 2)) { result |= ((uint64_t)1 << 3); } 71 | if (roundInfo & (1 << 1)) { result |= ((uint64_t)1 << 1); } 72 | if (roundInfo & (1 << 0)) { result |= ((uint64_t)1 << 0); } 73 | 74 | return result; 75 | } 76 | 77 | 78 | /* Initializing a sha3 context for given number of output bits */ 79 | void keccak_init(SHA3_CTX *ctx) { 80 | /* NB: The Keccak capacity parameter = bits * 2 */ 81 | 82 | memset(ctx, 0, sizeof(SHA3_CTX)); 83 | } 84 | 85 | /* Keccak theta() transformation */ 86 | static void keccak_theta(uint64_t *A) { 87 | uint64_t C[5], D[5]; 88 | 89 | for (uint8_t i = 0; i < 5; i++) { 90 | C[i] = A[i]; 91 | for (uint8_t j = 5; j < 25; j += 5) { C[i] ^= A[i + j]; } 92 | } 93 | 94 | for (uint8_t i = 0; i < 5; i++) { 95 | D[i] = ROTL64(C[(i + 1) % 5], 1) ^ C[(i + 4) % 5]; 96 | } 97 | 98 | for (uint8_t i = 0; i < 5; i++) { 99 | //for (uint8_t j = 0; j < 25; j += 5) { 100 | for (uint8_t j = 0; j < 25; j += 5) { A[i + j] ^= D[i]; } 101 | } 102 | } 103 | 104 | 105 | /* Keccak pi() transformation */ 106 | static void keccak_pi(uint64_t *A) { 107 | uint64_t A1 = A[1]; 108 | //for (uint8_t i = 1; i < sizeof(pi_transform); i++) { 109 | for (uint8_t i = 1; i < 24; i++) { 110 | //A[pgm_read_byte(&pi_transform[i - 1])] = A[pgm_read_byte(&pi_transform[i])]; 111 | A[getConstant(TYPE_PI_TRANSFORM, i - 1)] = A[getConstant(TYPE_PI_TRANSFORM, i)]; 112 | } 113 | A[10] = A1; 114 | /* note: A[ 0] is left as is */ 115 | } 116 | 117 | /* 118 | ketch uses 30084 bytes (93%) of program storage space. Maximum is 32256 bytes. 119 | Global variables use 743 bytes (36%) of dynamic memory, leaving 1305 bytes for local variables. Maximum is 2048 bytes. 120 | 121 | */ 122 | /* Keccak chi() transformation */ 123 | static void keccak_chi(uint64_t *A) { 124 | for (uint8_t i = 0; i < 25; i += 5) { 125 | uint64_t A0 = A[0 + i], A1 = A[1 + i]; 126 | A[0 + i] ^= ~A1 & A[2 + i]; 127 | A[1 + i] ^= ~A[2 + i] & A[3 + i]; 128 | A[2 + i] ^= ~A[3 + i] & A[4 + i]; 129 | A[3 + i] ^= ~A[4 + i] & A0; 130 | A[4 + i] ^= ~A0 & A1; 131 | } 132 | } 133 | 134 | 135 | static void sha3_permutation(uint64_t *state) { 136 | //for (uint8_t round = 0; round < sizeof(round_constant_info); round++) { 137 | for (uint8_t round = 0; round < 24; round++) { 138 | keccak_theta(state); 139 | 140 | /* apply Keccak rho() transformation */ 141 | for (uint8_t i = 1; i < 25; i++) { 142 | //state[i] = ROTL64(state[i], pgm_read_byte(&rhoTransforms[i - 1])); 143 | state[i] = ROTL64(state[i], getConstant(TYPE_RHO_TRANSFORM, i - 1)); 144 | } 145 | 146 | keccak_pi(state); 147 | keccak_chi(state); 148 | 149 | /* apply iota(state, round) */ 150 | *state ^= get_round_constant(round); 151 | } 152 | } 153 | 154 | /** 155 | * The core transformation. Process the specified block of data. 156 | * 157 | * @param hash the algorithm state 158 | * @param block the message block to process 159 | * @param block_size the size of the processed block in bytes 160 | */ 161 | static void sha3_process_block(uint64_t hash[25], const uint64_t *block) { 162 | for (uint8_t i = 0; i < 17; i++) { 163 | hash[i] ^= le2me_64(block[i]); 164 | } 165 | 166 | /* make a permutation of the hash */ 167 | sha3_permutation(hash); 168 | } 169 | 170 | //#define SHA3_FINALIZED 0x80000000 171 | //#define SHA3_FINALIZED 0x8000 172 | 173 | /** 174 | * Calculate message hash. 175 | * Can be called repeatedly with chunks of the message to be hashed. 176 | * 177 | * @param ctx the algorithm context containing current hashing state 178 | * @param msg message chunk 179 | * @param size length of the message chunk 180 | */ 181 | void keccak_update(SHA3_CTX *ctx, const unsigned char *msg, uint16_t size) 182 | { 183 | uint16_t idx = (uint16_t)ctx->rest; 184 | 185 | //if (ctx->rest & SHA3_FINALIZED) return; /* too late for additional input */ 186 | ctx->rest = (unsigned)((ctx->rest + size) % BLOCK_SIZE); 187 | 188 | /* fill partial block */ 189 | if (idx) { 190 | uint16_t left = BLOCK_SIZE - idx; 191 | memcpy((char*)ctx->message + idx, msg, (size < left ? size : left)); 192 | if (size < left) return; 193 | 194 | /* process partial block */ 195 | sha3_process_block(ctx->hash, ctx->message); 196 | msg += left; 197 | size -= left; 198 | } 199 | 200 | while (size >= BLOCK_SIZE) { 201 | uint64_t* aligned_message_block; 202 | if (IS_ALIGNED_64(msg)) { 203 | // the most common case is processing of an already aligned message without copying it 204 | aligned_message_block = (uint64_t*)(void*)msg; 205 | } else { 206 | memcpy(ctx->message, msg, BLOCK_SIZE); 207 | aligned_message_block = ctx->message; 208 | } 209 | 210 | sha3_process_block(ctx->hash, aligned_message_block); 211 | msg += BLOCK_SIZE; 212 | size -= BLOCK_SIZE; 213 | } 214 | 215 | if (size) { 216 | memcpy(ctx->message, msg, size); /* save leftovers */ 217 | } 218 | } 219 | 220 | /** 221 | * Store calculated hash into the given array. 222 | * 223 | * @param ctx the algorithm context containing current hashing state 224 | * @param result calculated hash in binary form 225 | */ 226 | void keccak_final(SHA3_CTX *ctx, unsigned char* result) 227 | { 228 | uint16_t digest_length = 100 - BLOCK_SIZE / 2; 229 | 230 | // if (!(ctx->rest & SHA3_FINALIZED)) { 231 | /* clear the rest of the data queue */ 232 | memset((char*)ctx->message + ctx->rest, 0, BLOCK_SIZE - ctx->rest); 233 | ((char*)ctx->message)[ctx->rest] |= 0x01; 234 | ((char*)ctx->message)[BLOCK_SIZE - 1] |= 0x80; 235 | 236 | /* process final block */ 237 | sha3_process_block(ctx->hash, ctx->message); 238 | // ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ 239 | // } 240 | 241 | if (result) { 242 | me64_to_le_str(result, ctx->hash, digest_length); 243 | } 244 | } 245 | 246 | -------------------------------------------------------------------------------- /source/libs/ethers/src/keccak256.h: -------------------------------------------------------------------------------- 1 | /* sha3 - an implementation of Secure Hash Algorithm 3 (Keccak). 2 | * based on the 3 | * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 4 | * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche 5 | * 6 | * Copyright: 2013 Aleksey Kravchenko 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the "Software"), 10 | * to deal in the Software without restriction, including without limitation 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | * and/or sell copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so. 14 | * 15 | * This program is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17 | * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! 18 | */ 19 | 20 | #ifndef __KECCAK256_H_ 21 | #define __KECCAK256_H_ 22 | 23 | #include 24 | 25 | #define sha3_max_permutation_size 25 26 | #define sha3_max_rate_in_qwords 24 27 | 28 | typedef struct SHA3_CTX { 29 | /* 1600 bits algorithm hashing state */ 30 | uint64_t hash[sha3_max_permutation_size]; 31 | /* 1536-bit buffer for leftovers */ 32 | uint64_t message[sha3_max_rate_in_qwords]; 33 | /* count of bytes in the message[] buffer */ 34 | uint16_t rest; 35 | /* size of a message block processed at once */ 36 | //unsigned block_size; 37 | } SHA3_CTX; 38 | 39 | 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif /* __cplusplus */ 43 | 44 | 45 | void keccak_init(SHA3_CTX *ctx); 46 | void keccak_update(SHA3_CTX *ctx, const unsigned char *msg, uint16_t size); 47 | void keccak_final(SHA3_CTX *ctx, unsigned char* result); 48 | 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif /* __cplusplus */ 53 | 54 | #endif /* __KECCAK256_H_ */ 55 | -------------------------------------------------------------------------------- /source/libs/ethers/src/platform-specific.inc: -------------------------------------------------------------------------------- 1 | /* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ 2 | 3 | #ifndef _UECC_PLATFORM_SPECIFIC_H_ 4 | #define _UECC_PLATFORM_SPECIFIC_H_ 5 | 6 | #include "types.h" 7 | 8 | #if (defined(_WIN32) || defined(_WIN64)) 9 | /* Windows */ 10 | 11 | // use pragma syntax to prevent tweaking the linker script for getting CryptXYZ function 12 | #pragma comment(lib, "crypt32.lib") 13 | #pragma comment(lib, "advapi32.lib") 14 | 15 | #define WIN32_LEAN_AND_MEAN 16 | #include 17 | #include 18 | 19 | static int default_RNG(uint8_t *dest, unsigned size) { 20 | HCRYPTPROV prov; 21 | if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { 22 | return 0; 23 | } 24 | 25 | CryptGenRandom(prov, size, (BYTE *)dest); 26 | CryptReleaseContext(prov, 0); 27 | return 1; 28 | } 29 | #define default_RNG_defined 1 30 | 31 | #elif defined(unix) || defined(__linux__) || defined(__unix__) || defined(__unix) || \ 32 | (defined(__APPLE__) && defined(__MACH__)) || defined(uECC_POSIX) 33 | 34 | /* Some POSIX-like system with /dev/urandom or /dev/random. */ 35 | #include 36 | #include 37 | #include 38 | 39 | #ifndef O_CLOEXEC 40 | #define O_CLOEXEC 0 41 | #endif 42 | 43 | static int default_RNG(uint8_t *dest, unsigned size) { 44 | int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); 45 | if (fd == -1) { 46 | fd = open("/dev/random", O_RDONLY | O_CLOEXEC); 47 | if (fd == -1) { 48 | return 0; 49 | } 50 | } 51 | 52 | char *ptr = (char *)dest; 53 | size_t left = size; 54 | while (left > 0) { 55 | ssize_t bytes_read = read(fd, ptr, left); 56 | if (bytes_read <= 0) { // read failed 57 | close(fd); 58 | return 0; 59 | } 60 | left -= bytes_read; 61 | ptr += bytes_read; 62 | } 63 | 64 | close(fd); 65 | return 1; 66 | } 67 | #define default_RNG_defined 1 68 | 69 | #endif /* platform */ 70 | 71 | #endif /* _UECC_PLATFORM_SPECIFIC_H_ */ 72 | -------------------------------------------------------------------------------- /source/libs/ethers/src/types.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ 2 | 3 | #ifndef _UECC_TYPES_H_ 4 | #define _UECC_TYPES_H_ 5 | 6 | #include "uECC.h" 7 | 8 | #ifndef uECC_PLATFORM 9 | #if __AVR__ 10 | #define uECC_PLATFORM uECC_avr 11 | #elif defined(__thumb2__) || defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ 12 | #define uECC_PLATFORM uECC_arm_thumb2 13 | #elif defined(__thumb__) 14 | #define uECC_PLATFORM uECC_arm_thumb 15 | #elif defined(__arm__) || defined(_M_ARM) 16 | #define uECC_PLATFORM uECC_arm 17 | #elif defined(__aarch64__) 18 | #define uECC_PLATFORM uECC_arm64 19 | #elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__) 20 | #define uECC_PLATFORM uECC_x86 21 | #elif defined(__amd64__) || defined(_M_X64) 22 | #define uECC_PLATFORM uECC_x86_64 23 | #else 24 | #define uECC_PLATFORM uECC_arch_other 25 | #endif 26 | #endif 27 | 28 | #ifndef uECC_ARM_USE_UMAAL 29 | #if (uECC_PLATFORM == uECC_arm) && (__ARM_ARCH >= 6) 30 | #define uECC_ARM_USE_UMAAL 1 31 | #elif (uECC_PLATFORM == uECC_arm_thumb2) && (__ARM_ARCH >= 6) && !__ARM_ARCH_7M__ 32 | #define uECC_ARM_USE_UMAAL 1 33 | #else 34 | #define uECC_ARM_USE_UMAAL 0 35 | #endif 36 | #endif 37 | 38 | #ifndef uECC_WORD_SIZE 39 | #if uECC_PLATFORM == uECC_avr 40 | #define uECC_WORD_SIZE 1 41 | #elif (uECC_PLATFORM == uECC_x86_64 || uECC_PLATFORM == uECC_arm64) 42 | #define uECC_WORD_SIZE 8 43 | #else 44 | #define uECC_WORD_SIZE 4 45 | #endif 46 | #endif 47 | 48 | #if (uECC_WORD_SIZE != 1) && (uECC_WORD_SIZE != 4) && (uECC_WORD_SIZE != 8) 49 | #error "Unsupported value for uECC_WORD_SIZE" 50 | #endif 51 | 52 | #if ((uECC_PLATFORM == uECC_avr) && (uECC_WORD_SIZE != 1)) 53 | #pragma message ("uECC_WORD_SIZE must be 1 for AVR") 54 | #undef uECC_WORD_SIZE 55 | #define uECC_WORD_SIZE 1 56 | #endif 57 | 58 | #if ((uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ 59 | uECC_PLATFORM == uECC_arm_thumb2) && \ 60 | (uECC_WORD_SIZE != 4)) 61 | #pragma message ("uECC_WORD_SIZE must be 4 for ARM") 62 | #undef uECC_WORD_SIZE 63 | #define uECC_WORD_SIZE 4 64 | #endif 65 | 66 | #if defined(__SIZEOF_INT128__) || ((__clang_major__ * 100 + __clang_minor__) >= 302) 67 | #define SUPPORTS_INT128 1 68 | #else 69 | #define SUPPORTS_INT128 0 70 | #endif 71 | 72 | typedef int8_t wordcount_t; 73 | typedef int16_t bitcount_t; 74 | typedef int8_t cmpresult_t; 75 | 76 | #if (uECC_WORD_SIZE == 1) 77 | 78 | typedef uint8_t uECC_word_t; 79 | typedef uint16_t uECC_dword_t; 80 | 81 | #define HIGH_BIT_SET 0x80 82 | #define uECC_WORD_BITS 8 83 | #define uECC_WORD_BITS_SHIFT 3 84 | #define uECC_WORD_BITS_MASK 0x07 85 | 86 | #elif (uECC_WORD_SIZE == 4) 87 | 88 | typedef uint32_t uECC_word_t; 89 | typedef uint64_t uECC_dword_t; 90 | 91 | #define HIGH_BIT_SET 0x80000000 92 | #define uECC_WORD_BITS 32 93 | #define uECC_WORD_BITS_SHIFT 5 94 | #define uECC_WORD_BITS_MASK 0x01F 95 | 96 | #elif (uECC_WORD_SIZE == 8) 97 | 98 | typedef uint64_t uECC_word_t; 99 | #if SUPPORTS_INT128 100 | typedef unsigned __int128 uECC_dword_t; 101 | #endif 102 | 103 | #define HIGH_BIT_SET 0x8000000000000000ull 104 | #define uECC_WORD_BITS 64 105 | #define uECC_WORD_BITS_SHIFT 6 106 | #define uECC_WORD_BITS_MASK 0x03F 107 | 108 | #endif /* uECC_WORD_SIZE */ 109 | 110 | #endif /* _UECC_TYPES_H_ */ 111 | -------------------------------------------------------------------------------- /source/libs/ethers/src/uECC.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ 2 | 3 | #ifndef _UECC_H_ 4 | #define _UECC_H_ 5 | 6 | #include 7 | 8 | /* Platform selection options. 9 | If uECC_PLATFORM is not defined, the code will try to guess it based on compiler macros. 10 | Possible values for uECC_PLATFORM are defined below: */ 11 | #define uECC_arch_other 0 12 | #define uECC_x86 1 13 | #define uECC_x86_64 2 14 | #define uECC_arm 3 15 | #define uECC_arm_thumb 4 16 | #define uECC_arm_thumb2 5 17 | #define uECC_arm64 6 18 | #define uECC_avr 7 19 | 20 | /* If desired, you can define uECC_WORD_SIZE as appropriate for your platform (1, 4, or 8 bytes). 21 | If uECC_WORD_SIZE is not explicitly defined then it will be automatically set based on your 22 | platform. */ 23 | 24 | /* Optimization level; trade speed for code size. 25 | Larger values produce code that is faster but larger. 26 | Currently supported values are 0 - 4; 0 is unusably slow for most applications. 27 | Optimization level 4 currently only has an effect ARM platforms where more than one 28 | curve is enabled. */ 29 | #ifndef uECC_OPTIMIZATION_LEVEL 30 | #define uECC_OPTIMIZATION_LEVEL 2 31 | #endif 32 | 33 | /* uECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a specific function to be 34 | used for (scalar) squaring instead of the generic multiplication function. This can make things 35 | faster somewhat faster, but increases the code size. */ 36 | #ifndef uECC_SQUARE_FUNC 37 | #define uECC_SQUARE_FUNC 0 38 | #endif 39 | 40 | /* uECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will switch to native 41 | little-endian format for *all* arrays passed in and out of the public API. This includes public 42 | and private keys, shared secrets, signatures and message hashes. 43 | Using this switch reduces the amount of call stack memory used by uECC, since less intermediate 44 | translations are required. 45 | Note that this will *only* work on native little-endian processors and it will treat the uint8_t 46 | arrays passed into the public API as word arrays, therefore requiring the provided byte arrays 47 | to be word aligned on architectures that do not support unaligned accesses. 48 | IMPORTANT: Keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible 49 | with keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use 50 | the same endianness. */ 51 | #ifndef uECC_VLI_NATIVE_LITTLE_ENDIAN 52 | #define uECC_VLI_NATIVE_LITTLE_ENDIAN 0 53 | #endif 54 | 55 | /* Curve support selection. Set to 0 to remove that curve. */ 56 | #ifndef uECC_SUPPORTS_secp160r1 57 | #define uECC_SUPPORTS_secp160r1 0 58 | #endif 59 | #ifndef uECC_SUPPORTS_secp192r1 60 | #define uECC_SUPPORTS_secp192r1 0 61 | #endif 62 | #ifndef uECC_SUPPORTS_secp224r1 63 | #define uECC_SUPPORTS_secp224r1 1 64 | #endif 65 | #ifndef uECC_SUPPORTS_secp256r1 66 | #define uECC_SUPPORTS_secp256r1 0 67 | #endif 68 | #ifndef uECC_SUPPORTS_secp256k1 69 | #define uECC_SUPPORTS_secp256k1 1 70 | #endif 71 | 72 | /* Specifies whether compressed point format is supported. 73 | Set to 0 to disable point compression/decompression functions. */ 74 | #ifndef uECC_SUPPORT_COMPRESSED_POINT 75 | #define uECC_SUPPORT_COMPRESSED_POINT 0 76 | #endif 77 | 78 | struct uECC_Curve_t; 79 | typedef const struct uECC_Curve_t * uECC_Curve; 80 | 81 | #ifdef __cplusplus 82 | extern "C" 83 | { 84 | #endif 85 | 86 | #if uECC_SUPPORTS_secp160r1 87 | uECC_Curve uECC_secp160r1(void); 88 | #endif 89 | #if uECC_SUPPORTS_secp192r1 90 | uECC_Curve uECC_secp192r1(void); 91 | #endif 92 | #if uECC_SUPPORTS_secp256r1 93 | uECC_Curve uECC_secp256r1(void); 94 | #endif 95 | #if uECC_SUPPORTS_secp256k1 96 | uECC_Curve uECC_secp256k1(void); 97 | #endif 98 | 99 | /* uECC_RNG_Function type 100 | The RNG function should fill 'size' random bytes into 'dest'. It should return 1 if 101 | 'dest' was filled with random data, or 0 if the random data could not be generated. 102 | The filled-in values should be either truly random, or from a cryptographically-secure PRNG. 103 | 104 | A correctly functioning RNG function must be set (using uECC_set_rng()) before calling 105 | uECC_make_key() or uECC_sign(). 106 | 107 | Setting a correctly functioning RNG function improves the resistance to side-channel attacks 108 | for uECC_shared_secret() and uECC_sign_deterministic(). 109 | 110 | A correct RNG function is set by default when building for Windows, Linux, or OS X. 111 | If you are building on another POSIX-compliant system that supports /dev/random or /dev/urandom, 112 | you can define uECC_POSIX to use the predefined RNG. For embedded platforms there is no predefined 113 | RNG function; you must provide your own. 114 | */ 115 | typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); 116 | 117 | /* uECC_set_rng() function. 118 | Set the function that will be used to generate random bytes. The RNG function should 119 | return 1 if the random data was generated, or 0 if the random data could not be generated. 120 | 121 | On platforms where there is no predefined RNG function (eg embedded platforms), this must 122 | be called before uECC_make_key() or uECC_sign() are used. 123 | 124 | Inputs: 125 | rng_function - The function that will be used to generate random bytes. 126 | */ 127 | void uECC_set_rng(uECC_RNG_Function rng_function); 128 | 129 | /* uECC_get_rng() function. 130 | 131 | Returns the function that will be used to generate random bytes. 132 | */ 133 | uECC_RNG_Function uECC_get_rng(void); 134 | 135 | /* uECC_curve_private_key_size() function. 136 | 137 | Returns the size of a private key for the curve in bytes. 138 | */ 139 | int uECC_curve_private_key_size(uECC_Curve curve); 140 | 141 | /* uECC_curve_public_key_size() function. 142 | 143 | Returns the size of a public key for the curve in bytes. 144 | */ 145 | int uECC_curve_public_key_size(uECC_Curve curve); 146 | 147 | /* uECC_make_key() function. 148 | Create a public/private key pair. 149 | 150 | Outputs: 151 | public_key - Will be filled in with the public key. Must be at least 2 * the curve size 152 | (in bytes) long. For example, if the curve is secp256r1, public_key must be 64 153 | bytes long. 154 | private_key - Will be filled in with the private key. Must be as long as the curve order; this 155 | is typically the same as the curve size, except for secp160r1. For example, if the 156 | curve is secp256r1, private_key must be 32 bytes long. 157 | 158 | For secp160r1, private_key must be 21 bytes long! Note that the first byte will 159 | almost always be 0 (there is about a 1 in 2^80 chance of it being non-zero). 160 | 161 | Returns 1 if the key pair was generated successfully, 0 if an error occurred. 162 | */ 163 | int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve); 164 | 165 | /* uECC_shared_secret() function. 166 | Compute a shared secret given your secret key and someone else's public key. 167 | Note: It is recommended that you hash the result of uECC_shared_secret() before using it for 168 | symmetric encryption or HMAC. 169 | 170 | Inputs: 171 | public_key - The public key of the remote party. 172 | private_key - Your private key. 173 | 174 | Outputs: 175 | secret - Will be filled in with the shared secret value. Must be the same size as the 176 | curve size; for example, if the curve is secp256r1, secret must be 32 bytes long. 177 | 178 | Returns 1 if the shared secret was generated successfully, 0 if an error occurred. 179 | */ 180 | int uECC_shared_secret(const uint8_t *public_key, 181 | const uint8_t *private_key, 182 | uint8_t *secret, 183 | uECC_Curve curve); 184 | 185 | #if uECC_SUPPORT_COMPRESSED_POINT 186 | /* uECC_compress() function. 187 | Compress a public key. 188 | 189 | Inputs: 190 | public_key - The public key to compress. 191 | 192 | Outputs: 193 | compressed - Will be filled in with the compressed public key. Must be at least 194 | (curve size + 1) bytes long; for example, if the curve is secp256r1, 195 | compressed must be 33 bytes long. 196 | */ 197 | void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve); 198 | 199 | /* uECC_decompress() function. 200 | Decompress a compressed public key. 201 | 202 | Inputs: 203 | compressed - The compressed public key. 204 | 205 | Outputs: 206 | public_key - Will be filled in with the decompressed public key. 207 | */ 208 | void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve); 209 | #endif /* uECC_SUPPORT_COMPRESSED_POINT */ 210 | 211 | /* uECC_valid_public_key() function. 212 | Check to see if a public key is valid. 213 | 214 | Note that you are not required to check for a valid public key before using any other uECC 215 | functions. However, you may wish to avoid spending CPU time computing a shared secret or 216 | verifying a signature using an invalid public key. 217 | 218 | Inputs: 219 | public_key - The public key to check. 220 | 221 | Returns 1 if the public key is valid, 0 if it is invalid. 222 | */ 223 | int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); 224 | 225 | /* uECC_compute_public_key() function. 226 | Compute the corresponding public key for a private key. 227 | 228 | Inputs: 229 | private_key - The private key to compute the public key for 230 | 231 | Outputs: 232 | public_key - Will be filled in with the corresponding public key 233 | 234 | Returns 1 if the key was computed successfully, 0 if an error occurred. 235 | */ 236 | int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve); 237 | 238 | /* uECC_sign() function. 239 | Generate an ECDSA signature for a given hash value. 240 | 241 | Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to 242 | this function along with your private key. 243 | 244 | Inputs: 245 | private_key - Your private key. 246 | message_hash - The hash of the message to sign. 247 | hash_size - The size of message_hash in bytes. 248 | 249 | Outputs: 250 | signature - Will be filled in with the signature value. Must be at least 2 * curve size long. 251 | For example, if the curve is secp256r1, signature must be 64 bytes long. 252 | 253 | Returns 1 if the signature generated successfully, 0 if an error occurred. 254 | */ 255 | int uECC_sign(const uint8_t *private_key, 256 | const uint8_t *message_hash, 257 | unsigned hash_size, 258 | uint8_t *signature, 259 | uECC_Curve curve); 260 | 261 | /* uECC_HashContext structure. 262 | This is used to pass in an arbitrary hash function to uECC_sign_deterministic(). 263 | The structure will be used for multiple hash computations; each time a new hash 264 | is computed, init_hash() will be called, followed by one or more calls to 265 | update_hash(), and finally a call to finish_hash() to produce the resulting hash. 266 | 267 | The intention is that you will create a structure that includes uECC_HashContext 268 | followed by any hash-specific data. For example: 269 | 270 | typedef struct SHA256_HashContext { 271 | uECC_HashContext uECC; 272 | SHA256_CTX ctx; 273 | } SHA256_HashContext; 274 | 275 | void init_SHA256(uECC_HashContext *base) { 276 | SHA256_HashContext *context = (SHA256_HashContext *)base; 277 | SHA256_Init(&context->ctx); 278 | } 279 | 280 | void update_SHA256(uECC_HashContext *base, 281 | const uint8_t *message, 282 | unsigned message_size) { 283 | SHA256_HashContext *context = (SHA256_HashContext *)base; 284 | SHA256_Update(&context->ctx, message, message_size); 285 | } 286 | 287 | void finish_SHA256(uECC_HashContext *base, uint8_t *hash_result) { 288 | SHA256_HashContext *context = (SHA256_HashContext *)base; 289 | SHA256_Final(hash_result, &context->ctx); 290 | } 291 | 292 | ... when signing ... 293 | { 294 | uint8_t tmp[32 + 32 + 64]; 295 | SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}}; 296 | uECC_sign_deterministic(key, message_hash, &ctx.uECC, signature); 297 | } 298 | */ 299 | typedef struct uECC_HashContext { 300 | void (*init_hash)(const struct uECC_HashContext *context); 301 | void (*update_hash)(const struct uECC_HashContext *context, 302 | const uint8_t *message, 303 | unsigned message_size); 304 | void (*finish_hash)(const struct uECC_HashContext *context, uint8_t *hash_result); 305 | unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ 306 | unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ 307 | uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ 308 | } uECC_HashContext; 309 | 310 | /* uECC_sign_deterministic() function. 311 | Generate an ECDSA signature for a given hash value, using a deterministic algorithm 312 | (see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling 313 | this function; however, if the RNG is defined it will improve resistance to side-channel 314 | attacks. 315 | 316 | Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it to 317 | this function along with your private key and a hash context. Note that the message_hash 318 | does not need to be computed with the same hash function used by hash_context. 319 | 320 | Inputs: 321 | private_key - Your private key. 322 | message_hash - The hash of the message to sign. 323 | hash_size - The size of message_hash in bytes. 324 | hash_context - A hash context to use. 325 | 326 | Outputs: 327 | signature - Will be filled in with the signature value. 328 | 329 | Returns 1 if the signature generated successfully, 0 if an error occurred. 330 | */ 331 | int uECC_sign_deterministic(const uint8_t *private_key, 332 | const uint8_t *message_hash, 333 | unsigned hash_size, 334 | const uECC_HashContext *hash_context, 335 | uint8_t *signature, 336 | uECC_Curve curve); 337 | 338 | /* uECC_verify() function. 339 | Verify an ECDSA signature. 340 | 341 | Usage: Compute the hash of the signed data using the same hash as the signer and 342 | pass it to this function along with the signer's public key and the signature values (r and s). 343 | 344 | Inputs: 345 | public_key - The signer's public key. 346 | message_hash - The hash of the signed data. 347 | hash_size - The size of message_hash in bytes. 348 | signature - The signature value. 349 | 350 | Returns 1 if the signature is valid, 0 if it is invalid. 351 | */ 352 | int uECC_verify(const uint8_t *public_key, 353 | const uint8_t *message_hash, 354 | unsigned hash_size, 355 | const uint8_t *signature, 356 | uECC_Curve curve); 357 | 358 | 359 | #ifdef __cplusplus 360 | } /* end of extern "C" */ 361 | #endif 362 | 363 | #endif /* _UECC_H_ */ 364 | -------------------------------------------------------------------------------- /source/libs/ethers/src/uECC_vli.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ 2 | 3 | #ifndef _UECC_VLI_H_ 4 | #define _UECC_VLI_H_ 5 | 6 | #include "uECC.h" 7 | #include "types.h" 8 | 9 | /* Functions for raw large-integer manipulation. These are only available 10 | if uECC.c is compiled with uECC_ENABLE_VLI_API defined to 1. */ 11 | #ifndef uECC_ENABLE_VLI_API 12 | #define uECC_ENABLE_VLI_API 0 13 | #endif 14 | 15 | #ifdef __cplusplus 16 | extern "C" 17 | { 18 | #endif 19 | 20 | #if uECC_ENABLE_VLI_API 21 | 22 | void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); 23 | 24 | /* Constant-time comparison to zero - secure way to compare long integers */ 25 | /* Returns 1 if vli == 0, 0 otherwise. */ 26 | uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); 27 | 28 | /* Returns nonzero if bit 'bit' of vli is set. */ 29 | uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); 30 | 31 | /* Counts the number of bits required to represent vli. */ 32 | bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words); 33 | 34 | /* Sets dest = src. */ 35 | void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words); 36 | 37 | /* Constant-time comparison function - secure way to compare long integers */ 38 | /* Returns one if left == right, zero otherwise */ 39 | uECC_word_t uECC_vli_equal(const uECC_word_t *left, 40 | const uECC_word_t *right, 41 | wordcount_t num_words); 42 | 43 | /* Constant-time comparison function - secure way to compare long integers */ 44 | /* Returns sign of left - right, in constant time. */ 45 | cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, wordcount_t num_words); 46 | 47 | /* Computes vli = vli >> 1. */ 48 | void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words); 49 | 50 | /* Computes result = left + right, returning carry. Can modify in place. */ 51 | uECC_word_t uECC_vli_add(uECC_word_t *result, 52 | const uECC_word_t *left, 53 | const uECC_word_t *right, 54 | wordcount_t num_words); 55 | 56 | /* Computes result = left - right, returning borrow. Can modify in place. */ 57 | uECC_word_t uECC_vli_sub(uECC_word_t *result, 58 | const uECC_word_t *left, 59 | const uECC_word_t *right, 60 | wordcount_t num_words); 61 | 62 | /* Computes result = left * right. Result must be 2 * num_words long. */ 63 | void uECC_vli_mult(uECC_word_t *result, 64 | const uECC_word_t *left, 65 | const uECC_word_t *right, 66 | wordcount_t num_words); 67 | 68 | /* Computes result = left^2. Result must be 2 * num_words long. */ 69 | void uECC_vli_square(uECC_word_t *result, const uECC_word_t *left, wordcount_t num_words); 70 | 71 | /* Computes result = (left + right) % mod. 72 | Assumes that left < mod and right < mod, and that result does not overlap mod. */ 73 | void uECC_vli_modAdd(uECC_word_t *result, 74 | const uECC_word_t *left, 75 | const uECC_word_t *right, 76 | const uECC_word_t *mod, 77 | wordcount_t num_words); 78 | 79 | /* Computes result = (left - right) % mod. 80 | Assumes that left < mod and right < mod, and that result does not overlap mod. */ 81 | void uECC_vli_modSub(uECC_word_t *result, 82 | const uECC_word_t *left, 83 | const uECC_word_t *right, 84 | const uECC_word_t *mod, 85 | wordcount_t num_words); 86 | 87 | /* Computes result = product % mod, where product is 2N words long. 88 | Currently only designed to work for mod == curve->p or curve_n. */ 89 | void uECC_vli_mmod(uECC_word_t *result, 90 | uECC_word_t *product, 91 | const uECC_word_t *mod, 92 | wordcount_t num_words); 93 | 94 | /* Calculates result = product (mod curve->p), where product is up to 95 | 2 * curve->num_words long. */ 96 | void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve); 97 | 98 | /* Computes result = (left * right) % mod. 99 | Currently only designed to work for mod == curve->p or curve_n. */ 100 | void uECC_vli_modMult(uECC_word_t *result, 101 | const uECC_word_t *left, 102 | const uECC_word_t *right, 103 | const uECC_word_t *mod, 104 | wordcount_t num_words); 105 | 106 | /* Computes result = (left * right) % curve->p. */ 107 | void uECC_vli_modMult_fast(uECC_word_t *result, 108 | const uECC_word_t *left, 109 | const uECC_word_t *right, 110 | uECC_Curve curve); 111 | 112 | /* Computes result = left^2 % mod. 113 | Currently only designed to work for mod == curve->p or curve_n. */ 114 | void uECC_vli_modSquare(uECC_word_t *result, 115 | const uECC_word_t *left, 116 | const uECC_word_t *mod, 117 | wordcount_t num_words); 118 | 119 | /* Computes result = left^2 % curve->p. */ 120 | void uECC_vli_modSquare_fast(uECC_word_t *result, const uECC_word_t *left, uECC_Curve curve); 121 | 122 | /* Computes result = (1 / input) % mod.*/ 123 | void uECC_vli_modInv(uECC_word_t *result, 124 | const uECC_word_t *input, 125 | const uECC_word_t *mod, 126 | wordcount_t num_words); 127 | 128 | #if uECC_SUPPORT_COMPRESSED_POINT 129 | /* Calculates a = sqrt(a) (mod curve->p) */ 130 | void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve); 131 | #endif 132 | 133 | /* Converts an integer in uECC native format to big-endian bytes. */ 134 | void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, const uECC_word_t *native); 135 | /* Converts big-endian bytes to an integer in uECC native format. */ 136 | void uECC_vli_bytesToNative(uECC_word_t *native, const uint8_t *bytes, int num_bytes); 137 | 138 | unsigned uECC_curve_num_words(uECC_Curve curve); 139 | unsigned uECC_curve_num_bytes(uECC_Curve curve); 140 | unsigned uECC_curve_num_bits(uECC_Curve curve); 141 | unsigned uECC_curve_num_n_words(uECC_Curve curve); 142 | unsigned uECC_curve_num_n_bytes(uECC_Curve curve); 143 | unsigned uECC_curve_num_n_bits(uECC_Curve curve); 144 | 145 | const uECC_word_t *uECC_curve_p(uECC_Curve curve); 146 | const uECC_word_t *uECC_curve_n(uECC_Curve curve); 147 | const uECC_word_t *uECC_curve_G(uECC_Curve curve); 148 | const uECC_word_t *uECC_curve_b(uECC_Curve curve); 149 | 150 | int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); 151 | 152 | /* Multiplies a point by a scalar. Points are represented by the X coordinate followed by 153 | the Y coordinate in the same array, both coordinates are curve->num_words long. Note 154 | that scalar must be curve->num_n_words long (NOT curve->num_words). */ 155 | void uECC_point_mult(uECC_word_t *result, 156 | const uECC_word_t *point, 157 | const uECC_word_t *scalar, 158 | uECC_Curve curve); 159 | 160 | /* Generates a random integer in the range 0 < random < top. 161 | Both random and top have num_words words. */ 162 | int uECC_generate_random_int(uECC_word_t *random, 163 | const uECC_word_t *top, 164 | wordcount_t num_words); 165 | 166 | #endif /* uECC_ENABLE_VLI_API */ 167 | 168 | #ifdef __cplusplus 169 | } /* end of extern "C" */ 170 | #endif 171 | 172 | #endif /* _UECC_VLI_H_ */ 173 | -------------------------------------------------------------------------------- /source/libs/firefly_blecast/README.md: -------------------------------------------------------------------------------- 1 | BLECast 2 | ======= 3 | 4 | BLECast is a protocol and library to send messages (up to 756 bytes) over the BLE 5 | advertsizing data, from iOS to an Arduino using the nRF2401. 6 | 7 | **Features:** 8 | 9 | - Works out of the box with iOS 10 | - Up to 756 bytes per message 11 | - nRF2401 is cheap ($1 or less) 12 | 13 | 14 | **Note:** This library and protocol are still VERY experimental; the API and protocol may change. 15 | 16 | 17 | API 18 | --- 19 | 20 | ### BLECastMessage 21 | 22 | An instance of this structure us used to manage the currently discovered blocks and 23 | the radio state. 24 | 25 | ```c 26 | #define BLECAST_INVALID_ID 0x7f000000 27 | 28 | typedef struct BLECastMessage { 29 | 30 | // The number of unique payloads that have been successfully discovered. 31 | int8_t discoveredPayloadCount; 32 | 33 | // The total number of expected payloads. This is -1 if the length 34 | // is not yet known. 35 | int8_t totalPayloadCount; 36 | 37 | // The size (in bytes) of the message and the message data. 38 | int16_t size; 39 | uint8_t *data; 40 | 41 | // The ID of the message (the CRC-24 of the total message) 42 | // This is equal to BLECAST_INVALID_ID until a message is complete 43 | uint32_t id; 44 | 45 | // Generally, do NOT touch these 46 | void *headPayload; 47 | void *aesContext; 48 | void *radioInfo; 49 | } BLECastMessage; 50 | ``` 51 | 52 | ### Initializing 53 | 54 | Initialize *message* with the 128-bit (16 byte) secret *key* used to decrypt payloads 55 | and the *pin1* and *pin2* indicate the CE and CSN pins of the nRF2401. 56 | 57 | ```c 58 | bool blecast_init(BLECastMessage *message, uint8_t key[16], uint8_t pin1, uint8_t pin2) 59 | ``` 60 | 61 | 62 | ### Polling 63 | 64 | Returns true if a complete message has been found. This must be called periodically to 65 | check the radio's buffer for payloads, which are constructed into a message. 66 | 67 | ```c 68 | bool blecast_poll(BLECastMessage *message) 69 | ``` 70 | 71 | 72 | ### Resetting 73 | 74 | To continue using the same key and configured radio, reset will clean up the 75 | payload memory, but keep the internal state for re-using a BLECastMessage. 76 | 77 | ```c 78 | void blecast_reset(BLECastMessage *message) 79 | ``` 80 | 81 | 82 | ### Cleaning up 83 | 84 | There are various data structures stored on the heap. This must be called when a 85 | message is not longer needed to reclaim this memory. 86 | 87 | ```c 88 | void blecast_free(BLECastMessage *message); 89 | ``` 90 | 91 | 92 | Example 93 | ------- 94 | 95 | This example will continuously listen for new messages and display it in the serial monitor. 96 | 97 | Please see the example in examples for pin connections. 98 | 99 | ```c 100 | #include "blecast.h" 101 | 102 | BLECastMessage message; 103 | 104 | uint32_t lastMessageId = BLECAST_INVALID_ID; 105 | 106 | void setup() { 107 | 108 | // Set up the Serial Monitor 109 | Serial.begin(115200); 110 | while (!Serial) { } 111 | 112 | Serial.println("BLECast ready."); 113 | 114 | // The secret key to listen for 115 | uint8_t key[] = { 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 2, 2, 3, 3, 4, 4 }; 116 | 117 | // Initialize the message data structure 118 | blecast_init(&message, key, 9, 10); 119 | } 120 | 121 | void loop() { 122 | 123 | // Poll for a message (returns true if a new message is complete) 124 | if (blecast_poll(&message)) { 125 | 126 | // A different message 127 | if (message.id != lastMessageId) { 128 | 129 | Serial.print("Got Message: "); 130 | 131 | // Print each characater and a new line 132 | for (uint8_t i = 0; i < message.size; i++) { Serial.print((char)(message.data[i])); } 133 | Serial.println("\n"); 134 | 135 | // Remember this message ID 136 | lastMessageId = message.id; 137 | } 138 | 139 | // Reset the message data structure so it is ready for a new message 140 | blecast_reset(&message); 141 | } 142 | } 143 | ``` 144 | 145 | Protocol 146 | -------- 147 | 148 | The data is sent as the UUID of a BLE advertising service packet. This limits a payload to 16 bytes. 149 | 150 | If the message is over 12 bytes, it is prefixed with the CRC-24 checksum of the message. 151 | 152 | The message is chunked into 12 byte blocks, with a 4 byte header. 153 | 154 | Each block: 155 | 156 | - The CRC-24 of the block without this checksum (13 bytes) - 24 bits 157 | - The Termination bit; 1 if this is the last block, 0 otherwise - 1 bit 158 | - The Partial bit; 1 if this block is less than 12 bytes, 0 if it is 12 - 1 bit 159 | - The Index of this block - 6 bits 160 | - The block data - 12 bytes 161 | 162 | The CRC-24 bytes is then extended across each byte, bit-shift by 1 bit for each byte. 163 | 164 | The entire payload is then encrypted using AES-128-ECB. 165 | 166 | 167 | License 168 | ------- 169 | 170 | MIT License. 171 | 172 | Dependency libraries' are licenced under other various libraries. I plan to migrate off these 173 | libraries over time. 174 | -------------------------------------------------------------------------------- /source/libs/firefly_blecast/library.properties: -------------------------------------------------------------------------------- 1 | name=BLECast 2 | version=0.0.1 3 | author=Richard Moore 4 | maintainer=Richard Moore 5 | sentence=A custom BLE broadcast and receive protocol library. 6 | paragraph=A custom BLE broadcast and receive protocol library, for sending chunked encryped data from iOS devices to an Arduino over advertising data. 7 | category=Communication 8 | url=https://github.com/ricmoo/blecast/ 9 | architectures=* 10 | includes=blecast.h 11 | -------------------------------------------------------------------------------- /source/libs/firefly_blecast/src/aes-otfks-decrypt.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2014 Craig McQueen 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * See: https://github.com/cmcqueen/aes-min 25 | */ 26 | 27 | /***************************************************************************** 28 | * aes-otfks-decrypt.c 29 | * 30 | * AES-128 decryption with on-the-fly calculation of key schedule. 31 | ****************************************************************************/ 32 | 33 | #include "aes.h" 34 | 35 | // XOR the specified round key into the AES block. 36 | static inline void aes_add_round_key(uint8_t p_block[AES_BLOCK_SIZE], const uint8_t p_round_key[AES_BLOCK_SIZE]) { 37 | uint_fast8_t i; 38 | 39 | for (i = 0; i < AES_BLOCK_SIZE; ++i) { 40 | p_block[i] ^= p_round_key[i]; 41 | } 42 | } 43 | 44 | /* Hopefully the compiler reduces this to a single rotate instruction. 45 | * However in testing with gcc on x86-64, it didn't happen. But it is target- 46 | * and compiler-specific. 47 | * 48 | * Alternatively for a particular platform: 49 | * - Use an intrinsic 8-bit rotate function provided by the compiler. 50 | * - Use inline assembler. 51 | * 52 | * TODO: Examine code produced on the target platform. 53 | */ 54 | static inline uint8_t aes_rotate_left_uint8(uint8_t a, uint_fast8_t num_bits) 55 | { 56 | return ((a << num_bits) | (a >> (8u - num_bits))); 57 | } 58 | 59 | #define AES_REDUCE_BYTE 0x1Bu 60 | #define AES_2_INVERSE 141u 61 | 62 | #if 0 63 | 64 | /* This is probably the most straight-forward expression of the algorithm. 65 | * This seems more likely to have variable timing, although inspection 66 | * of compiled code would be needed to confirm it. 67 | * It is more likely to have variable timing when no optimisations are 68 | * enabled. */ 69 | static inline uint8_t aes_mul2(uint8_t a) 70 | { 71 | uint8_t result; 72 | 73 | result = a << 1u; 74 | if (a & 0x80u) 75 | result ^= AES_REDUCE_BYTE; 76 | return result; 77 | } 78 | 79 | static inline uint8_t aes_div2(uint8_t a) 80 | { 81 | uint8_t result; 82 | 83 | result = a >> 1u; 84 | if (a & 1u) 85 | result ^= AES_2_INVERSE; 86 | return result; 87 | } 88 | 89 | #elif 0 90 | 91 | /* This hopefully has fixed timing, although inspection 92 | * of compiled code would be needed to confirm it. */ 93 | static inline uint8_t aes_mul2(uint8_t a) 94 | { 95 | static const uint8_t reduce[2] = { 0, AES_REDUCE_BYTE }; 96 | 97 | return (a << 1u) ^ reduce[a >= 0x80u]; 98 | } 99 | 100 | static inline uint8_t aes_div2(uint8_t a) 101 | { 102 | static const uint8_t reduce[2] = { 0, AES_2_INVERSE }; 103 | 104 | return (a >> 1u) ^ reduce[a & 1u]; 105 | } 106 | 107 | #else 108 | 109 | /* This hopefully has fixed timing, although inspection 110 | * of compiled code would be needed to confirm it. */ 111 | static inline uint8_t aes_mul2(uint8_t a) 112 | { 113 | return (a << 1u) ^ ((-(a >= 0x80u)) & AES_REDUCE_BYTE); 114 | } 115 | 116 | static inline uint8_t aes_div2(uint8_t a) 117 | { 118 | return (a >> 1u) ^ ((-(a & 1u)) & AES_2_INVERSE); 119 | } 120 | 121 | #endif 122 | 123 | 124 | uint8_t aes_mul(uint8_t a, uint8_t b) 125 | { 126 | uint8_t result = 0; 127 | uint_fast8_t i; 128 | for (i = 0; i < 8u; i++) 129 | { 130 | #if 0 131 | /* This code variant is less likely to have constant execution time, 132 | * and thus more likely to be vulnerable to timing attacks. */ 133 | if (b & 1) 134 | { 135 | result ^= a; 136 | } 137 | #else 138 | result ^= (-(b & 1u)) & a; 139 | #endif 140 | a = aes_mul2(a); 141 | b >>= 1; 142 | } 143 | return result; 144 | } 145 | 146 | 147 | #define CHAIN_LEN 11u 148 | 149 | /* Calculation of inverse in GF(2^8), by exponentiation to power 254. 150 | * Use minimal addition chain to raise to the power of 254, which requires 151 | * 11 multiplies. 152 | * There are many addition chains of length 11 for 254. This one was picked 153 | * because it has the most multiplies by the previous value, and least 154 | * references to earlier history, which in theory could minimise the size of 155 | * prev_values[]. However, in the end we do the simplest possible 156 | * implementation of the algorithm to minimise code size (because aes_inv() is 157 | * used to achieve smallest possible S-box implementation), so it doesn't 158 | * really matter which addition chain we pick. 159 | */ 160 | uint8_t aes_inv(uint8_t a) 161 | { 162 | static const uint8_t addition_chain_idx[CHAIN_LEN] = { 0, 1, 1, 3, 4, 3, 6, 7, 3, 9, 1 }; 163 | uint_fast8_t i; 164 | uint8_t prev_values[CHAIN_LEN]; 165 | 166 | for (i = 0; i < CHAIN_LEN; i++) 167 | { 168 | prev_values[i] = a; 169 | a = aes_mul(a, prev_values[addition_chain_idx[i]]); 170 | } 171 | return a; 172 | } 173 | 174 | uint8_t aes_sbox(uint8_t a) { 175 | uint8_t x; 176 | 177 | a = aes_inv(a); 178 | 179 | x = aes_rotate_left_uint8(a, 1u); 180 | x ^= aes_rotate_left_uint8(x, 1u); 181 | x ^= aes_rotate_left_uint8(x, 2u); 182 | 183 | return a ^ x ^ 0x63u; 184 | } 185 | 186 | uint8_t aes_sbox_inv(uint8_t a) 187 | { 188 | uint8_t x; 189 | 190 | x = aes_rotate_left_uint8(a, 1u); 191 | a = aes_rotate_left_uint8(x, 2u); 192 | x ^= a; 193 | a = aes_rotate_left_uint8(a, 3u); 194 | 195 | return aes_inv(a ^ x ^ 0x05u); 196 | } 197 | 198 | void aes_sbox_inv_apply_block(uint8_t p_block[AES_BLOCK_SIZE]) 199 | { 200 | uint_fast8_t i; 201 | 202 | for (i = 0; i < AES_BLOCK_SIZE; ++i) 203 | { 204 | p_block[i] = aes_sbox_inv(p_block[i]); 205 | } 206 | } 207 | 208 | 209 | void aes_sbox_apply_block(uint8_t p_block[AES_BLOCK_SIZE]) 210 | { 211 | uint_fast8_t i; 212 | 213 | for (i = 0; i < AES_BLOCK_SIZE; ++i) 214 | { 215 | p_block[i] = aes_sbox(p_block[i]); 216 | } 217 | } 218 | 219 | //#include "aes-key-schedule-round.h" 220 | /* This is used for aes128_otfks_encrypt(), on-the-fly key schedule encryption. 221 | * It is also used by aes128_otfks_decrypt_start_key() to calculate the 222 | * starting key state for decryption with on-the-fly key schedule calculation. 223 | * rcon for the round must be provided, out of the sequence: 224 | * 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 225 | * Subsequent values can be calculated with aes_mul2(). 226 | */ 227 | void aes128_key_schedule_round(uint8_t p_key[AES128_KEY_SIZE], uint8_t rcon) 228 | { 229 | uint_fast8_t round; 230 | uint8_t * p_key_0 = p_key; 231 | uint8_t * p_key_m1 = p_key + AES128_KEY_SIZE - AES_KEY_SCHEDULE_WORD_SIZE; 232 | 233 | /* Rotate previous word and apply S-box. Also XOR Rcon for first byte. */ 234 | p_key_0[0] ^= aes_sbox(p_key_m1[1]) ^ rcon; 235 | p_key_0[1] ^= aes_sbox(p_key_m1[2]); 236 | p_key_0[2] ^= aes_sbox(p_key_m1[3]); 237 | p_key_0[3] ^= aes_sbox(p_key_m1[0]); 238 | 239 | for (round = 1; round < AES128_KEY_SIZE / AES_KEY_SCHEDULE_WORD_SIZE; ++round) 240 | { 241 | p_key_m1 = p_key_0; 242 | p_key_0 += AES_KEY_SCHEDULE_WORD_SIZE; 243 | 244 | /* XOR in previous word */ 245 | p_key_0[0] ^= p_key_m1[0]; 246 | p_key_0[1] ^= p_key_m1[1]; 247 | p_key_0[2] ^= p_key_m1[2]; 248 | p_key_0[3] ^= p_key_m1[3]; 249 | } 250 | } 251 | 252 | //#include "aes-shift-rows.h" 253 | void aes_shift_rows(uint8_t p_block[AES_BLOCK_SIZE]) 254 | { 255 | uint8_t temp_byte; 256 | 257 | /* First row doesn't shift */ 258 | 259 | /* Shift the second row */ 260 | temp_byte = p_block[0 * AES_COLUMN_SIZE + 1u]; 261 | p_block[0 * AES_COLUMN_SIZE + 1u] = p_block[1u * AES_COLUMN_SIZE + 1u]; 262 | p_block[1u * AES_COLUMN_SIZE + 1u] = p_block[2u * AES_COLUMN_SIZE + 1u]; 263 | p_block[2u * AES_COLUMN_SIZE + 1u] = p_block[3u * AES_COLUMN_SIZE + 1u]; 264 | p_block[3u * AES_COLUMN_SIZE + 1u] = temp_byte; 265 | 266 | /* Shift the third row */ 267 | temp_byte = p_block[0 * AES_COLUMN_SIZE + 2u]; 268 | p_block[0 * AES_COLUMN_SIZE + 2u] = p_block[2u * AES_COLUMN_SIZE + 2u]; 269 | p_block[2u * AES_COLUMN_SIZE + 2u] = temp_byte; 270 | temp_byte = p_block[1u * AES_COLUMN_SIZE + 2u]; 271 | p_block[1u * AES_COLUMN_SIZE + 2u] = p_block[3u * AES_COLUMN_SIZE + 2u]; 272 | p_block[3u * AES_COLUMN_SIZE + 2u] = temp_byte; 273 | 274 | /* Shift the fourth row */ 275 | temp_byte = p_block[3u * AES_COLUMN_SIZE + 3u]; 276 | p_block[3u * AES_COLUMN_SIZE + 3u] = p_block[2u * AES_COLUMN_SIZE + 3u]; 277 | p_block[2u * AES_COLUMN_SIZE + 3u] = p_block[1u * AES_COLUMN_SIZE + 3u]; 278 | p_block[1u * AES_COLUMN_SIZE + 3u] = p_block[0 * AES_COLUMN_SIZE + 3u]; 279 | p_block[0 * AES_COLUMN_SIZE + 3u] = temp_byte; 280 | } 281 | 282 | void aes_shift_rows_inv(uint8_t p_block[AES_BLOCK_SIZE]) 283 | { 284 | uint8_t temp_byte; 285 | 286 | /* First row doesn't shift */ 287 | 288 | /* Shift the second row */ 289 | temp_byte = p_block[3u * AES_COLUMN_SIZE + 1u]; 290 | p_block[3u * AES_COLUMN_SIZE + 1u] = p_block[2u * AES_COLUMN_SIZE + 1u]; 291 | p_block[2u * AES_COLUMN_SIZE + 1u] = p_block[1u * AES_COLUMN_SIZE + 1u]; 292 | p_block[1u * AES_COLUMN_SIZE + 1u] = p_block[0 * AES_COLUMN_SIZE + 1u]; 293 | p_block[0 * AES_COLUMN_SIZE + 1u] = temp_byte; 294 | 295 | /* Shift the third row */ 296 | temp_byte = p_block[0 * AES_COLUMN_SIZE + 2u]; 297 | p_block[0 * AES_COLUMN_SIZE + 2u] = p_block[2u * AES_COLUMN_SIZE + 2u]; 298 | p_block[2u * AES_COLUMN_SIZE + 2u] = temp_byte; 299 | temp_byte = p_block[1u * AES_COLUMN_SIZE + 2u]; 300 | p_block[1u * AES_COLUMN_SIZE + 2u] = p_block[3u * AES_COLUMN_SIZE + 2u]; 301 | p_block[3u * AES_COLUMN_SIZE + 2u] = temp_byte; 302 | 303 | /* Shift the fourth row */ 304 | temp_byte = p_block[0 * AES_COLUMN_SIZE + 3u]; 305 | p_block[0 * AES_COLUMN_SIZE + 3u] = p_block[1u * AES_COLUMN_SIZE + 3u]; 306 | p_block[1u * AES_COLUMN_SIZE + 3u] = p_block[2u * AES_COLUMN_SIZE + 3u]; 307 | p_block[2u * AES_COLUMN_SIZE + 3u] = p_block[3u * AES_COLUMN_SIZE + 3u]; 308 | p_block[3u * AES_COLUMN_SIZE + 3u] = temp_byte; 309 | } 310 | 311 | void aes_mix_columns(uint8_t p_block[AES_BLOCK_SIZE]) 312 | { 313 | uint8_t temp_column[AES_COLUMN_SIZE]; 314 | uint_fast8_t i; 315 | uint_fast8_t j; 316 | uint8_t byte_value; 317 | uint8_t byte_value_2; 318 | 319 | for (i = 0; i < AES_NUM_COLUMNS; i++) 320 | { 321 | memset(temp_column, 0, AES_COLUMN_SIZE); 322 | for (j = 0; j < AES_COLUMN_SIZE; j++) 323 | { 324 | byte_value = p_block[i * AES_COLUMN_SIZE + j]; 325 | byte_value_2 = aes_mul2(byte_value); 326 | temp_column[(j + 0 ) % AES_COLUMN_SIZE] ^= byte_value_2; 327 | temp_column[(j + 1u) % AES_COLUMN_SIZE] ^= byte_value; 328 | temp_column[(j + 2u) % AES_COLUMN_SIZE] ^= byte_value; 329 | temp_column[(j + 3u) % AES_COLUMN_SIZE] ^= byte_value ^ byte_value_2; 330 | } 331 | memcpy(&p_block[i * AES_COLUMN_SIZE], temp_column, AES_COLUMN_SIZE); 332 | } 333 | } 334 | 335 | /* 14 = 1110b 336 | * 9 = 1001b 337 | * 13 = 1101b 338 | * 11 = 1011b 339 | */ 340 | void aes_mix_columns_inv(uint8_t p_block[AES_BLOCK_SIZE]) 341 | { 342 | uint8_t temp_column[AES_COLUMN_SIZE]; 343 | uint_fast8_t i; 344 | uint_fast8_t j; 345 | uint8_t byte_value; 346 | uint8_t byte_value_2; 347 | uint8_t byte_value_4; 348 | uint8_t byte_value_8; 349 | 350 | for (i = 0; i < AES_NUM_COLUMNS; i++) 351 | { 352 | memset(temp_column, 0, AES_COLUMN_SIZE); 353 | for (j = 0; j < AES_COLUMN_SIZE; j++) 354 | { 355 | byte_value = p_block[i * AES_COLUMN_SIZE + j]; 356 | byte_value_2 = aes_mul2(byte_value); 357 | byte_value_4 = aes_mul2(byte_value_2); 358 | byte_value_8 = aes_mul2(byte_value_4); 359 | temp_column[(j + 0 ) % AES_COLUMN_SIZE] ^= byte_value_8 ^ byte_value_4 ^ byte_value_2; // 14 = 1110b 360 | temp_column[(j + 1u) % AES_COLUMN_SIZE] ^= byte_value_8 ^ byte_value; // 9 = 1001b 361 | temp_column[(j + 2u) % AES_COLUMN_SIZE] ^= byte_value_8 ^ byte_value_4 ^ byte_value; // 13 = 1101b 362 | temp_column[(j + 3u) % AES_COLUMN_SIZE] ^= byte_value_8 ^ byte_value_2 ^ byte_value; // 11 = 1011b 363 | } 364 | memcpy(&p_block[i * AES_COLUMN_SIZE], temp_column, AES_COLUMN_SIZE); 365 | } 366 | } 367 | 368 | 369 | /***************************************************************************** 370 | * Defines 371 | ****************************************************************************/ 372 | 373 | #define AES_KEY_SCHEDULE_FIRST_RCON 1u 374 | #define AES128_KEY_SCHEDULE_LAST_RCON 54u 375 | 376 | /***************************************************************************** 377 | * Functions 378 | ****************************************************************************/ 379 | 380 | /* This is used for aes128_otfks_decrypt(), on-the-fly key schedule decryption. 381 | * rcon for the round must be provided, out of the sequence: 382 | * 54, 27, 128, 64, 32, 16, 8, 4, 2, 1 383 | * Subsequent values can be calculated with aes_div2(). 384 | */ 385 | static void aes128_key_schedule_inv_round(uint8_t p_key[AES128_KEY_SIZE], uint8_t rcon) 386 | { 387 | uint_fast8_t round; 388 | uint8_t * p_key_0 = p_key + AES128_KEY_SIZE - AES_KEY_SCHEDULE_WORD_SIZE; 389 | uint8_t * p_key_m1 = p_key_0 - AES_KEY_SCHEDULE_WORD_SIZE; 390 | 391 | for (round = 1; round < AES128_KEY_SIZE / AES_KEY_SCHEDULE_WORD_SIZE; ++round) 392 | { 393 | /* XOR in previous word */ 394 | p_key_0[0] ^= p_key_m1[0]; 395 | p_key_0[1] ^= p_key_m1[1]; 396 | p_key_0[2] ^= p_key_m1[2]; 397 | p_key_0[3] ^= p_key_m1[3]; 398 | 399 | p_key_0 = p_key_m1; 400 | p_key_m1 -= AES_KEY_SCHEDULE_WORD_SIZE; 401 | } 402 | 403 | /* Rotate previous word and apply S-box. Also XOR Rcon for first byte. */ 404 | p_key_m1 = p_key + AES128_KEY_SIZE - AES_KEY_SCHEDULE_WORD_SIZE; 405 | p_key_0[0] ^= aes_sbox(p_key_m1[1]) ^ rcon; 406 | p_key_0[1] ^= aes_sbox(p_key_m1[2]); 407 | p_key_0[2] ^= aes_sbox(p_key_m1[3]); 408 | p_key_0[3] ^= aes_sbox(p_key_m1[0]); 409 | } 410 | 411 | /* Calculate the starting key state needed for decryption with on-the-fly key 412 | * schedule calculation. The starting decryption key state is the last 16 bytes 413 | * of the AES-128 key schedule. 414 | * The decryption start key calculation is done in-place in the buffer p_key[]. 415 | * So p_key points to a 16-byte buffer containing the AES-128 key. On exit, it 416 | * contains the decryption start key state suitable for aes128_otfks_decrypt(). 417 | */ 418 | void aes128_otfks_decrypt_start_key(uint8_t p_key[AES128_KEY_SIZE]) 419 | { 420 | uint_fast8_t round; 421 | uint8_t rcon = AES_KEY_SCHEDULE_FIRST_RCON; 422 | 423 | for (round = 0; round < AES128_NUM_ROUNDS; ++round) 424 | { 425 | aes128_key_schedule_round(p_key, rcon); 426 | 427 | /* Next rcon */ 428 | rcon = aes_mul2(rcon); 429 | } 430 | } 431 | 432 | /* AES-128 decryption with on-the-fly key schedule calculation. 433 | * 434 | * p_block points to a 16-byte buffer of encrypted data to decrypt. Decryption 435 | * is done in-place in that buffer. 436 | * p_key must initially point to a starting key state for decryption, which is 437 | * the last 16 bytes of the AES-128 key schedule. It can be calculated from the 438 | * AES-128 16-byte key by aes128_otfks_decrypt_start_key(). Key schedule is 439 | * calculated on-the-fly in that buffer, so the buffer must re-initialised for 440 | * subsequent decryption operations. 441 | */ 442 | void aes128_otfks_decrypt(uint8_t p_block[AES_BLOCK_SIZE], uint8_t p_key[AES128_KEY_SIZE]) 443 | { 444 | uint_fast8_t round; 445 | uint8_t rcon = AES128_KEY_SCHEDULE_LAST_RCON; 446 | 447 | aes_add_round_key(p_block, p_key); 448 | aes_shift_rows_inv(p_block); 449 | aes_sbox_inv_apply_block(p_block); 450 | for (round = AES128_NUM_ROUNDS - 1u; round >= 1; --round) 451 | { 452 | aes128_key_schedule_inv_round(p_key, rcon); 453 | aes_add_round_key(p_block, p_key); 454 | aes_mix_columns_inv(p_block); 455 | aes_shift_rows_inv(p_block); 456 | aes_sbox_inv_apply_block(p_block); 457 | 458 | /* Previous rcon */ 459 | rcon = aes_div2(rcon); 460 | } 461 | aes128_key_schedule_inv_round(p_key, rcon); 462 | aes_add_round_key(p_block, p_key); 463 | } 464 | -------------------------------------------------------------------------------- /source/libs/firefly_blecast/src/aes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2014 Craig McQueen 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | * See: https://github.com/cmcqueen/aes-min 25 | */ 26 | 27 | /***************************************************************************** 28 | * aes.h 29 | * 30 | * Minimal byte-oriented AES-128 encryption/decryption implementation suitable 31 | * for small microprocessors. 32 | ****************************************************************************/ 33 | 34 | #ifndef AES_H 35 | #define AES_H 36 | 37 | /***************************************************************************** 38 | * Includes 39 | ****************************************************************************/ 40 | 41 | #include 42 | 43 | /***************************************************************************** 44 | * Defines 45 | ****************************************************************************/ 46 | 47 | #define AES_BLOCK_SIZE 16u 48 | #define AES_COLUMN_SIZE 4u 49 | #define AES_NUM_COLUMNS 4u 50 | 51 | #define AES_KEY_SCHEDULE_WORD_SIZE 4u 52 | 53 | #define AES128_NUM_ROUNDS 10u 54 | #define AES128_KEY_SIZE 16u 55 | #define AES128_KEY_SCHEDULE_SIZE (AES_BLOCK_SIZE * (AES128_NUM_ROUNDS + 1u)) 56 | 57 | /***************************************************************************** 58 | * Function prototypes 59 | ****************************************************************************/ 60 | 61 | #ifdef __cplusplus 62 | extern "C"{ 63 | #endif /* __cplusplus */ 64 | 65 | //void aes128_encrypt(uint8_t p_block[AES_BLOCK_SIZE], const uint8_t p_key_schedule[AES128_KEY_SCHEDULE_SIZE]); 66 | //void aes128_decrypt(uint8_t p_block[AES_BLOCK_SIZE], const uint8_t p_key_schedule[AES128_KEY_SCHEDULE_SIZE]); 67 | 68 | //void aes128_key_schedule(uint8_t p_key_schedule[AES128_KEY_SCHEDULE_SIZE], const uint8_t p_key[AES128_KEY_SIZE]); 69 | 70 | void aes128_otfks_encrypt(uint8_t p_block[AES_BLOCK_SIZE], uint8_t p_key[AES128_KEY_SIZE]); 71 | void aes128_otfks_decrypt(uint8_t p_block[AES_BLOCK_SIZE], uint8_t p_decrypt_start_key[AES128_KEY_SIZE]); 72 | 73 | void aes128_otfks_decrypt_start_key(uint8_t p_key[AES128_KEY_SIZE]); 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif /* __cplusplus */ 78 | 79 | 80 | #endif /* !defined(AES_H) */ 81 | 82 | -------------------------------------------------------------------------------- /source/libs/firefly_blecast/src/firefly_blecast.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Richard Moore 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // nRF24L01 26 | // See: http://www.nordicsemi.com/eng/nordic/download_resource/8041/1/46023864/2730 27 | 28 | // SPI 29 | // See: http://www.avrbeginners.net/architecture/spi/spi.html 30 | 31 | 32 | #include "firefly_blecast.h" 33 | 34 | #include "aes.h" 35 | 36 | 37 | // We use this to pack enums into uint8_t 38 | // https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes 39 | // https://stackoverflow.com/questions/2791739/c-packing-a-typedef-enum 40 | #ifdef __GNUC__ 41 | #define attribute(x) __attribute__((x)); 42 | #else 43 | #define attribute(x) 44 | #endif 45 | 46 | 47 | // We use this to make sure the packing worked 48 | // https://www.pixelbeat.org/programming/gcc/static_assert.html 49 | #define ASSERT_CONCAT_(a, b) a##b 50 | #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) 51 | /* These can't be used after statements in c89. */ 52 | #ifdef __COUNTER__ 53 | #define STATIC_ASSERT(e,m) \ 54 | ;enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(int)(!!(e)) } 55 | #else 56 | /* This can't be used twice on the same line so ensure if using in headers 57 | * that the headers are not included twice (by wrapping in #ifndef...#endif) 58 | * Note it doesn't cause an issue when used on same line of separate modules 59 | * compiled with gcc -combine -fwhole-program. */ 60 | #define STATIC_ASSERT(e,m) \ 61 | ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!!(e)) } 62 | #endif 63 | 64 | 65 | enum RadioRegister { 66 | RadioRegisterConfig = 0x00, 67 | RadioRegisterAutoAck = 0x01, 68 | RadioRegisterEnabledReceivePipes = 0x02, 69 | RadioRegisterAddressWidth = 0x03, 70 | RadioRegisterRetries = 0x04, 71 | RadioRegisterChannel = 0x05, 72 | RadioRegisterRadioConfig = 0x06, 73 | RadioRegisterStatus = 0x07, 74 | 75 | // Receive Pipes 76 | RadioRegisterReceiveAddressPipe0 = 0x0a, 77 | RadioRegisterReceiveAddressPipe1 = 0x0b, 78 | RadioRegisterReceivePayloadWidthPipe0 = 0x11, 79 | RadioRegisterReceivePayloadWidthPipe1 = 0x12, 80 | 81 | RadioRegisterFIFOStatus = 0x17, 82 | } attribute(packed); 83 | typedef enum RadioRegister RadioRegister; 84 | 85 | 86 | enum RadioCommand { 87 | RadioCommandReadRegister = 0x00, 88 | RadioCommandWriteRegister = 0x20, 89 | RadioCommandReadPayload = 0x61, 90 | RadioCommandFlushReceive = 0xe2, 91 | } attribute(packed); 92 | typedef enum RadioCommand RadioCommand; 93 | 94 | 95 | enum SPIControl { 96 | SPIControlEnableInterrupt = (1 << 7), 97 | SPIControlEnabled = (1 << 6), 98 | SPIControlOrderLSBFirst = (1 << 5), 99 | SPIControlMaster = (1 << 4), 100 | 101 | SPIControlClockPolarityIdleHigh = (1 << 3), 102 | SPIControlAlphaSampleTrailing = (1 << 2), 103 | 104 | // Modes (combines Clock Polarity and Clock Alpha) 105 | SPIControlMode0 = (0 << 2), 106 | SPIControlMode1 = (1 << 2), 107 | SPIControlMode2 = (2 << 2), 108 | SPIControlMode3 = (3 << 2), 109 | 110 | // Clock Rate Select 111 | SPIControlClockRateFosc4 = (0 << 0), 112 | SPIControlClockRateFosc16 = (1 << 0), 113 | SPIControlClockRateFosc64 = (2 << 0), 114 | SPIControlClockRateFosc128 = (3 << 0), 115 | } attribute(packed); 116 | typedef enum SPIControl SPIControl; 117 | 118 | 119 | enum SPIStatus { 120 | SPIStatusInterruptFlag = (1 << 7), 121 | SPIStatusWriteCollision = (1 << 6), 122 | SPIStatusDoubleSpeed = (1 << 0), 123 | } attribute(packed); 124 | typedef enum SPIStatus SPIStatus; 125 | 126 | STATIC_ASSERT( sizeof ( RadioRegister ) == 1, "RadioRegister is incorrect width"); 127 | STATIC_ASSERT( sizeof ( RadioCommand ) == 1, "RadioCommand is incorrect width"); 128 | STATIC_ASSERT( sizeof ( SPIControl ) == 1, "SPIControl is incorrect width"); 129 | STATIC_ASSERT( sizeof ( SPIStatus ) == 1, "SPIStatus is incorrect width"); 130 | 131 | 132 | const uint8_t RadioRegisterMask = 0x1f; 133 | 134 | 135 | // The size required to read a BLECast packet (1 byte PDU type and 16 bytes address) 136 | #define BLECAST_PACKET_SIZE (1 + 16) 137 | 138 | // The size of a BLE packet 139 | #define BLE_PACKET_SIZE (32) 140 | 141 | 142 | #define NEXT_CHANNEL (0x7f) 143 | 144 | #define RADIO_SPEED (10000000) 145 | 146 | 147 | 148 | /** 149 | * Cyclic Redundancy Check - 24 bit (CRC24) 150 | * 151 | * See: http://sunsite.icm.edu.pl/gnupg/rfc2440-6.html 152 | */ 153 | 154 | #define CRC24_INIT 0xb704ce 155 | #define CRC24_POLY 0x1864cfb 156 | 157 | static uint32_t computeCrc24(uint8_t *data, uint16_t length) { 158 | uint32_t crc = CRC24_INIT; 159 | 160 | for (uint16_t i = 0; i < length; i++) { 161 | crc ^= ((uint32_t)data[i]) << 16; 162 | 163 | for (uint8_t j = 0; j < 8; j++) { 164 | crc <<= 1; 165 | if (crc & 0x1000000) { 166 | crc ^= CRC24_POLY; 167 | } 168 | } 169 | } 170 | 171 | return crc & 0xffffff; 172 | } 173 | 174 | // Reverse the bits in a byte (BLE bytes are backward) 175 | static uint8_t reverse(uint8_t v) { 176 | 177 | uint8_t result = 0; 178 | 179 | if (v & (1 << 0)) { result |= (1 << 7); } 180 | if (v & (1 << 1)) { result |= (1 << 6); } 181 | if (v & (1 << 2)) { result |= (1 << 5); } 182 | if (v & (1 << 3)) { result |= (1 << 4); } 183 | if (v & (1 << 4)) { result |= (1 << 3); } 184 | if (v & (1 << 5)) { result |= (1 << 2); } 185 | if (v & (1 << 6)) { result |= (1 << 1); } 186 | if (v & (1 << 7)) { result |= (1 << 0); } 187 | 188 | return result; 189 | } 190 | 191 | // Returns ((clockRateFosc << 1) | SPIStatusDoubleSpeed) 192 | static uint8_t spi_getClockRate(uint32_t clock) { 193 | if (clock > F_CPU / 2) { 194 | return (SPIControlClockRateFosc4 << 1) | SPIStatusDoubleSpeed; 195 | } else if (clock > F_CPU / 4) { 196 | return (SPIControlClockRateFosc4 << 1); 197 | } else if (clock > F_CPU / 8) { 198 | return (SPIControlClockRateFosc16 << 1) | SPIStatusDoubleSpeed; 199 | } else if (clock > F_CPU / 16) { 200 | return (SPIControlClockRateFosc16 << 1); 201 | } else if (clock > F_CPU / 32) { 202 | return (SPIControlClockRateFosc64 << 1) | SPIStatusDoubleSpeed; 203 | } else if (clock > F_CPU / 64) { 204 | return (SPIControlClockRateFosc64 << 1); 205 | } 206 | return (SPIControlClockRateFosc128 << 1); 207 | } 208 | 209 | 210 | static void spi_init(BLECastMessage *message) { 211 | 212 | // Slave Select should be set as an output to prevent slave mode 213 | // See: https://www.arduino.cc/en/Reference/SPI 214 | pinMode(SS, OUTPUT); 215 | digitalWrite(SS, HIGH); 216 | 217 | // Get the clock rate for the SPI Control Register 218 | uint8_t spcr = spi_getClockRate(RADIO_SPEED); 219 | 220 | // Set the Double Speed in the SPI Status Register 221 | uint8_t spsr = spcr & SPIStatusDoubleSpeed; 222 | 223 | // Strip off the SPIStatusDoubleSpeed 224 | spcr >>= 1; 225 | spcr |= SPIControlEnabled | SPIControlMaster | SPIControlMode0; 226 | 227 | // Set the SPI Control Register (enabled, master, mode 0, CLOCK_RATE) 228 | SPCR = spcr; 229 | 230 | // Set the SPI Status Register (possibly SPI double speed) 231 | SPSR = spsr; 232 | 233 | // SPI Clock and SPI Master-Out-Slave-In should be outputs 234 | pinMode(SCK, OUTPUT); 235 | pinMode(MOSI, OUTPUT); 236 | } 237 | 238 | static uint8_t spi_transfer(uint8_t data) { 239 | SPDR = data; 240 | //asm volatile("nop"); // @TODO: The official SPI lib does this; do some benchmarks 241 | while (!(SPSR & SPIStatusInterruptFlag)); 242 | return SPDR; 243 | } 244 | 245 | static void spi_shutdown() { 246 | SPCR &= ~SPIControlEnabled; 247 | } 248 | 249 | static void radio_ce(BLECastMessage *message, uint8_t enable) { 250 | digitalWrite(message->radioPinCE, enable ? HIGH: LOW); 251 | } 252 | 253 | static void radio_csn(BLECastMessage *message, uint8_t enable) { 254 | digitalWrite(message->radioPinCSN, enable ? HIGH: LOW); 255 | delayMicroseconds(5); 256 | } 257 | 258 | static void radio_beginTransaction(BLECastMessage *message) { 259 | radio_csn(message, 0); 260 | } 261 | 262 | static void radio_endTransaction(BLECastMessage *message) { 263 | radio_csn(message, 1); 264 | } 265 | 266 | static void radio_writeRegister(BLECastMessage *message, RadioRegister reg, const uint8_t *buffer, uint8_t lengthOrValue) { 267 | radio_beginTransaction(message); 268 | 269 | uint8_t status = spi_transfer(RadioCommandWriteRegister | (RadioRegisterMask & reg)); 270 | 271 | if (buffer == NULL) { 272 | spi_transfer(lengthOrValue); 273 | } else { 274 | while(lengthOrValue--) { 275 | spi_transfer(*buffer++); 276 | } 277 | } 278 | 279 | radio_endTransaction(message); 280 | } 281 | 282 | static uint8_t radio_readRegister(BLECastMessage *message, RadioRegister reg, uint8_t *buffer, uint8_t length) { 283 | radio_beginTransaction(message); 284 | 285 | uint8_t status = spi_transfer(RadioCommandReadRegister | (RadioRegisterMask & reg)); 286 | 287 | if (buffer == NULL) { 288 | status = spi_transfer(0xff); 289 | } else { 290 | while(length--) { 291 | *buffer++ = spi_transfer(0xff); 292 | } 293 | } 294 | 295 | radio_endTransaction(message); 296 | 297 | return status; 298 | } 299 | 300 | static uint8_t radio_getChannel(BLECastMessage *message) { 301 | switch (radio_readRegister(message, RadioRegisterChannel, NULL, 0)) { 302 | case 26: return 1; 303 | case 80: return 2; 304 | default: break; 305 | } 306 | 307 | return 0; 308 | } 309 | 310 | static void radio_setChannel(BLECastMessage *message, uint8_t channel) { 311 | 312 | if (channel == NEXT_CHANNEL) { 313 | channel = radio_getChannel(message) + 1; //getFrequencyIndex(radio_getChannel(message)) + 1; 314 | } 315 | 316 | if (channel > 2) { channel = 0; } 317 | 318 | // Advertising frequencies (the nRF2401 adds 2400 to these values) 319 | //static const uint8_t frequencies[] = { 2, 26, 80 }; 320 | //uin8_t frequency = 2 + channel * 24 + (channel & 0x02) * 15; 321 | uint8_t frequency = 2; 322 | switch(channel) { 323 | case 2: frequency += 54; // Falls through! 324 | case 1: frequency += 24; 325 | } 326 | 327 | radio_writeRegister(message, RadioRegisterChannel, NULL, frequency); 328 | } 329 | 330 | static void radio_init(BLECastMessage *message) { 331 | 332 | pinMode(message->radioPinCE, OUTPUT); 333 | pinMode(message->radioPinCSN, OUTPUT); 334 | 335 | spi_init(message); 336 | 337 | radio_ce(message, 0); 338 | radio_csn(message, 1); 339 | 340 | delay(5); 341 | 342 | // CONFIG 343 | // Configuration Register 344 | radio_writeRegister(message, RadioRegisterConfig, NULL, 0x00); 345 | 346 | // EN_AA 347 | // Enable "Auto Acknowledgement" (disable for nRF24L01 compatibility) 348 | radio_writeRegister(message, RadioRegisterAutoAck, NULL, 0x00); 349 | 350 | // SETUP_RETR 351 | // Setup of Automatic Retransmission 352 | radio_writeRegister(message, RadioRegisterRetries, NULL, 0x00); 353 | 354 | // RF_SETUP 355 | // [3] => 0b = 1Mbps, 1b = 2Mbps 356 | // [0] => Low Noise Amplifier (@TODO: should we use this to save power?) 357 | radio_writeRegister(message, RadioRegisterRadioConfig, NULL, 0x00); 358 | 359 | // Reset the RX buffer 360 | radio_writeRegister(message, RadioRegisterStatus, NULL, 0x70); 361 | 362 | // Set the channel 363 | radio_setChannel(message, 0); 364 | 365 | // Flush the RX buffer 366 | radio_beginTransaction(message); 367 | spi_transfer(RadioCommandFlushReceive); 368 | radio_endTransaction(message); 369 | 370 | // CONFIG 371 | // Configuration Register 372 | // 0x02 => Power up 373 | // 0x01 => Receive Mode 374 | radio_writeRegister(message, RadioRegisterConfig, NULL, 0x03); 375 | 376 | // Let things settle 377 | delay(5); 378 | 379 | // SETUP_AW 380 | // Setup of Address Widths (common for all data pipes) 381 | // 0x01 => 3 bytes 382 | // 0x02 => 4 bytes 383 | // 0x03 => 5 bytes 384 | radio_writeRegister(message, RadioRegisterAddressWidth, NULL, 0x02); 385 | 386 | // Access Address; see Core Specification B.2.1.2 (reverseBits(0x8E89BED6)) 387 | 388 | // RX_ADDR_P1 389 | // Receive address data pipe 1 (LSB first) 390 | uint8_t address[] = { 0x71, 0x91, 0x7D, 0x6b }; 391 | radio_writeRegister(message, RadioRegisterReceiveAddressPipe1, address, 4); 392 | radio_writeRegister(message, RadioRegisterReceivePayloadWidthPipe1, NULL, BLE_PACKET_SIZE); 393 | 394 | // EN_RXADDR 395 | // Enabled RX Addresses 396 | // 0x01 => Pipe 0 397 | // 0x02 => Pipe 1 398 | radio_writeRegister(message, RadioRegisterEnabledReceivePipes, NULL, 0x02); 399 | 400 | delay(5); 401 | } 402 | 403 | static void radio_shutdown(BLECastMessage *message) { 404 | radio_ce(message, 0); 405 | radio_writeRegister(message, RadioRegisterConfig, NULL, 0x01); 406 | spi_shutdown(); 407 | } 408 | 409 | static void radio_startListening(BLECastMessage *message) { 410 | radio_writeRegister(message, RadioRegisterConfig, NULL, 0x03); /// @TODO: needed? 411 | 412 | // STATUS 413 | // Status Register 414 | // 0x40 => Receive Data Ready (write 1 to clear) 415 | // 0x20 => Sent Data Complete (write 1 to clear) // @TODO: needed? 416 | // 0x10 => Max retransmits (write 1 to clear; must clear to continue) // @TODO: needed? 417 | radio_writeRegister(message, RadioRegisterStatus, NULL, 0x70); 418 | radio_ce(message, 1); 419 | 420 | delay(10); 421 | } 422 | 423 | static void radio_stopListening(BLECastMessage *message) { 424 | radio_ce(message, 0); 425 | delay(5); 426 | radio_writeRegister(message, RadioRegisterConfig, NULL, 0x0); 427 | } 428 | 429 | static uint8_t radio_available(BLECastMessage *message) { 430 | // FIFO_STATUS 431 | // FIFO Status Register 432 | // 0x02 => RX full 433 | // 0x01 => RX empty 434 | return !(radio_readRegister(message, RadioRegisterFIFOStatus, NULL, 0) & 0x01); 435 | } 436 | 437 | // Reads 17 bytes; 1 byte PDU type and the 16 byte BLE address (where BLECast transmits data) 438 | static void radio_read_packet(BLECastMessage *message, uint8_t *buffer) { 439 | radio_beginTransaction(message); 440 | 441 | uint8_t status = spi_transfer(RadioCommandReadPayload); 442 | 443 | // Read the first byte (for the BLE PDU_TYPE) 444 | *buffer++ = spi_transfer(0xff); 445 | 446 | // Skip 12 bytes (for BLECast stuff we do not need this BLE metadata) 447 | for (uint8_t i = 0; i < 12; i++) { 448 | spi_transfer(0xff); 449 | } 450 | 451 | // The 16 bytes of BLECast data (BLE address) 452 | for (uint8_t i = 0; i < 16; i++) { 453 | *buffer++ = spi_transfer(0xff); 454 | } 455 | 456 | // Finish off the 32 byte of BLE_PACKET_SIZE (3 bytes remain) 457 | for (uint8_t i = 0; i < 3; i++) { 458 | spi_transfer(0xff); 459 | } 460 | 461 | radio_endTransaction(message); 462 | 463 | // Clear the data so we can get more 464 | radio_writeRegister(message, RadioRegisterStatus, NULL, 0x70); 465 | } 466 | 467 | 468 | static void _blecast_init(BLECastMessage *message) { 469 | message->totalPayloadCount = -1; 470 | message->discoveredPayloadCount = 0; 471 | message->size = -1; 472 | message->id = BLECAST_INVALID_ID; 473 | memset(message->data, 0, message->maxSize); 474 | } 475 | 476 | 477 | bool blecast_init(BLECastMessage *message, uint8_t *key, uint8_t *data, uint16_t dataLength) { 478 | message->data = data; 479 | message->maxSize = dataLength; 480 | 481 | _blecast_init(message); 482 | 483 | memcpy(message->aesKey, key, 16); 484 | 485 | radio_init(message); 486 | } 487 | 488 | void blecast_shutdown(BLECastMessage *message) { 489 | radio_shutdown(message); 490 | } 491 | 492 | void blecast_reset(BLECastMessage *message) { 493 | _blecast_init(message); 494 | } 495 | 496 | 497 | void blecast_dump(BLECastMessage *message) { 498 | /* 499 | Serial.print("Message count="); 500 | Serial.print(message->totalPayloadCount); 501 | Serial.print(", discovered="); 502 | Serial.print(message->discoveredPayloadCount); 503 | Serial.print(", size="); 504 | Serial.print(message->size); 505 | Serial.print("\n"); 506 | for (uint8_t i = 0; i < (message->maxSize / 13); i++) { 507 | uint8_t *data = &(message->data[i * 13]); 508 | if (data[0] == 0) { continue; } 509 | Serial.print(" Payload "); 510 | Serial.print(i); 511 | Serial.print(": "); 512 | for (uint8_t i = 1; i < 13; i++) { 513 | if (data[i] < 0x10) { Serial.print("0"); } 514 | Serial.print((uint8_t)(data[i]), HEX); Serial.print(" "); 515 | } 516 | Serial.println(""); 517 | } 518 | */ 519 | } 520 | 521 | 522 | 523 | bool blecast_addPayload(BLECastMessage *message, uint8_t *data) { 524 | // Already done 525 | if (message->size >= 0) { return false; } 526 | 527 | // Decrypt the payload 528 | aes128_otfks_decrypt_start_key(message->aesKey); 529 | aes128_otfks_decrypt(data, message->aesKey); 530 | 531 | // This is the CRC to match 532 | uint32_t payloadCrc = ((uint32_t)data[0] << 16) | ((uint32_t)data[1] << 8) | data[2]; 533 | 534 | // De-noise the payload data 535 | for (uint32_t i = 3; i < 16; i++) { 536 | data[i] ^= (payloadCrc >> (uint32_t)(i - 3)); 537 | } 538 | 539 | // Compute the CRC (while removing the noise applied during shrink-wrapping) 540 | uint32_t computedPayloadCrc = computeCrc24(&data[3], 13); 541 | 542 | // Check the CRC (either not for us of data transmission error) 543 | if (payloadCrc != computedPayloadCrc) { return false; } 544 | 545 | // Done with the CRC; strip it 546 | data += 3; 547 | 548 | // The index byte; [terminal1] [partial1] [index6] 549 | uint8_t index = data[0]; 550 | data++; 551 | 552 | // This message is too big! Reset and hope things are better in the future 553 | uint8_t blockIndex = (index & 0x3f); 554 | if (blockIndex * 13 > message->maxSize) { 555 | _blecast_init(message); 556 | return false; 557 | } 558 | 559 | // Already have this block 560 | if (message->data[blockIndex * 13]) { 561 | return false; 562 | } 563 | 564 | // Add the data (mark it as found and keep the partial and final bits) 565 | message->data[blockIndex * 13] = index | 0x01; 566 | memcpy(&message->data[blockIndex * 13 + 1], data, 12); 567 | 568 | message->discoveredPayloadCount++; 569 | 570 | // Last payload for this message 571 | if ((index & 0x80) && message->totalPayloadCount == -1) { 572 | message->totalPayloadCount = blockIndex + 1; 573 | } 574 | 575 | // This can happen when switching between messages; stray payloads 576 | // got picked up from a previous message without the terminal. 577 | if (message->totalPayloadCount != -1 && message->totalPayloadCount < message->discoveredPayloadCount) { 578 | _blecast_init(message); 579 | return false; 580 | } 581 | 582 | 583 | // Message complete! 584 | if (message->totalPayloadCount == message->discoveredPayloadCount) { 585 | 586 | // Conpute size 587 | 588 | // A partial payload (the length is in the last payload content slot) 589 | uint8_t lastIndex = 13 * (message->totalPayloadCount - 1); 590 | if (message->data[lastIndex] & 0x40) { 591 | message->size = (message->totalPayloadCount - 1) * 12 + message->data[lastIndex + 12]; 592 | 593 | } else { 594 | message->size = message->totalPayloadCount * 12; 595 | } 596 | 597 | // If the message is more than 1 block, if contains an additional message CRC prefix 598 | //uint8_t *outputPtr = output; 599 | if (message->size > 12) { 600 | 601 | uint32_t messageCrc = ((uint32_t)(message->data[1]) << 16) | ((uint32_t)(message->data[2]) << 8) | (message->data[3]); 602 | 603 | for (uint8_t i = 0; i < message->totalPayloadCount; i++) { 604 | uint8_t targetOffset = i * 12, sourceOffset = 1 + i * 13; 605 | for (uint8_t j = 0; j < 12; j++) { 606 | message->data[targetOffset + j] = message->data[sourceOffset + j]; 607 | } 608 | } 609 | 610 | message->size -= 3; 611 | 612 | for (uint8_t i = 0; i < message->size; i++) { 613 | message->data[i] = message->data[i + 3]; 614 | } 615 | 616 | uint32_t computedMessageCrc = computeCrc24(message->data, message->size); 617 | 618 | if (computedMessageCrc != messageCrc) { 619 | _blecast_init(message); 620 | return false; 621 | } 622 | 623 | message->id = messageCrc; 624 | 625 | } else { 626 | for (uint8_t i = 0; i < 12; i++) { 627 | message->data[i] = message->data[i + 1]; 628 | } 629 | 630 | message->id = payloadCrc; 631 | } 632 | 633 | blecast_dump(message); 634 | 635 | return true; 636 | } 637 | 638 | return false; 639 | } 640 | 641 | // Pre-computed whiten mask for each channel (byte[0] and byte[13:13 + 16] 642 | const uint8_t whitenMask[] PROGMEM = { 643 | //const uint8_t whitenMask[] = { 644 | // Channel 37 645 | 0x8d, 0x77, 0xf8, 0xe3, 0x46, 0xe9, 0xab, 0xd0, 0x9e, 646 | 0x53, 0x33, 0xd8, 0xba, 0x98, 0x08, 0x24, 0xcb, 647 | 648 | // Channel 38 649 | 0xd6, 0x4e, 0xcd, 0x60, 0xeb, 0x62, 0x22, 0x90, 0x2c, 650 | 0xef, 0xf0, 0xc7, 0x8d, 0xd2, 0x57, 0xa1, 0x3d, 651 | 652 | // Channel 39 653 | 0x1f, 0x59, 0xde, 0xe1, 0x8f, 0x1b, 0xa5, 0xaf, 0x42, 654 | 0x7b, 0x4e, 0xcd, 0x60, 0xeb, 0x62, 0x22, 0x90 655 | }; 656 | 657 | 658 | bool blecast_poll(BLECastMessage *message) { 659 | 660 | // Already done this message 661 | if (message->size >= 0) { return false; } 662 | 663 | bool success = false; 664 | 665 | radio_startListening(message); 666 | 667 | uint8_t buffer[BLECAST_PACKET_SIZE]; 668 | 669 | while (radio_available(message)) { 670 | radio_read_packet(message, buffer); 671 | 672 | uint8_t *data = buffer; 673 | //uint8_t whitenIndex = whitenMask + getFrequencyIndex(radio_getChannel(message) - 37) * 17; 674 | uint8_t whitenIndex = whitenMask + radio_getChannel(message) * 17; 675 | //uint8_t *whiten = whitenMask[getFrequencyIndex(message->radio->getChannel() - 37) * 17]; 676 | 677 | uint8_t b = *data; 678 | // @TODO: Pre-compute this and just do a comparison instead of the below 0x40 679 | b = reverse(b); 680 | b ^= pgm_read_byte(whitenIndex++); 681 | //b ^= *(whiten++); 682 | 683 | // PDU type must be ADV_NONCONN_IND (E.7.7.65.13) 684 | if (b != 0x40) { continue; } 685 | 686 | data += 1; 687 | for (uint8_t index = 0; index < 16; index++) { 688 | uint8_t b = *data; 689 | 690 | // Reverse the bits 691 | // https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits 692 | b = reverse(b); 693 | 694 | // De-whiten 695 | b ^= pgm_read_byte(whitenIndex++); 696 | //b ^= *(whiten++); 697 | 698 | *(data++) = b; 699 | } 700 | 701 | bool complete = blecast_addPayload(message, &buffer[1]); 702 | if (complete) { success = true; } 703 | } 704 | 705 | radio_stopListening(message); 706 | 707 | radio_setChannel(message, NEXT_CHANNEL); 708 | 709 | return success; 710 | } 711 | 712 | -------------------------------------------------------------------------------- /source/libs/firefly_blecast/src/firefly_blecast.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2018 Richard Moore 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _FIREFLY_BLECAST_H_ 26 | #define _FIREFLY_BLECAST_H_ 27 | 28 | 29 | #include 30 | 31 | #include 32 | 33 | 34 | #define BLECAST_INVALID_ID 0x7f000000 35 | 36 | #define BLECAST_MINIMUM_BUFFER 96 37 | 38 | typedef struct BLECastMessage { 39 | // Total payload counts and unique discovered payload counts 40 | int8_t discoveredPayloadCount; 41 | int8_t totalPayloadCount; 42 | 43 | // Total number of bytes of the message (-1 if the message is incomplete) 44 | int16_t size; 45 | 46 | // This is used to store the message as payloads arrive 47 | uint8_t *data; 48 | uint16_t maxSize; 49 | 50 | // An ID for this message (BLECAST_INVALID_ID until message is valid) 51 | uint32_t id; 52 | 53 | // The AES context initialized with the key 54 | uint8_t aesKey[16]; 55 | 56 | // Radio State 57 | uint8_t radioPinCE; 58 | uint8_t radioPinCSN; 59 | } BLECastMessage; 60 | 61 | 62 | #ifdef __cplusplus 63 | extern "C"{ 64 | #endif /* __cplusplus */ 65 | 66 | 67 | bool blecast_init(BLECastMessage *message, uint8_t *key, uint8_t *data, uint16_t dataLength); 68 | 69 | bool blecast_poll(BLECastMessage *message); 70 | 71 | void blecast_reset(BLECastMessage *message); 72 | 73 | //void blecast_free(BLECastMessage *message); 74 | void blecast_shutdown(BLECastMessage *message); 75 | 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif /* __cplusplus */ 80 | 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /source/libs/firefly_qrcode/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Richard Moore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /source/libs/firefly_qrcode/keywords.txt: -------------------------------------------------------------------------------- 1 | 2 | # Datatypes (KEYWORD1) 3 | 4 | bool KEYWORD1 5 | uint8_t KEYWORD1 6 | QRCode KEYWORD1 7 | 8 | 9 | # Methods and Functions (KEYWORD2) 10 | 11 | qrcode_getBufferSize KEYWORD2 12 | qrcode_initText KEYWORD2 13 | qrcode_initBytes KEYWORD2 14 | qrcode_getModule KEYWORD2 15 | 16 | 17 | # Instances (KEYWORD2) 18 | 19 | 20 | # Constants (LITERAL1) 21 | 22 | false LITERAL1 23 | true LITERAL1 24 | 25 | ECC_LOW LITERAL1 26 | ECC_MEDIUM LITERAL1 27 | ECC_QUARTILE LITERAL1 28 | ECC_HIGH LITERAL1 29 | MODE_NUMERIC LITERAL1 30 | MODE_ALPHANUMERIC LITERAL1 31 | MODE_BYTE LITERAL1 32 | -------------------------------------------------------------------------------- /source/libs/firefly_qrcode/library.properties: -------------------------------------------------------------------------------- 1 | name=QRCode 2 | version=0.0.1 3 | author=Richard Moore 4 | maintainer=Richard Moore 5 | sentence=A simple QR code generation library. 6 | paragraph=A simple QR code generation library. 7 | category=Other 8 | url=https://github.com/ricmoo/qrcode/ 9 | architectures=* 10 | includes=qrcode.h 11 | -------------------------------------------------------------------------------- /source/libs/firefly_qrcode/src/firefly_qrcode.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was 27 | * heavily inspired and compared against. 28 | * 29 | * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp 30 | */ 31 | 32 | #include "firefly_qrcode.h" 33 | 34 | #include 35 | #include 36 | 37 | #pragma mark - Error Correction Lookup tables 38 | 39 | #if LOCK_VERSION == 0 40 | 41 | static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = { 42 | // 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 Error correction level 43 | { 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium 44 | { 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low 45 | { 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High 46 | { 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile 47 | }; 48 | 49 | static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = { 50 | // Version: (note that index 0 is for padding, and is set to an illegal value) 51 | // 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 Error correction level 52 | { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium 53 | { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low 54 | { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High 55 | { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile 56 | }; 57 | 58 | static const uint16_t NUM_RAW_DATA_MODULES[40] = { 59 | // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 60 | 208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523, 61 | // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 62 | 7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587, 63 | // 32, 33, 34, 35, 36, 37, 38, 39, 40 64 | 19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648 65 | }; 66 | 67 | // @TODO: Put other LOCK_VERSIONS here 68 | #elif LOCK_VERSION == 3 69 | 70 | static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = { 71 | 26, 15, 44, 36 72 | }; 73 | 74 | static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = { 75 | 1, 1, 2, 2 76 | }; 77 | 78 | static const uint16_t NUM_RAW_DATA_MODULES = 567; 79 | 80 | #else 81 | 82 | #error Unsupported LOCK_VERSION (add it...) 83 | 84 | #endif 85 | 86 | 87 | static int max(int a, int b) { 88 | if (a > b) { return a; } 89 | return b; 90 | } 91 | 92 | /* 93 | static int abs(int value) { 94 | if (value < 0) { return -value; } 95 | return value; 96 | } 97 | */ 98 | 99 | 100 | #pragma mark - Mode testing and conversion 101 | 102 | static int8_t getAlphanumeric(char c) { 103 | 104 | if (c >= '0' && c <= '9') { return (c - '0'); } 105 | if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); } 106 | 107 | switch (c) { 108 | case ' ': return 36; 109 | case '$': return 37; 110 | case '%': return 38; 111 | case '*': return 39; 112 | case '+': return 40; 113 | case '-': return 41; 114 | case '.': return 42; 115 | case '/': return 43; 116 | case ':': return 44; 117 | } 118 | 119 | return -1; 120 | } 121 | 122 | static bool isAlphanumeric(const char *text, uint16_t length) { 123 | while (length != 0) { 124 | if (getAlphanumeric(text[--length]) == -1) { return false; } 125 | } 126 | return true; 127 | } 128 | 129 | 130 | static bool isNumeric(const char *text, uint16_t length) { 131 | while (length != 0) { 132 | char c = text[--length]; 133 | if (c < '0' || c > '9') { return false; } 134 | } 135 | return true; 136 | } 137 | 138 | 139 | #pragma mark - Counting 140 | 141 | // We store the following tightly packed (less 8) in modeInfo 142 | // <=9 <=26 <= 40 143 | // NUMERIC ( 10, 12, 14); 144 | // ALPHANUMERIC ( 9, 11, 13); 145 | // BYTE ( 8, 16, 16); 146 | static char getModeBits(uint8_t version, uint8_t mode) { 147 | // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits 148 | // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2)) 149 | uint32_t modeInfo = 0x7bbb80a; 150 | 151 | #if LOCK_VERSION == 0 || LOCK_VERSION > 9 152 | if (version > 9) { modeInfo >>= 9; } 153 | #endif 154 | 155 | #if LOCK_VERSION == 0 || LOCK_VERSION > 26 156 | if (version > 26) { modeInfo >>= 9; } 157 | #endif 158 | 159 | char result = 8 + ((modeInfo >> (3 * mode)) & 0x07); 160 | if (result == 15) { result = 16; } 161 | 162 | return result; 163 | } 164 | 165 | 166 | #pragma mark - BitBucket 167 | 168 | typedef struct BitBucket { 169 | uint32_t bitOffsetOrWidth; 170 | uint16_t capacityBytes; 171 | uint8_t *data; 172 | } BitBucket; 173 | 174 | /* 175 | void bb_dump(BitBucket *bitBuffer) { 176 | printf("Buffer: "); 177 | for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) { 178 | printf("%02x", bitBuffer->data[i]); 179 | if ((i % 4) == 3) { printf(" "); } 180 | } 181 | printf("\n"); 182 | } 183 | */ 184 | 185 | static uint16_t bb_getGridSizeBytes(uint8_t size) { 186 | return (((size * size) + 7) / 8); 187 | } 188 | 189 | static uint16_t bb_getBufferSizeBytes(uint32_t bits) { 190 | return ((bits + 7) / 8); 191 | } 192 | 193 | static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) { 194 | bitBuffer->bitOffsetOrWidth = 0; 195 | bitBuffer->capacityBytes = capacityBytes; 196 | bitBuffer->data = data; 197 | 198 | memset(data, 0, bitBuffer->capacityBytes); 199 | } 200 | 201 | static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) { 202 | bitGrid->bitOffsetOrWidth = size; 203 | bitGrid->capacityBytes = bb_getGridSizeBytes(size); 204 | bitGrid->data = data; 205 | 206 | memset(data, 0, bitGrid->capacityBytes); 207 | } 208 | 209 | static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) { 210 | uint32_t offset = bitBuffer->bitOffsetOrWidth; 211 | for (int8_t i = length - 1; i >= 0; i--, offset++) { 212 | bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); 213 | } 214 | bitBuffer->bitOffsetOrWidth = offset; 215 | } 216 | /* 217 | void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) { 218 | for (int8_t i = length - 1; i >= 0; i--, offset++) { 219 | bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); 220 | } 221 | } 222 | */ 223 | static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) { 224 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 225 | uint8_t mask = 1 << (7 - (offset & 0x07)); 226 | if (on) { 227 | bitGrid->data[offset >> 3] |= mask; 228 | } else { 229 | bitGrid->data[offset >> 3] &= ~mask; 230 | } 231 | } 232 | 233 | static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) { 234 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 235 | uint8_t mask = 1 << (7 - (offset & 0x07)); 236 | bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0); 237 | if (on ^ invert) { 238 | bitGrid->data[offset >> 3] |= mask; 239 | } else { 240 | bitGrid->data[offset >> 3] &= ~mask; 241 | } 242 | } 243 | 244 | static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) { 245 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 246 | return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; 247 | } 248 | 249 | 250 | #pragma mark - Drawing Patterns 251 | 252 | // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical 253 | // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. 254 | // This means it is possible to apply a mask, undo it, and try another mask. Note that a final 255 | // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). 256 | static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) { 257 | uint8_t size = modules->bitOffsetOrWidth; 258 | 259 | for (uint8_t y = 0; y < size; y++) { 260 | for (uint8_t x = 0; x < size; x++) { 261 | if (bb_getBit(isFunction, x, y)) { continue; } 262 | 263 | bool invert = 0; 264 | switch (mask) { 265 | case 0: invert = (x + y) % 2 == 0; break; 266 | case 1: invert = y % 2 == 0; break; 267 | case 2: invert = x % 3 == 0; break; 268 | case 3: invert = (x + y) % 3 == 0; break; 269 | case 4: invert = (x / 3 + y / 2) % 2 == 0; break; 270 | case 5: invert = x * y % 2 + x * y % 3 == 0; break; 271 | case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; 272 | case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; 273 | } 274 | bb_invertBit(modules, x, y, invert); 275 | } 276 | } 277 | } 278 | 279 | static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) { 280 | bb_setBit(modules, x, y, on); 281 | bb_setBit(isFunction, x, y, true); 282 | } 283 | 284 | // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). 285 | static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { 286 | uint8_t size = modules->bitOffsetOrWidth; 287 | 288 | for (int8_t i = -4; i <= 4; i++) { 289 | for (int8_t j = -4; j <= 4; j++) { 290 | uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm 291 | int16_t xx = x + j, yy = y + i; 292 | if (0 <= xx && xx < size && 0 <= yy && yy < size) { 293 | setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4); 294 | } 295 | } 296 | } 297 | } 298 | 299 | // Draws a 5*5 alignment pattern, with the center module at (x, y). 300 | static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { 301 | for (int8_t i = -2; i <= 2; i++) { 302 | for (int8_t j = -2; j <= 2; j++) { 303 | setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1); 304 | } 305 | } 306 | } 307 | 308 | // Draws two copies of the format bits (with its own error correction code) 309 | // based on the given mask and this object's error correction level field. 310 | static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) { 311 | 312 | uint8_t size = modules->bitOffsetOrWidth; 313 | 314 | // Calculate error correction code and pack bits 315 | uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3 316 | uint32_t rem = data; 317 | for (int i = 0; i < 10; i++) { 318 | rem = (rem << 1) ^ ((rem >> 9) * 0x537); 319 | } 320 | 321 | data = data << 10 | rem; 322 | data ^= 0x5412; // uint15 323 | 324 | // Draw first copy 325 | for (uint8_t i = 0; i <= 5; i++) { 326 | setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0); 327 | } 328 | 329 | setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0); 330 | setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0); 331 | setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0); 332 | 333 | for (int8_t i = 9; i < 15; i++) { 334 | setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0); 335 | } 336 | 337 | // Draw second copy 338 | for (int8_t i = 0; i <= 7; i++) { 339 | setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0); 340 | } 341 | 342 | for (int8_t i = 8; i < 15; i++) { 343 | setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0); 344 | } 345 | 346 | setFunctionModule(modules, isFunction, 8, size - 8, true); 347 | } 348 | 349 | 350 | // Draws two copies of the version bits (with its own error correction code), 351 | // based on this object's version field (which only has an effect for 7 <= version <= 40). 352 | static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) { 353 | 354 | int8_t size = modules->bitOffsetOrWidth; 355 | 356 | #if LOCK_VERSION != 0 && LOCK_VERSION < 7 357 | return; 358 | 359 | #else 360 | if (version < 7) { return; } 361 | 362 | // Calculate error correction code and pack bits 363 | uint32_t rem = version; // version is uint6, in the range [7, 40] 364 | for (uint8_t i = 0; i < 12; i++) { 365 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 366 | } 367 | 368 | uint32_t data = version << 12 | rem; // uint18 369 | 370 | // Draw two copies 371 | for (uint8_t i = 0; i < 18; i++) { 372 | bool bit = ((data >> i) & 1) != 0; 373 | uint8_t a = size - 11 + i % 3, b = i / 3; 374 | setFunctionModule(modules, isFunction, a, b, bit); 375 | setFunctionModule(modules, isFunction, b, a, bit); 376 | } 377 | 378 | #endif 379 | } 380 | 381 | static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) { 382 | 383 | uint8_t size = modules->bitOffsetOrWidth; 384 | 385 | // Draw the horizontal and vertical timing patterns 386 | for (uint8_t i = 0; i < size; i++) { 387 | setFunctionModule(modules, isFunction, 6, i, i % 2 == 0); 388 | setFunctionModule(modules, isFunction, i, 6, i % 2 == 0); 389 | } 390 | 391 | // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 392 | drawFinderPattern(modules, isFunction, 3, 3); 393 | drawFinderPattern(modules, isFunction, size - 4, 3); 394 | drawFinderPattern(modules, isFunction, 3, size - 4); 395 | 396 | #if LOCK_VERSION == 0 || LOCK_VERSION > 1 397 | 398 | if (version > 1) { 399 | 400 | // Draw the numerous alignment patterns 401 | 402 | uint8_t alignCount = version / 7 + 2; 403 | uint8_t step; 404 | if (version != 32) { 405 | step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 406 | } else { // C-C-C-Combo breaker! 407 | step = 26; 408 | } 409 | 410 | uint8_t alignPositionIndex = alignCount - 1; 411 | uint8_t alignPosition[alignCount]; 412 | 413 | alignPosition[0] = 6; 414 | 415 | uint8_t size = version * 4 + 17; 416 | for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) { 417 | alignPosition[alignPositionIndex--] = pos; 418 | } 419 | 420 | for (uint8_t i = 0; i < alignCount; i++) { 421 | for (uint8_t j = 0; j < alignCount; j++) { 422 | if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) { 423 | continue; // Skip the three finder corners 424 | } else { 425 | drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]); 426 | } 427 | } 428 | } 429 | } 430 | 431 | #endif 432 | 433 | // Draw configuration data 434 | drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor 435 | drawVersion(modules, isFunction, version); 436 | } 437 | 438 | 439 | // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire 440 | // data area of this QR Code symbol. Function modules need to be marked off before this is called. 441 | static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) { 442 | 443 | uint32_t bitLength = codewords->bitOffsetOrWidth; 444 | uint8_t *data = codewords->data; 445 | 446 | uint8_t size = modules->bitOffsetOrWidth; 447 | 448 | // Bit index into the data 449 | uint32_t i = 0; 450 | 451 | // Do the funny zigzag scan 452 | for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair 453 | if (right == 6) { right = 5; } 454 | 455 | for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter 456 | for (int j = 0; j < 2; j++) { 457 | uint8_t x = right - j; // Actual x coordinate 458 | bool upwards = ((right & 2) == 0) ^ (x < 6); 459 | uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate 460 | if (!bb_getBit(isFunction, x, y) && i < bitLength) { 461 | bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0); 462 | i++; 463 | } 464 | // If there are any remainder bits (0 to 7), they are already 465 | // set to 0/false/white when the grid of modules was initialized 466 | } 467 | } 468 | } 469 | } 470 | 471 | 472 | 473 | #pragma mark - Penalty Calculation 474 | 475 | #define PENALTY_N1 3 476 | #define PENALTY_N2 3 477 | #define PENALTY_N3 40 478 | #define PENALTY_N4 10 479 | 480 | // Calculates and returns the penalty score based on state of this QR Code's current modules. 481 | // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. 482 | // @TODO: This can be optimized by working with the bytes instead of bits. 483 | static uint32_t getPenaltyScore(BitBucket *modules) { 484 | uint32_t result = 0; 485 | 486 | uint8_t size = modules->bitOffsetOrWidth; 487 | 488 | // Adjacent modules in row having same color 489 | for (uint8_t y = 0; y < size; y++) { 490 | 491 | bool colorX = bb_getBit(modules, 0, y); 492 | for (uint8_t x = 1, runX = 1; x < size; x++) { 493 | bool cx = bb_getBit(modules, x, y); 494 | if (cx != colorX) { 495 | colorX = cx; 496 | runX = 1; 497 | 498 | } else { 499 | runX++; 500 | if (runX == 5) { 501 | result += PENALTY_N1; 502 | } else if (runX > 5) { 503 | result++; 504 | } 505 | } 506 | } 507 | } 508 | 509 | // Adjacent modules in column having same color 510 | for (uint8_t x = 0; x < size; x++) { 511 | bool colorY = bb_getBit(modules, x, 0); 512 | for (uint8_t y = 1, runY = 1; y < size; y++) { 513 | bool cy = bb_getBit(modules, x, y); 514 | if (cy != colorY) { 515 | colorY = cy; 516 | runY = 1; 517 | } else { 518 | runY++; 519 | if (runY == 5) { 520 | result += PENALTY_N1; 521 | } else if (runY > 5) { 522 | result++; 523 | } 524 | } 525 | } 526 | } 527 | 528 | uint16_t black = 0; 529 | for (uint8_t y = 0; y < size; y++) { 530 | uint16_t bitsRow = 0, bitsCol = 0; 531 | for (uint8_t x = 0; x < size; x++) { 532 | bool color = bb_getBit(modules, x, y); 533 | 534 | // 2*2 blocks of modules having same color 535 | if (x > 0 && y > 0) { 536 | bool colorUL = bb_getBit(modules, x - 1, y - 1); 537 | bool colorUR = bb_getBit(modules, x, y - 1); 538 | bool colorL = bb_getBit(modules, x - 1, y); 539 | if (color == colorUL && color == colorUR && color == colorL) { 540 | result += PENALTY_N2; 541 | } 542 | } 543 | 544 | // Finder-like pattern in rows and columns 545 | bitsRow = ((bitsRow << 1) & 0x7FF) | color; 546 | bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x); 547 | 548 | // Needs 11 bits accumulated 549 | if (x >= 10) { 550 | if (bitsRow == 0x05D || bitsRow == 0x5D0) { 551 | result += PENALTY_N3; 552 | } 553 | if (bitsCol == 0x05D || bitsCol == 0x5D0) { 554 | result += PENALTY_N3; 555 | } 556 | } 557 | 558 | // Balance of black and white modules 559 | if (color) { black++; } 560 | } 561 | } 562 | 563 | // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% 564 | uint16_t total = size * size; 565 | for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) { 566 | result += PENALTY_N4; 567 | } 568 | 569 | return result; 570 | } 571 | 572 | 573 | #pragma mark - Reed-Solomon Generator 574 | 575 | static uint8_t rs_multiply(uint8_t x, uint8_t y) { 576 | // Russian peasant multiplication 577 | // See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication 578 | uint16_t z = 0; 579 | for (int8_t i = 7; i >= 0; i--) { 580 | z = (z << 1) ^ ((z >> 7) * 0x11D); 581 | z ^= ((y >> i) & 1) * x; 582 | } 583 | return z; 584 | } 585 | 586 | static void rs_init(uint8_t degree, uint8_t *coeff) { 587 | memset(coeff, 0, degree); 588 | coeff[degree - 1] = 1; 589 | 590 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 591 | // drop the highest term, and store the rest of the coefficients in order of descending powers. 592 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 593 | uint16_t root = 1; 594 | for (uint8_t i = 0; i < degree; i++) { 595 | // Multiply the current product by (x - r^i) 596 | for (uint8_t j = 0; j < degree; j++) { 597 | coeff[j] = rs_multiply(coeff[j], root); 598 | if (j + 1 < degree) { 599 | coeff[j] ^= coeff[j + 1]; 600 | } 601 | } 602 | root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) 603 | } 604 | } 605 | 606 | static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) { 607 | 608 | // Compute the remainder by performing polynomial division 609 | for (uint8_t i = 0; i < length; i++) { 610 | uint8_t factor = data[i] ^ result[0]; 611 | for (uint8_t j = 1; j < degree; j++) { 612 | result[(j - 1) * stride] = result[j * stride]; 613 | } 614 | 615 | result[(degree - 1) * stride] = 0; 616 | 617 | for (uint8_t j = 0; j < degree; j++) { 618 | result[j * stride] ^= rs_multiply(coeff[j], factor); 619 | } 620 | } 621 | } 622 | 623 | 624 | 625 | #pragma mark - QrCode 626 | 627 | static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) { 628 | int8_t mode = MODE_BYTE; 629 | 630 | if (isNumeric((char*)text, length)) { 631 | mode = MODE_NUMERIC; 632 | bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4); 633 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC)); 634 | 635 | uint16_t accumData = 0; 636 | uint8_t accumCount = 0; 637 | for (uint16_t i = 0; i < length; i++) { 638 | accumData = accumData * 10 + ((char)(text[i]) - '0'); 639 | accumCount++; 640 | if (accumCount == 3) { 641 | bb_appendBits(dataCodewords, accumData, 10); 642 | accumData = 0; 643 | accumCount = 0; 644 | } 645 | } 646 | 647 | // 1 or 2 digits remaining 648 | if (accumCount > 0) { 649 | bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1); 650 | } 651 | 652 | } else if (isAlphanumeric((char*)text, length)) { 653 | mode = MODE_ALPHANUMERIC; 654 | bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4); 655 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC)); 656 | 657 | uint16_t accumData = 0; 658 | uint8_t accumCount = 0; 659 | for (uint16_t i = 0; i < length; i++) { 660 | accumData = accumData * 45 + getAlphanumeric((char)(text[i])); 661 | accumCount++; 662 | if (accumCount == 2) { 663 | bb_appendBits(dataCodewords, accumData, 11); 664 | accumData = 0; 665 | accumCount = 0; 666 | } 667 | } 668 | 669 | // 1 character remaining 670 | if (accumCount > 0) { 671 | bb_appendBits(dataCodewords, accumData, 6); 672 | } 673 | } else { 674 | bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4); 675 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE)); 676 | for (uint16_t i = 0; i < length; i++) { 677 | bb_appendBits(dataCodewords, (char)(text[i]), 8); 678 | } 679 | } 680 | return mode; 681 | } 682 | 683 | static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) { 684 | 685 | // See: http://www.thonky.com/qr-code-tutorial/structure-final-message 686 | 687 | #if LOCK_VERSION == 0 688 | uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1]; 689 | uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1]; 690 | uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; 691 | #else 692 | uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc]; 693 | uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc]; 694 | uint16_t moduleCount = NUM_RAW_DATA_MODULES; 695 | #endif 696 | 697 | uint8_t blockEccLen = totalEcc / numBlocks; 698 | uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks; 699 | uint8_t shortBlockLen = moduleCount / 8 / numBlocks; 700 | 701 | uint8_t shortDataBlockLen = shortBlockLen - blockEccLen; 702 | 703 | uint8_t result[data->capacityBytes]; 704 | memset(result, 0, sizeof(result)); 705 | 706 | uint8_t coeff[blockEccLen]; 707 | rs_init(blockEccLen, coeff); 708 | 709 | uint16_t offset = 0; 710 | uint8_t *dataBytes = data->data; 711 | 712 | // Interleave all short blocks 713 | for (uint8_t i = 0; i < shortDataBlockLen; i++) { 714 | uint16_t index = i; 715 | uint8_t stride = shortDataBlockLen; 716 | for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { 717 | result[offset++] = dataBytes[index]; 718 | 719 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 720 | if (blockNum == numShortBlocks) { stride++; } 721 | #endif 722 | index += stride; 723 | } 724 | } 725 | 726 | // Version less than 5 only have short blocks 727 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 728 | { 729 | // Interleave long blocks 730 | uint16_t index = shortDataBlockLen * (numShortBlocks + 1); 731 | uint8_t stride = shortDataBlockLen; 732 | for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) { 733 | result[offset++] = dataBytes[index]; 734 | 735 | if (blockNum == 0) { stride++; } 736 | index += stride; 737 | } 738 | } 739 | #endif 740 | 741 | // Add all ecc blocks, interleaved 742 | uint8_t blockSize = shortDataBlockLen; 743 | for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { 744 | 745 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 746 | if (blockNum == numShortBlocks) { blockSize++; } 747 | #endif 748 | 749 | rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks); 750 | dataBytes += blockSize; 751 | } 752 | 753 | memcpy(data->data, result, data->capacityBytes); 754 | data->bitOffsetOrWidth = moduleCount; 755 | } 756 | 757 | // We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits) 758 | // The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc) 759 | static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0); 760 | 761 | 762 | #pragma mark - Public QRCode functions 763 | 764 | uint16_t qrcode_getBufferSize(uint8_t version) { 765 | return bb_getGridSizeBytes(4 * version + 17); 766 | } 767 | 768 | // @TODO: Return error if data is too big. 769 | int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) { 770 | uint8_t size = version * 4 + 17; 771 | qrcode->version = version; 772 | qrcode->size = size; 773 | qrcode->ecc = ecc; 774 | qrcode->modules = modules; 775 | 776 | uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03; 777 | 778 | #if LOCK_VERSION == 0 779 | uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; 780 | uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1]; 781 | #else 782 | version = LOCK_VERSION; 783 | uint16_t moduleCount = NUM_RAW_DATA_MODULES; 784 | uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits]; 785 | #endif 786 | 787 | struct BitBucket codewords; 788 | uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)]; 789 | bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes)); 790 | 791 | // Place the data code words into the buffer 792 | int8_t mode = encodeDataCodewords(&codewords, data, length, version); 793 | 794 | if (mode < 0) { return -1; } 795 | qrcode->mode = mode; 796 | 797 | // Add terminator and pad up to a byte if applicable 798 | uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth; 799 | if (padding > 4) { padding = 4; } 800 | bb_appendBits(&codewords, 0, padding); 801 | bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8); 802 | 803 | // Pad with alternate bytes until data capacity is reached 804 | for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) { 805 | bb_appendBits(&codewords, padByte, 8); 806 | } 807 | 808 | BitBucket modulesGrid; 809 | bb_initGrid(&modulesGrid, modules, size); 810 | 811 | BitBucket isFunctionGrid; 812 | uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)]; 813 | bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size); 814 | 815 | // Draw function patterns, draw all codewords, do masking 816 | drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits); 817 | performErrorCorrection(version, eccFormatBits, &codewords); 818 | drawCodewords(&modulesGrid, &isFunctionGrid, &codewords); 819 | 820 | // Find the best (lowest penalty) mask 821 | uint8_t mask = 0; 822 | int32_t minPenalty = INT32_MAX; 823 | for (uint8_t i = 0; i < 8; i++) { 824 | drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i); 825 | applyMask(&modulesGrid, &isFunctionGrid, i); 826 | int penalty = getPenaltyScore(&modulesGrid); 827 | if (penalty < minPenalty) { 828 | mask = i; 829 | minPenalty = penalty; 830 | } 831 | applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR 832 | } 833 | qrcode->mask = mask; 834 | 835 | // Overwrite old format bits 836 | drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask); 837 | 838 | // Apply the final choice of mask 839 | applyMask(&modulesGrid, &isFunctionGrid, mask); 840 | 841 | return 0; 842 | } 843 | 844 | int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) { 845 | return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data)); 846 | } 847 | 848 | bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) { 849 | if (x < 0 || x >= qrcode->size || y < 0 || y >= qrcode->size) { 850 | return false; 851 | } 852 | 853 | uint32_t offset = y * qrcode->size + x; 854 | return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; 855 | } 856 | 857 | /* 858 | uint8_t qrcode_getHexLength(QRCode *qrcode) { 859 | return ((qrcode->size * qrcode->size) + 7) / 4; 860 | } 861 | 862 | void qrcode_getHex(QRCode *qrcode, char *result) { 863 | 864 | } 865 | */ 866 | -------------------------------------------------------------------------------- /source/libs/firefly_qrcode/src/firefly_qrcode.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was 27 | * heavily inspired and compared against. 28 | * 29 | * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp 30 | */ 31 | 32 | 33 | #ifndef __ETHERS_QRCODE_H_ 34 | #define __ETHERS_QRCODE_H_ 35 | 36 | #ifndef __cplusplus 37 | typedef unsigned char bool; 38 | static const bool false = 0; 39 | static const bool true = 1; 40 | #endif 41 | 42 | #include 43 | 44 | 45 | // QR Code Format Encoding 46 | #define MODE_NUMERIC 0 47 | #define MODE_ALPHANUMERIC 1 48 | #define MODE_BYTE 2 49 | 50 | 51 | // Error Correction Code Levels 52 | #define ECC_LOW 0 53 | #define ECC_MEDIUM 1 54 | #define ECC_QUARTILE 2 55 | #define ECC_HIGH 3 56 | 57 | 58 | // If set to non-zero, this library can ONLY produce QR codes at that version 59 | // This saves a lot of dynamic memory, as the codeword tables are skipped 60 | #ifndef LOCK_VERSION 61 | #define LOCK_VERSION 3 62 | #endif 63 | 64 | 65 | typedef struct QRCode { 66 | uint8_t version; 67 | uint8_t size; 68 | uint8_t ecc; 69 | uint8_t mode; 70 | uint8_t mask; 71 | uint8_t *modules; 72 | } QRCode; 73 | 74 | 75 | #ifdef __cplusplus 76 | extern "C"{ 77 | #endif /* __cplusplus */ 78 | 79 | 80 | 81 | uint16_t qrcode_getBufferSize(uint8_t version); 82 | 83 | int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data); 84 | int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length); 85 | 86 | bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y); 87 | 88 | 89 | 90 | #ifdef __cplusplus 91 | } 92 | #endif /* __cplusplus */ 93 | 94 | 95 | #endif /* __ETHERS_QRCODE_H_ */ 96 | --------------------------------------------------------------------------------