├── .gitignore ├── LICENSE ├── library.json ├── library.properties ├── readme.md └── src ├── ArduinoJWT.cpp ├── ArduinoJWT.h ├── base64.hpp ├── sha256.cpp └── sha256.h /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .piolibdeps 3 | .clang_complete 4 | .gcc-flags.json 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Chris Moorhouse 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ArduinoJWT", 3 | "keywords": "jwt, iot", 4 | "description": "Library for encoding and decoding JSON web tokens for the Arduino and ESP8266 platforms. This library does not currently support adding any options to the JWT and uses a fixed header for HS256.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/yutter/ArduinoJWT.git" 8 | }, 9 | "authors": 10 | [ 11 | { 12 | "name": "Chris Moorhouse", 13 | "email": "cm@interior-automation.co.uk", 14 | "url": "http://www.interior-automation.co.uk", 15 | "maintainer": true 16 | } 17 | ], 18 | "version": "1.0.1", 19 | "frameworks": "arduino", 20 | "platforms": [ 21 | "atmelavr", 22 | "espressif" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ArduinoJWT 2 | version=1.0.1 3 | author=Chris Moorhouse 4 | maintainer=Chris Moorhouse 5 | sentence=Library for encoding and decoding JSON web tokens for the Arduino, ESP8266 and ESP32 platforms. 6 | paragraph=This library does not currently support adding any options to the JWT and uses a fixed header for HS256. 7 | category=Communication 8 | url=https://github.com/yutter/ArduinoJWT 9 | architectures=avr,esp8266,esp32 10 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Arduino JSON Web Token Library 2 | 3 | Library for encoding and decoding JSON web tokens for the Arduino and ESP8266 platforms. 4 | 5 | ## Limitations 6 | 7 | - It is not currently possible to add options such as expiry time, user etc to the JWT 8 | - The header is fixed to be {"alg": "HS256", "typ": "JWT"} 9 | 10 | ## Compatible Hardware 11 | 12 | - Arduino 13 | - Intel Galileo/Edison 14 | - ESP8266 15 | 16 | ## License 17 | 18 | This code is released under the BSD 3 Clause License. 19 | 20 | ## Special Thanks 21 | 22 | This library uses extracts from the following libraries: 23 | - Base64 Encoding: https://github.com/Densaugeo/base64_arduino by Densaugeo 24 | - HMAC-SHA256 Cryptography: https://github.com/Cathedrow/Cryptosuite by Cathedrow 25 | -------------------------------------------------------------------------------- /src/ArduinoJWT.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Copyright (c) 2016, Interior Automation Ltd. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its contributors may be 17 | used to endorse or promote products derived from this software without specific prior 18 | written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 21 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 24 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | **/ 30 | 31 | #include 32 | #include "ArduinoJWT.h" 33 | #include "base64.hpp" 34 | #include "sha256.h" 35 | 36 | // The standard JWT header already base64 encoded. Equates to {"alg": "HS256", "typ": "JWT"} 37 | const PROGMEM char* jwtHeader = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; 38 | 39 | ArduinoJWT::ArduinoJWT(String psk) { 40 | _psk = psk; 41 | } 42 | 43 | ArduinoJWT::ArduinoJWT(char* psk) { 44 | _psk = String(psk); 45 | } 46 | 47 | void ArduinoJWT::setPSK(String psk) { 48 | _psk = psk; 49 | } 50 | void ArduinoJWT::setPSK(char* psk) { 51 | _psk = String(psk); 52 | } 53 | 54 | int ArduinoJWT::getJWTLength(String& payload) { 55 | return getJWTLength((char*)payload.c_str()); 56 | } 57 | 58 | int ArduinoJWT::getJWTLength(char* payload) { 59 | return strlen(jwtHeader) + encode_base64_length(strlen(payload)) + encode_base64_length(32) + 2; 60 | } 61 | 62 | int ArduinoJWT::getJWTPayloadLength(String& jwt) { 63 | return getJWTPayloadLength((char*)jwt.c_str()); 64 | } 65 | 66 | int ArduinoJWT::getJWTPayloadLength(char* jwt) { 67 | char jwtCopy[strlen(jwt)]; 68 | memcpy((char*)jwtCopy, jwt, strlen(jwt)); 69 | // Get all three jwt parts 70 | const char* sep = "."; 71 | char* token; 72 | token = strtok(jwtCopy, sep); 73 | token = strtok(NULL, sep); 74 | if(token == NULL) { 75 | return -1; 76 | } else { 77 | return decode_base64_length((unsigned char*)token) + 1; 78 | } 79 | } 80 | 81 | String ArduinoJWT::encodeJWT(String& payload) { 82 | char jwt[getJWTLength(payload)]; 83 | encodeJWT((char*)payload.c_str(), (char*)jwt); 84 | return String(jwt); 85 | } 86 | 87 | void ArduinoJWT::encodeJWT(char* payload, char* jwt) { 88 | unsigned char* ptr = (unsigned char*)jwt; 89 | // Build the initial part of the jwt (header.payload) 90 | memcpy(ptr, jwtHeader, strlen(jwtHeader)); 91 | ptr += strlen(jwtHeader); 92 | *ptr++ = '.'; 93 | encode_base64((unsigned char*)payload, strlen(payload), ptr); 94 | ptr += encode_base64_length(strlen(payload)); 95 | // Get rid of any padding (trailing '=' added when base64 encoding) 96 | while(*(ptr - 1) == '=') { 97 | ptr--; 98 | } 99 | *(ptr) = 0; 100 | // Build the signature 101 | Sha256.initHmac((const unsigned char*)_psk.c_str(), _psk.length()); 102 | Sha256.print(jwt); 103 | // Add the signature to the jwt 104 | *ptr++ = '.'; 105 | encode_base64(Sha256.resultHmac(), 32, ptr); 106 | ptr += encode_base64_length(32); 107 | // Get rid of any padding and replace / and + 108 | while(*(ptr - 1) == '=') { 109 | ptr--; 110 | } 111 | *(ptr) = 0; 112 | } 113 | 114 | bool ArduinoJWT::decodeJWT(String& jwt, String& payload) { 115 | int payloadLength = getJWTPayloadLength(jwt); 116 | if(payloadLength > 0) { 117 | char jsonPayload[payloadLength]; 118 | if(decodeJWT((char*)jwt.c_str(), (char*)jsonPayload, payloadLength)) { 119 | payload = String(jsonPayload); 120 | return true; 121 | } 122 | } 123 | return false; 124 | } 125 | 126 | bool ArduinoJWT::decodeJWT(char* jwt, char* payload, int payloadLength) { 127 | // Get all three jwt parts 128 | const char* sep = "."; 129 | char* encodedHeader = strtok(jwt, sep); 130 | char* encodedPayload = strtok(NULL, sep); 131 | char* encodedSignature = strtok(NULL, sep); 132 | 133 | // Check all three jwt parts exist 134 | if(encodedHeader == NULL || encodedPayload == NULL || encodedSignature == NULL) 135 | { 136 | payload = NULL; 137 | return false; 138 | } 139 | 140 | // Build the signature 141 | Sha256.initHmac((const unsigned char*)_psk.c_str(), _psk.length()); 142 | Sha256.print(encodedHeader); 143 | Sha256.print("."); 144 | Sha256.print(encodedPayload); 145 | 146 | // Encode the signature as base64 147 | unsigned char base64Signature[encode_base64_length(32)]; 148 | encode_base64(Sha256.resultHmac(), 32, base64Signature); 149 | unsigned char* ptr = &base64Signature[0] + encode_base64_length(32); 150 | // Get rid of any padding and replace / and + 151 | while(*(ptr - 1) == '=') { 152 | ptr--; 153 | } 154 | *(ptr) = 0; 155 | 156 | // Do the signatures match? 157 | if(strcmp((char*)encodedSignature, (char*)base64Signature) == 0) { 158 | // Decode the payload 159 | decode_base64((unsigned char*)encodedPayload, (unsigned char*)payload); 160 | payload[payloadLength - 1] = 0; 161 | return true; 162 | } else { 163 | payload = NULL; 164 | return false; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/ArduinoJWT.h: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Copyright (c) 2016, Interior Automation Ltd. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its contributors may be 17 | used to endorse or promote products derived from this software without specific prior 18 | written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 21 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 24 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | **/ 30 | 31 | #ifndef ARDUINO_JWT_H 32 | #define ARDUINO_JWT_H 33 | 34 | #include 35 | 36 | 37 | 38 | class ArduinoJWT { 39 | private: 40 | String _psk; 41 | 42 | public: 43 | ArduinoJWT(String psk); 44 | ArduinoJWT(char* psk); 45 | 46 | // Set a new psk for encoding and decoding JWTs 47 | void setPSK(String psk); 48 | void setPSK(char* psk); 49 | 50 | // Get the calculated length of a JWT 51 | int getJWTLength(String& payload); 52 | int getJWTLength(char* payload); 53 | // Get the length of the decoded payload from a JWT 54 | int getJWTPayloadLength(String& jwt); 55 | int getJWTPayloadLength(char* jwt); 56 | // Create a JSON Web Token 57 | String encodeJWT(String& payload); 58 | void encodeJWT(char* payload, char* jwt); 59 | // Decode a JWT and retreive the payload 60 | bool decodeJWT(String& jwt, String& payload); 61 | bool decodeJWT(char* jwt, char* payload, int payloadLength); 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/base64.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on original work by Densaugeo 3 | * Original library located at https://github.com/Densaugeo/base64_arduino 4 | */ 5 | 6 | /** 7 | * Base64 encoding and decoding of strings. Uses '+' for 62, '\' for 63, '=' for padding 8 | * This has been modified to use '-' for 62, '_' for 63 as per the JWT specification 9 | */ 10 | 11 | #ifndef BASE64_H_INCLUDED 12 | #define BASE64_H_INCLUDED 13 | 14 | /* binary_to_base64: 15 | * Description: 16 | * Converts a single byte from a binary value to the corresponding base64 character 17 | * Parameters: 18 | * v - Byte to convert 19 | * Returns: 20 | * ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character 21 | * and 255 is returned 22 | */ 23 | unsigned char binary_to_base64(unsigned char v); 24 | 25 | /* base64_to_binary: 26 | * Description: 27 | * Converts a single byte from a base64 character to the corresponding binary value 28 | * Parameters: 29 | * c - Base64 character (as ascii code) 30 | * Returns: 31 | * 6-bit binary value 32 | */ 33 | unsigned char base64_to_binary(unsigned char v); 34 | 35 | /* encode_base64_length: 36 | * Description: 37 | * Calculates length of base64 string needed for a given number of binary bytes 38 | * Parameters: 39 | * input_length - Amount of binary data in bytes 40 | * Returns: 41 | * Number of base64 characters needed to encode input_length bytes of binary data 42 | */ 43 | unsigned int encode_base64_length(unsigned int input_length); 44 | 45 | /* decode_base64_length: 46 | * Description: 47 | * Calculates number of bytes of binary data in a base64 string 48 | * Parameters: 49 | * input - Base64-encoded null-terminated string 50 | * Returns: 51 | * Number of bytes of binary data in input 52 | */ 53 | unsigned int decode_base64_length(unsigned char input[]); 54 | 55 | /* encode_base64: 56 | * Description: 57 | * Converts an array of bytes to a base64 null-terminated string 58 | * Parameters: 59 | * input - Pointer to input data 60 | * input_length - Number of bytes to read from input pointer 61 | * output - Pointer to output string. Null terminator will be added automatically 62 | * Returns: 63 | * Length of encoded string in bytes (not including null terminator) 64 | */ 65 | unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]); 66 | 67 | /* decode_base64: 68 | * Description: 69 | * Converts a base64 null-terminated string to an array of bytes 70 | * Parameters: 71 | * input - Pointer to input string 72 | * output - Pointer to output array 73 | * Returns: 74 | * Number of bytes in the decoded binary 75 | */ 76 | unsigned int decode_base64(unsigned char input[], unsigned char output[]); 77 | 78 | unsigned char binary_to_base64(unsigned char v) { 79 | // Capital letters - 'A' is ascii 65 and base64 0 80 | if(v < 26) return v + 'A'; 81 | 82 | // Lowercase letters - 'a' is ascii 97 and base64 26 83 | if(v < 52) return v + 71; 84 | 85 | // Digits - '0' is ascii 48 and base64 52 86 | if(v < 62) return v - 4; 87 | 88 | // '+' is ascii 43 and base64 62 89 | if(v == 62) return '-'; 90 | 91 | // '/' is ascii 47 and base64 63 92 | if(v == 63) return '_'; 93 | 94 | return 64; 95 | } 96 | 97 | unsigned char base64_to_binary(unsigned char c) { 98 | // Capital letters - 'A' is ascii 65 and base64 0 99 | if('A' <= c && c <= 'Z') return c - 'A'; 100 | 101 | // Lowercase letters - 'a' is ascii 97 and base64 26 102 | if('a' <= c && c <= 'z') return c - 71; 103 | 104 | // Digits - '0' is ascii 48 and base64 52 105 | if('0' <= c && c <= '9') return c + 4; 106 | 107 | // '+' is ascii 43 and base64 62 108 | if(c == '-') return 62; 109 | 110 | // '/' is ascii 47 and base64 63 111 | if(c == '_') return 63; 112 | 113 | return 255; 114 | } 115 | 116 | unsigned int encode_base64_length(unsigned int input_length) { 117 | return (input_length + 2)/3*4; 118 | } 119 | 120 | unsigned int decode_base64_length(unsigned char input[]) { 121 | unsigned char *start = input; 122 | 123 | while(base64_to_binary(input[0]) < 64) { 124 | ++input; 125 | } 126 | 127 | unsigned int input_length = input - start; 128 | 129 | unsigned int output_length = input_length/4*3; 130 | 131 | switch(input_length % 4) { 132 | default: return output_length; 133 | case 2: return output_length + 1; 134 | case 3: return output_length + 2; 135 | } 136 | } 137 | 138 | unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) { 139 | unsigned int full_sets = input_length/3; 140 | 141 | // While there are still full sets of 24 bits... 142 | for(unsigned int i = 0; i < full_sets; ++i) { 143 | output[0] = binary_to_base64( input[0] >> 2); 144 | output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4); 145 | output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6); 146 | output[3] = binary_to_base64( input[2] & 0x3F); 147 | 148 | input += 3; 149 | output += 4; 150 | } 151 | 152 | switch(input_length % 3) { 153 | case 0: 154 | output[0] = '\0'; 155 | break; 156 | case 1: 157 | output[0] = binary_to_base64( input[0] >> 2); 158 | output[1] = binary_to_base64((input[0] & 0x03) << 4); 159 | output[2] = '='; 160 | output[3] = '='; 161 | output[4] = '\0'; 162 | break; 163 | case 2: 164 | output[0] = binary_to_base64( input[0] >> 2); 165 | output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4); 166 | output[2] = binary_to_base64((input[1] & 0x0F) << 2); 167 | output[3] = '='; 168 | output[4] = '\0'; 169 | break; 170 | } 171 | 172 | return encode_base64_length(input_length); 173 | } 174 | 175 | unsigned int decode_base64(unsigned char input[], unsigned char output[]) { 176 | unsigned int output_length = decode_base64_length(input); 177 | 178 | // While there are still full sets of 24 bits... 179 | for(unsigned int i = 2; i < output_length; i += 3) { 180 | output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4; 181 | output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2; 182 | output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]); 183 | 184 | input += 4; 185 | output += 3; 186 | } 187 | 188 | switch(output_length % 3) { 189 | case 1: 190 | output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4; 191 | break; 192 | case 2: 193 | output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4; 194 | output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2; 195 | break; 196 | } 197 | 198 | return output_length; 199 | } 200 | 201 | #endif // ifndef 202 | -------------------------------------------------------------------------------- /src/sha256.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on original work by Cathedrow 3 | * Original library located at https://github.com/Cathedrow/Cryptosuite 4 | */ 5 | 6 | #include 7 | #include "sha256.h" 8 | 9 | uint32_t sha256K[] PROGMEM = { 10 | 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 11 | 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 12 | 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 13 | 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 14 | 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 15 | 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 16 | 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 17 | 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 18 | }; 19 | 20 | #define BUFFER_SIZE 64 21 | 22 | unsigned char sha256InitState[] PROGMEM = { 23 | 0x67,0xe6,0x09,0x6a, // H0 24 | 0x85,0xae,0x67,0xbb, // H1 25 | 0x72,0xf3,0x6e,0x3c, // H2 26 | 0x3a,0xf5,0x4f,0xa5, // H3 27 | 0x7f,0x52,0x0e,0x51, // H4 28 | 0x8c,0x68,0x05,0x9b, // H5 29 | 0xab,0xd9,0x83,0x1f, // H6 30 | 0x19,0xcd,0xe0,0x5b // H7 31 | }; 32 | 33 | void Sha256Class::init(void) { 34 | memcpy_P(state.b,sha256InitState,32); 35 | byteCount = 0; 36 | bufferOffset = 0; 37 | } 38 | 39 | uint32_t Sha256Class::ror32(uint32_t number, unsigned char bits) { 40 | return ((number << (32-bits)) | (number >> bits)); 41 | } 42 | 43 | void Sha256Class::hashBlock() { 44 | // Sha256 only for now 45 | unsigned char i; 46 | uint32_t a,b,c,d,e,f,g,h,t1,t2; 47 | 48 | a=state.w[0]; 49 | b=state.w[1]; 50 | c=state.w[2]; 51 | d=state.w[3]; 52 | e=state.w[4]; 53 | f=state.w[5]; 54 | g=state.w[6]; 55 | h=state.w[7]; 56 | 57 | for (i=0; i<64; i++) { 58 | if (i>=16) { 59 | t1 = buffer.w[i&15] + buffer.w[(i-7)&15]; 60 | t2 = buffer.w[(i-2)&15]; 61 | t1 += ror32(t2,17) ^ ror32(t2,19) ^ (t2>>10); 62 | t2 = buffer.w[(i-15)&15]; 63 | t1 += ror32(t2,7) ^ ror32(t2,18) ^ (t2>>3); 64 | buffer.w[i&15] = t1; 65 | } 66 | t1 = h; 67 | t1 += ror32(e,6) ^ ror32(e,11) ^ ror32(e,25); // ?1(e) 68 | t1 += g ^ (e & (g ^ f)); // Ch(e,f,g) 69 | t1 += pgm_read_dword(sha256K+i); // Ki 70 | t1 += buffer.w[i&15]; // Wi 71 | t2 = ror32(a,2) ^ ror32(a,13) ^ ror32(a,22); // ?0(a) 72 | t2 += ((b & c) | (a & (b | c))); // Maj(a,b,c) 73 | h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2; 74 | } 75 | state.w[0] += a; 76 | state.w[1] += b; 77 | state.w[2] += c; 78 | state.w[3] += d; 79 | state.w[4] += e; 80 | state.w[5] += f; 81 | state.w[6] += g; 82 | state.w[7] += h; 83 | } 84 | 85 | void Sha256Class::addUncounted(unsigned char data) { 86 | buffer.b[bufferOffset ^ 3] = data; 87 | bufferOffset++; 88 | if (bufferOffset == BUFFER_SIZE) { 89 | hashBlock(); 90 | bufferOffset = 0; 91 | } 92 | } 93 | 94 | size_t Sha256Class::write(unsigned char data) { 95 | ++byteCount; 96 | addUncounted(data); 97 | return( 1 ); 98 | } 99 | 100 | void Sha256Class::pad() { 101 | // Implement SHA-256 padding (fips180-2 §5.1.1) 102 | 103 | // Pad with 0x80 followed by 0x00 until the end of the block 104 | addUncounted(0x80); 105 | while (bufferOffset != 56) addUncounted(0x00); 106 | 107 | // Append length in the last 8 bytes 108 | addUncounted(0); // We're only using 32 bit lengths 109 | addUncounted(0); // But SHA-1 supports 64 bit lengths 110 | addUncounted(0); // So zero pad the top bits 111 | addUncounted(byteCount >> 29); // Shifting to multiply by 8 112 | addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as 113 | addUncounted(byteCount >> 13); // byte. 114 | addUncounted(byteCount >> 5); 115 | addUncounted(byteCount << 3); 116 | } 117 | 118 | 119 | unsigned char* Sha256Class::result(void) { 120 | // Pad to complete the last block 121 | pad(); 122 | 123 | // Swap byte order back 124 | for (int i=0; i<8; i++) { 125 | uint32_t a,b; 126 | a=state.w[i]; 127 | b=a<<24; 128 | b|=(a<<8) & 0x00ff0000; 129 | b|=(a>>8) & 0x0000ff00; 130 | b|=a>>24; 131 | state.w[i]=b; 132 | } 133 | 134 | // Return pointer to hash (20 characters) 135 | return state.b; 136 | } 137 | 138 | 139 | #define HMAC_IPAD 0x36 140 | #define HMAC_OPAD 0x5c 141 | 142 | unsigned char keyBuffer[BLOCK_LENGTH]; // K0 in FIPS-198a 143 | unsigned char innerHash[HASH_LENGTH]; 144 | 145 | void Sha256Class::initHmac(const unsigned char* key, int keyLength) { 146 | unsigned char i; 147 | memset(keyBuffer,0,BLOCK_LENGTH); 148 | if (keyLength > BLOCK_LENGTH) { 149 | // Hash long keys 150 | init(); 151 | for (;keyLength--;) write(*key++); 152 | memcpy(keyBuffer,result(),HASH_LENGTH); 153 | } else { 154 | // Block length keys are used as is 155 | memcpy(keyBuffer,key,keyLength); 156 | } 157 | //for (i=0; i 10 | #include "Print.h" 11 | 12 | #define HASH_LENGTH 32 13 | #define BLOCK_LENGTH 64 14 | 15 | union _buffer { 16 | unsigned char b[BLOCK_LENGTH]; 17 | uint32_t w[BLOCK_LENGTH/4]; 18 | }; 19 | union _state { 20 | unsigned char b[HASH_LENGTH]; 21 | uint32_t w[HASH_LENGTH/4]; 22 | }; 23 | 24 | class Sha256Class : public Print 25 | { 26 | public: 27 | void init(void); 28 | void initHmac(const unsigned char* secret, int secretLength); 29 | unsigned char* result(void); 30 | unsigned char* resultHmac(void); 31 | virtual size_t write(unsigned char); 32 | using Print::write; 33 | private: 34 | void pad(); 35 | void addUncounted(unsigned char data); 36 | void hashBlock(); 37 | uint32_t ror32(uint32_t number, unsigned char bits); 38 | _buffer buffer; 39 | unsigned char bufferOffset; 40 | _state state; 41 | uint32_t byteCount; 42 | unsigned char keyBuffer[BLOCK_LENGTH]; 43 | unsigned char innerHash[HASH_LENGTH]; 44 | }; 45 | extern Sha256Class Sha256; 46 | 47 | #endif 48 | --------------------------------------------------------------------------------