├── .gitignore ├── Base64.cpp ├── Base64.h ├── MD5.c ├── MD5.h ├── README.md ├── WebSocketClient.cpp ├── WebSocketClient.h ├── WebSocketServer.cpp ├── WebSocketServer.h ├── examples ├── WebSocketClient_Demo │ └── WebSocketClient_Demo.ino ├── WebSocketServer.html └── WebSocketServer_Demo │ └── WebSocketServer_Demo.ino ├── global.h ├── sha1.cpp └── sha1.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Base64.cpp: -------------------------------------------------------------------------------- 1 | #include "Base64.h" 2 | 3 | const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 4 | "abcdefghijklmnopqrstuvwxyz" 5 | "0123456789+/"; 6 | 7 | /* 'Private' declarations */ 8 | inline void a3_to_a4(unsigned char * a4, unsigned char * a3); 9 | inline void a4_to_a3(unsigned char * a3, unsigned char * a4); 10 | inline unsigned char b64_lookup(char c); 11 | 12 | int base64_encode(char *output, char *input, int inputLen) { 13 | int i = 0, j = 0; 14 | int encLen = 0; 15 | unsigned char a3[3]; 16 | unsigned char a4[4]; 17 | 18 | while(inputLen--) { 19 | a3[i++] = *(input++); 20 | if(i == 3) { 21 | a3_to_a4(a4, a3); 22 | 23 | for(i = 0; i < 4; i++) { 24 | output[encLen++] = b64_alphabet[a4[i]]; 25 | } 26 | 27 | i = 0; 28 | } 29 | } 30 | 31 | if(i) { 32 | for(j = i; j < 3; j++) { 33 | a3[j] = '\0'; 34 | } 35 | 36 | a3_to_a4(a4, a3); 37 | 38 | for(j = 0; j < i + 1; j++) { 39 | output[encLen++] = b64_alphabet[a4[j]]; 40 | } 41 | 42 | while((i++ < 3)) { 43 | output[encLen++] = '='; 44 | } 45 | } 46 | output[encLen] = '\0'; 47 | return encLen; 48 | } 49 | 50 | int base64_decode(char * output, char * input, int inputLen) { 51 | int i = 0, j = 0; 52 | int decLen = 0; 53 | unsigned char a3[3]; 54 | unsigned char a4[4]; 55 | 56 | 57 | while (inputLen--) { 58 | if(*input == '=') { 59 | break; 60 | } 61 | 62 | a4[i++] = *(input++); 63 | if (i == 4) { 64 | for (i = 0; i <4; i++) { 65 | a4[i] = b64_lookup(a4[i]); 66 | } 67 | 68 | a4_to_a3(a3,a4); 69 | 70 | for (i = 0; i < 3; i++) { 71 | output[decLen++] = a3[i]; 72 | } 73 | i = 0; 74 | } 75 | } 76 | 77 | if (i) { 78 | for (j = i; j < 4; j++) { 79 | a4[j] = '\0'; 80 | } 81 | 82 | for (j = 0; j <4; j++) { 83 | a4[j] = b64_lookup(a4[j]); 84 | } 85 | 86 | a4_to_a3(a3,a4); 87 | 88 | for (j = 0; j < i - 1; j++) { 89 | output[decLen++] = a3[j]; 90 | } 91 | } 92 | output[decLen] = '\0'; 93 | return decLen; 94 | } 95 | 96 | int base64_enc_len(int plainLen) { 97 | int n = plainLen; 98 | return (n + 2 - ((n + 2) % 3)) / 3 * 4; 99 | } 100 | 101 | int base64_dec_len(char * input, int inputLen) { 102 | int i = 0; 103 | int numEq = 0; 104 | for(i = inputLen - 1; input[i] == '='; i--) { 105 | numEq++; 106 | } 107 | 108 | return ((6 * inputLen) / 8) - numEq; 109 | } 110 | 111 | inline void a3_to_a4(unsigned char * a4, unsigned char * a3) { 112 | a4[0] = (a3[0] & 0xfc) >> 2; 113 | a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); 114 | a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); 115 | a4[3] = (a3[2] & 0x3f); 116 | } 117 | 118 | inline void a4_to_a3(unsigned char * a3, unsigned char * a4) { 119 | a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); 120 | a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); 121 | a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; 122 | } 123 | 124 | inline unsigned char b64_lookup(char c) { 125 | int i; 126 | for(i = 0; i < 64; i++) { 127 | if(b64_alphabet[i] == c) { 128 | return i; 129 | } 130 | } 131 | 132 | return -1; 133 | } 134 | -------------------------------------------------------------------------------- /Base64.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE64_H 2 | #define _BASE64_H 3 | 4 | /* b64_alphabet: 5 | * Description: Base64 alphabet table, a mapping between integers 6 | * and base64 digits 7 | * Notes: This is an extern here but is defined in Base64.c 8 | */ 9 | extern const char b64_alphabet[]; 10 | 11 | /* base64_encode: 12 | * Description: 13 | * Encode a string of characters as base64 14 | * Parameters: 15 | * output: the output buffer for the encoding, stores the encoded string 16 | * input: the input buffer for the encoding, stores the binary to be encoded 17 | * inputLen: the length of the input buffer, in bytes 18 | * Return value: 19 | * Returns the length of the encoded string 20 | * Requirements: 21 | * 1. output must not be null or empty 22 | * 2. input must not be null 23 | * 3. inputLen must be greater than or equal to 0 24 | */ 25 | int base64_encode(char *output, char *input, int inputLen); 26 | 27 | /* base64_decode: 28 | * Description: 29 | * Decode a base64 encoded string into bytes 30 | * Parameters: 31 | * output: the output buffer for the decoding, 32 | * stores the decoded binary 33 | * input: the input buffer for the decoding, 34 | * stores the base64 string to be decoded 35 | * inputLen: the length of the input buffer, in bytes 36 | * Return value: 37 | * Returns the length of the decoded string 38 | * Requirements: 39 | * 1. output must not be null or empty 40 | * 2. input must not be null 41 | * 3. inputLen must be greater than or equal to 0 42 | */ 43 | int base64_decode(char *output, char *input, int inputLen); 44 | 45 | /* base64_enc_len: 46 | * Description: 47 | * Returns the length of a base64 encoded string whose decoded 48 | * form is inputLen bytes long 49 | * Parameters: 50 | * inputLen: the length of the decoded string 51 | * Return value: 52 | * The length of a base64 encoded string whose decoded form 53 | * is inputLen bytes long 54 | * Requirements: 55 | * None 56 | */ 57 | int base64_enc_len(int inputLen); 58 | 59 | /* base64_dec_len: 60 | * Description: 61 | * Returns the length of the decoded form of a 62 | * base64 encoded string 63 | * Parameters: 64 | * input: the base64 encoded string to be measured 65 | * inputLen: the length of the base64 encoded string 66 | * Return value: 67 | * Returns the length of the decoded form of a 68 | * base64 encoded string 69 | * Requirements: 70 | * 1. input must not be null 71 | * 2. input must be greater than or equal to zero 72 | */ 73 | int base64_dec_len(char *input, int inputLen); 74 | 75 | #endif // _BASE64_H 76 | -------------------------------------------------------------------------------- /MD5.c: -------------------------------------------------------------------------------- 1 | /* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm 2 | * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. 3 | * All rights reserved. 4 | * 5 | * License to copy and use this software is granted provided that it 6 | * is identified as the "RSA Data Security, Inc. MD5 Message-Digest 7 | * Algorithm" in all material mentioning or referencing this software 8 | * or this function. 9 | * 10 | * License is also granted to make and use derivative works provided 11 | * that such works are identified as "derived from the RSA Data 12 | * Security, Inc. MD5 Message-Digest Algorithm" in all material 13 | * mentioning or referencing the derived work. 14 | * 15 | * RSA Data Security, Inc. makes no representations concerning either 16 | * the merchantability of this software or the suitability of this 17 | * software for any particular purpose. It is provided "as is" 18 | * without express or implied warranty of any kind. 19 | * 20 | * These notices must be retained in any copies of any part of this 21 | * documentation and/or software. 22 | */ 23 | 24 | #include "global.h" 25 | #include "MD5.h" 26 | 27 | /* Constants for MD5Transform routine. */ 28 | 29 | #define S11 7 30 | #define S12 12 31 | #define S13 17 32 | #define S14 22 33 | #define S21 5 34 | #define S22 9 35 | #define S23 14 36 | #define S24 20 37 | #define S31 4 38 | #define S32 11 39 | #define S33 16 40 | #define S34 23 41 | #define S41 6 42 | #define S42 10 43 | #define S43 15 44 | #define S44 21 45 | 46 | static void MD5Transform(UINT4 [4], unsigned char [64]); 47 | static void Encode(unsigned char *, UINT4 *, unsigned int); 48 | static void Decode(UINT4 *, unsigned char *, unsigned int); 49 | static void MD5_memcpy(POINTER, POINTER, unsigned int); 50 | static void MD5_memset(POINTER, int, unsigned int); 51 | 52 | static unsigned char PADDING[64] = { 53 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 56 | }; 57 | 58 | /* F, G, H and I are basic MD5 functions. */ 59 | #define MF(x, y, z) (((x) & (y)) | ((~x) & (z))) 60 | #define MG(x, y, z) (((x) & (z)) | ((y) & (~z))) 61 | #define MH(x, y, z) ((x) ^ (y) ^ (z)) 62 | #define MI(x, y, z) ((y) ^ ((x) | (~z))) 63 | 64 | /* ROTATE_LEFT rotates x left n bits. */ 65 | #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) 66 | 67 | /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. 68 | * Rotation is separate from addition to prevent recomputation. 69 | */ 70 | #define FF(a, b, c, d, x, s, ac) { \ 71 | (a) += MF ((b), (c), (d)) + (x) + (UINT4)(ac); \ 72 | (a) = ROTATE_LEFT ((a), (s)); \ 73 | (a) += (b); \ 74 | } 75 | #define GG(a, b, c, d, x, s, ac) { \ 76 | (a) += MG ((b), (c), (d)) + (x) + (UINT4)(ac); \ 77 | (a) = ROTATE_LEFT ((a), (s)); \ 78 | (a) += (b); \ 79 | } 80 | #define HH(a, b, c, d, x, s, ac) { \ 81 | (a) += MH ((b), (c), (d)) + (x) + (UINT4)(ac); \ 82 | (a) = ROTATE_LEFT ((a), (s)); \ 83 | (a) += (b); \ 84 | } 85 | #define II(a, b, c, d, x, s, ac) { \ 86 | (a) += MI ((b), (c), (d)) + (x) + (UINT4)(ac); \ 87 | (a) = ROTATE_LEFT ((a), (s)); \ 88 | (a) += (b); \ 89 | } 90 | 91 | /* MD5 initialization. Begins an MD5 operation, writing a new context. */ 92 | void MD5Init (MD5_CTX *context) /* context */ 93 | { 94 | context->count[0] = context->count[1] = 0; 95 | /* Load magic initialization constants. */ 96 | context->state[0] = 0x67452301; 97 | context->state[1] = 0xefcdab89; 98 | context->state[2] = 0x98badcfe; 99 | context->state[3] = 0x10325476; 100 | } 101 | 102 | /* MD5 block update operation. Continues an MD5 message-digest 103 | * operation, processing another message block, and updating the 104 | * context. 105 | */ 106 | void MD5Update (MD5_CTX *context, unsigned char *input,unsigned int inputLen) /* length of input block */ 107 | { 108 | unsigned int i, index, partLen; 109 | 110 | /* Compute number of bytes mod 64 */ 111 | index = (unsigned int)((context->count[0] >> 3) & 0x3F); 112 | 113 | /* Update number of bits */ 114 | if ((context->count[0] += ((UINT4)inputLen << 3)) 115 | < ((UINT4)inputLen << 3)) 116 | context->count[1]++; 117 | context->count[1] += ((UINT4)inputLen >> 29); 118 | partLen = 64 - index; 119 | 120 | /* Transform as many times as possible. */ 121 | if (inputLen >= partLen) { 122 | MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen); 123 | MD5Transform (context->state, context->buffer); 124 | for (i = partLen; i + 63 < inputLen; i += 64) 125 | MD5Transform (context->state, &input[i]); 126 | index = 0; 127 | } 128 | else 129 | i = 0; 130 | 131 | /* Buffer remaining input */ 132 | MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], 133 | inputLen-i); 134 | } 135 | 136 | /* MD5 finalization. Ends an MD5 message-digest operation, writing the 137 | * the message digest and zeroizing the context. 138 | */ 139 | void MD5Final (unsigned char digest[16], MD5_CTX *context) 140 | { 141 | unsigned char bits[8]; 142 | unsigned int index, padLen; 143 | 144 | /* Save number of bits */ 145 | Encode (bits, context->count, 8); 146 | 147 | /* Pad out to 56 mod 64. */ 148 | index = (unsigned int)((context->count[0] >> 3) & 0x3f); 149 | padLen = (index < 56) ? (56 - index) : (120 - index); 150 | MD5Update (context, PADDING, padLen); 151 | 152 | /* Append length (before padding) */ 153 | MD5Update (context, bits, 8); 154 | 155 | /* Store state in digest */ 156 | Encode (digest, context->state, 16); 157 | 158 | /* Zeroize sensitive information. */ 159 | MD5_memset ((POINTER)context, 0, sizeof (*context)); 160 | } 161 | 162 | /* MD5 basic transformation. Transforms state based on block. */ 163 | static void MD5Transform (UINT4 state[4], unsigned char block[64]) 164 | { 165 | UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; 166 | 167 | Decode (x, block, 64); 168 | 169 | /* Round 1 */ 170 | FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ 171 | FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ 172 | FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ 173 | FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ 174 | FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ 175 | FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ 176 | FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ 177 | FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ 178 | FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ 179 | FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ 180 | FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ 181 | FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ 182 | FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ 183 | FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ 184 | FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ 185 | FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ 186 | 187 | /* Round 2 */ 188 | GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ 189 | GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ 190 | GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ 191 | GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ 192 | GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ 193 | GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ 194 | GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ 195 | GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ 196 | GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ 197 | GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ 198 | GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ 199 | GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ 200 | GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ 201 | GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ 202 | GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ 203 | GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ 204 | 205 | /* Round 3 */ 206 | HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ 207 | HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ 208 | HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ 209 | HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ 210 | HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ 211 | HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ 212 | HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ 213 | HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ 214 | HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ 215 | HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ 216 | HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ 217 | HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ 218 | HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ 219 | HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ 220 | HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ 221 | HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ 222 | 223 | /* Round 4 */ 224 | II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ 225 | II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ 226 | II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ 227 | II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ 228 | II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ 229 | II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ 230 | II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ 231 | II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ 232 | II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ 233 | II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ 234 | II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ 235 | II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ 236 | II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ 237 | II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ 238 | II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ 239 | II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ 240 | 241 | state[0] += a; 242 | state[1] += b; 243 | state[2] += c; 244 | state[3] += d; 245 | 246 | /* Zeroize sensitive information. */ 247 | MD5_memset ((POINTER)x, 0, sizeof (x)); 248 | } 249 | 250 | /* Encodes input (UINT4) into output (unsigned char). Assumes len is 251 | * a multiple of 4. 252 | */ 253 | static void Encode (unsigned char *output, UINT4 *input, unsigned int len) 254 | { 255 | unsigned int i, j; 256 | 257 | for (i = 0, j = 0; j < len; i++, j += 4) { 258 | output[j] = (unsigned char)(input[i] & 0xff); 259 | output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); 260 | output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); 261 | output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); 262 | } 263 | } 264 | 265 | /* Decodes input (unsigned char) into output (UINT4). Assumes len is 266 | * a multiple of 4. 267 | */ 268 | static void Decode (UINT4 *output, unsigned char *input, unsigned int len) 269 | { 270 | unsigned int i, j; 271 | 272 | for (i = 0, j = 0; j < len; i++, j += 4) 273 | output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | 274 | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); 275 | } 276 | 277 | /* Note: Replace "for loop" with standard memcpy if possible. */ 278 | 279 | static void MD5_memcpy (POINTER output, POINTER input, unsigned int len) 280 | { 281 | unsigned int i; 282 | 283 | for (i = 0; i < len; i++) 284 | output[i] = input[i]; 285 | } 286 | 287 | /* Note: Replace "for loop" with standard memset if possible. */ 288 | static void MD5_memset (POINTER output, int value, unsigned int len) 289 | { 290 | unsigned int i; 291 | 292 | for (i = 0; i < len; i++) 293 | ((char *)output)[i] = (char)value; 294 | } 295 | 296 | /* Calculate MD5 Digest into md5Digest */ 297 | void MD5(unsigned char strInputString[], unsigned char md5Digest[], unsigned int len){ 298 | MD5_CTX ctx; 299 | MD5Init(&ctx); 300 | 301 | MD5Update(&ctx, strInputString, len); 302 | MD5Final(md5Digest, &ctx); 303 | } -------------------------------------------------------------------------------- /MD5.h: -------------------------------------------------------------------------------- 1 | /* MD5.H - header file for MD5C.C 2 | * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 3 | * rights reserved. 4 | * 5 | * License to copy and use this software is granted provided that it 6 | * is identified as the "RSA Data Security, Inc. MD5 Message-Digest 7 | * Algorithm" in all material mentioning or referencing this software 8 | * or this function. 9 | * 10 | * License is also granted to make and use derivative works provided 11 | * that such works are identified as "derived from the RSA Data 12 | * Security, Inc. MD5 Message-Digest Algorithm" in all material 13 | * mentioning or referencing the derived work. 14 | * 15 | * RSA Data Security, Inc. makes no representations concerning either 16 | * the merchantability of this software or the suitability of this 17 | * software for any particular purpose. It is provided "as is" 18 | * without express or implied warranty of any kind. 19 | * These notices must be retained in any copies of any part of this 20 | * documentation and/or software. 21 | */ 22 | 23 | /* MD5 context. */ 24 | typedef struct { 25 | UINT4 state[4]; /* state (ABCD) */ 26 | UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ 27 | unsigned char buffer[64]; /* input buffer */ 28 | } MD5_CTX; 29 | 30 | void MD5Init (MD5_CTX *); 31 | void MD5Update (MD5_CTX *, unsigned char *, unsigned int); 32 | void MD5Final (unsigned char [16], MD5_CTX *); 33 | 34 | /* Function used by Websockets implementation */ 35 | void MD5 (unsigned char [], unsigned char [], unsigned int); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Websocket Client and Server for Arduino 2 | 3 | This is a simple library that implements a Websocket client and server running on an Arduino. 4 | 5 | ### Getting started 6 | 7 | The example WebSocketServer.html file should be served from any web server you have access to. Remember to change the URL in it to your Arduino. The examples are based on using a WiFly wireless card to connect. If you're using ethernet instead you'll need to swap out the client class. 8 | 9 | Install the library to "libraries" folder in your Arduino sketchbook folder. For example, on a mac that's `~/Documents/Arduino/libraries`. 10 | 11 | Try the examples to ensure that things work. 12 | 13 | Start playing with your own code! 14 | 15 | ### Notes 16 | Inside of the WebSocketServer class there is a compiler directive to turn on support for the older "Hixie76" standard. If you don't need it, leave it off as it greatly increases the memory required. 17 | 18 | Because of limitations of the current Arduino platform (Uno at the time of this writing), this library does not support messages larger than 65535 characters. In addition, this library only supports single-frame text frames. It currently does not recognize continuation frames, binary frames, or ping/pong frames. 19 | 20 | ### Credits 21 | Thank you to github user ejeklint for the excellent starting point for this library. From his original Hixie76-only code I was able to add support for RFC 6455 and create the WebSocket client. 22 | 23 | - Branden -------------------------------------------------------------------------------- /WebSocketClient.cpp: -------------------------------------------------------------------------------- 1 | //#define DEBUGGING 2 | 3 | #include "global.h" 4 | #include "WebSocketClient.h" 5 | 6 | #include "sha1.h" 7 | #include "base64.h" 8 | 9 | 10 | bool WebSocketClient::handshake(Client &client) { 11 | 12 | socket_client = &client; 13 | 14 | // If there is a connected client-> 15 | if (socket_client->connected()) { 16 | // Check request and look for websocket handshake 17 | #ifdef DEBUGGING 18 | Serial.println(F("Client connected")); 19 | #endif 20 | if (analyzeRequest()) { 21 | #ifdef DEBUGGING 22 | Serial.println(F("Websocket established")); 23 | #endif 24 | 25 | return true; 26 | 27 | } else { 28 | // Might just need to break until out of socket_client loop. 29 | #ifdef DEBUGGING 30 | Serial.println(F("Invalid handshake")); 31 | #endif 32 | disconnectStream(); 33 | 34 | return false; 35 | } 36 | } else { 37 | return false; 38 | } 39 | } 40 | 41 | bool WebSocketClient::analyzeRequest() { 42 | String temp; 43 | 44 | int bite; 45 | bool foundupgrade = false; 46 | unsigned long intkey[2]; 47 | String serverKey; 48 | char keyStart[17]; 49 | char b64Key[25]; 50 | String key = "------------------------"; 51 | 52 | randomSeed(analogRead(0)); 53 | 54 | for (int i=0; i<16; ++i) { 55 | keyStart[i] = (char)random(1, 256); 56 | } 57 | 58 | base64_encode(b64Key, keyStart, 16); 59 | 60 | for (int i=0; i<24; ++i) { 61 | key[i] = b64Key[i]; 62 | } 63 | 64 | #ifdef DEBUGGING 65 | Serial.println(F("Sending websocket upgrade headers")); 66 | #endif 67 | 68 | socket_client->print(F("GET ")); 69 | socket_client->print(path); 70 | socket_client->print(F(" HTTP/1.1\r\n")); 71 | socket_client->print(F("Upgrade: websocket\r\n")); 72 | socket_client->print(F("Connection: Upgrade\r\n")); 73 | socket_client->print(F("Host: ")); 74 | socket_client->print(host); 75 | socket_client->print(CRLF); 76 | socket_client->print(F("Sec-WebSocket-Key: ")); 77 | socket_client->print(key); 78 | socket_client->print(CRLF); 79 | socket_client->print(F("Sec-WebSocket-Protocol: ")); 80 | socket_client->print(protocol); 81 | socket_client->print(CRLF); 82 | socket_client->print(F("Sec-WebSocket-Version: 13\r\n")); 83 | socket_client->print(CRLF); 84 | 85 | #ifdef DEBUGGING 86 | Serial.println(F("Analyzing response headers")); 87 | #endif 88 | 89 | while (socket_client->connected() && !socket_client->available()) { 90 | delay(100); 91 | Serial.println("Waiting..."); 92 | } 93 | 94 | // TODO: More robust string extraction 95 | while ((bite = socket_client->read()) != -1) { 96 | 97 | temp += (char)bite; 98 | 99 | if ((char)bite == '\n') { 100 | #ifdef DEBUGGING 101 | Serial.print("Got Header: " + temp); 102 | #endif 103 | if (!foundupgrade && temp.startsWith("Upgrade: websocket")) { 104 | foundupgrade = true; 105 | } else if (temp.startsWith("Sec-WebSocket-Accept: ")) { 106 | serverKey = temp.substring(22,temp.length() - 2); // Don't save last CR+LF 107 | } 108 | temp = ""; 109 | } 110 | 111 | if (!socket_client->available()) { 112 | delay(20); 113 | } 114 | } 115 | 116 | key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 117 | uint8_t *hash; 118 | char result[21]; 119 | char b64Result[30]; 120 | 121 | Sha1.init(); 122 | Sha1.print(key); 123 | hash = Sha1.result(); 124 | 125 | for (int i=0; i<20; ++i) { 126 | result[i] = (char)hash[i]; 127 | } 128 | result[20] = '\0'; 129 | 130 | base64_encode(b64Result, result, 20); 131 | 132 | // if the keys match, good to go 133 | return serverKey.equals(String(b64Result)); 134 | } 135 | 136 | 137 | bool WebSocketClient::handleStream(String& data, uint8_t *opcode) { 138 | uint8_t msgtype; 139 | uint8_t bite; 140 | unsigned int length; 141 | uint8_t mask[4]; 142 | uint8_t index; 143 | unsigned int i; 144 | bool hasMask = false; 145 | 146 | if (!socket_client->connected() || !socket_client->available()) 147 | { 148 | return false; 149 | } 150 | 151 | msgtype = timedRead(); 152 | if (!socket_client->connected()) { 153 | return false; 154 | } 155 | 156 | length = timedRead(); 157 | 158 | if (length & WS_MASK) { 159 | hasMask = true; 160 | length = length & ~WS_MASK; 161 | } 162 | 163 | 164 | if (!socket_client->connected()) { 165 | return false; 166 | } 167 | 168 | index = 6; 169 | 170 | if (length == WS_SIZE16) { 171 | length = timedRead() << 8; 172 | if (!socket_client->connected()) { 173 | return false; 174 | } 175 | 176 | length |= timedRead(); 177 | if (!socket_client->connected()) { 178 | return false; 179 | } 180 | 181 | } else if (length == WS_SIZE64) { 182 | #ifdef DEBUGGING 183 | Serial.println(F("No support for over 16 bit sized messages")); 184 | #endif 185 | return false; 186 | } 187 | 188 | if (hasMask) { 189 | // get the mask 190 | mask[0] = timedRead(); 191 | if (!socket_client->connected()) { 192 | return false; 193 | } 194 | 195 | mask[1] = timedRead(); 196 | if (!socket_client->connected()) { 197 | 198 | return false; 199 | } 200 | 201 | mask[2] = timedRead(); 202 | if (!socket_client->connected()) { 203 | return false; 204 | } 205 | 206 | mask[3] = timedRead(); 207 | if (!socket_client->connected()) { 208 | return false; 209 | } 210 | } 211 | 212 | data = ""; 213 | 214 | if (opcode != NULL) 215 | { 216 | *opcode = msgtype & ~WS_FIN; 217 | } 218 | 219 | if (hasMask) { 220 | for (i=0; iconnected()) { 223 | return false; 224 | } 225 | } 226 | } else { 227 | for (i=0; iconnected()) { 230 | return false; 231 | } 232 | } 233 | } 234 | 235 | return true; 236 | } 237 | 238 | void WebSocketClient::disconnectStream() { 239 | #ifdef DEBUGGING 240 | Serial.println(F("Terminating socket")); 241 | #endif 242 | // Should send 0x8700 to server to tell it I'm quitting here. 243 | socket_client->write((uint8_t) 0x87); 244 | socket_client->write((uint8_t) 0x00); 245 | 246 | socket_client->flush(); 247 | delay(10); 248 | socket_client->stop(); 249 | } 250 | 251 | bool WebSocketClient::getData(String& data, uint8_t *opcode) { 252 | return handleStream(data, opcode); 253 | } 254 | 255 | void WebSocketClient::sendData(const char *str, uint8_t opcode) { 256 | #ifdef DEBUGGING 257 | Serial.print(F("Sending data: ")); 258 | Serial.println(str); 259 | #endif 260 | if (socket_client->connected()) { 261 | sendEncodedData(str, opcode); 262 | } 263 | } 264 | 265 | void WebSocketClient::sendData(String str, uint8_t opcode) { 266 | #ifdef DEBUGGING 267 | Serial.print(F("Sending data: ")); 268 | Serial.println(str); 269 | #endif 270 | if (socket_client->connected()) { 271 | sendEncodedData(str, opcode); 272 | } 273 | } 274 | 275 | int WebSocketClient::timedRead() { 276 | while (!socket_client->available()) { 277 | delay(20); 278 | } 279 | 280 | return socket_client->read(); 281 | } 282 | 283 | void WebSocketClient::sendEncodedData(char *str, uint8_t opcode) { 284 | uint8_t mask[4]; 285 | int size = strlen(str); 286 | 287 | // Opcode; final fragment 288 | socket_client->write(opcode | WS_FIN); 289 | 290 | // NOTE: no support for > 16-bit sized messages 291 | if (size > 125) { 292 | socket_client->write(WS_SIZE16 | WS_MASK); 293 | socket_client->write((uint8_t) (size >> 8)); 294 | socket_client->write((uint8_t) (size & 0xFF)); 295 | } else { 296 | socket_client->write((uint8_t) size | WS_MASK); 297 | } 298 | 299 | mask[0] = random(0, 256); 300 | mask[1] = random(0, 256); 301 | mask[2] = random(0, 256); 302 | mask[3] = random(0, 256); 303 | 304 | socket_client->write(mask[0]); 305 | socket_client->write(mask[1]); 306 | socket_client->write(mask[2]); 307 | socket_client->write(mask[3]); 308 | 309 | for (int i=0; iwrite(str[i] ^ mask[i % 4]); 311 | } 312 | } 313 | 314 | void WebSocketClient::sendEncodedData(String str, uint8_t opcode) { 315 | int size = str.length() + 1; 316 | char cstr[size]; 317 | 318 | str.toCharArray(cstr, size); 319 | 320 | sendEncodedData(cstr, opcode); 321 | } 322 | -------------------------------------------------------------------------------- /WebSocketClient.h: -------------------------------------------------------------------------------- 1 | /* 2 | Websocket-Arduino, a websocket implementation for Arduino 3 | Copyright 2011 Per Ejeklint 4 | 5 | Based on previous implementations by 6 | Copyright 2010 Ben Swanson 7 | and 8 | Copyright 2010 Randall Brewer 9 | and 10 | Copyright 2010 Oliver Smith 11 | 12 | Some code and concept based off of Webduino library 13 | Copyright 2009 Ben Combee, Ran Talbott 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | 33 | ------------- 34 | Now based off 35 | http://www.whatwg.org/specs/web-socket-protocol/ 36 | 37 | - OLD - 38 | Currently based off of "The Web Socket protocol" draft (v 75): 39 | http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 40 | */ 41 | 42 | 43 | #ifndef WEBSOCKETCLIENT_H_ 44 | #define WEBSOCKETCLIENT_H_ 45 | 46 | #include 47 | #include 48 | #include "String.h" 49 | #include "Client.h" 50 | 51 | // CRLF characters to terminate lines/handshakes in headers. 52 | #define CRLF "\r\n" 53 | 54 | // Amount of time (in ms) a user may be connected before getting disconnected 55 | // for timing out (i.e. not sending any data to the server). 56 | #define TIMEOUT_IN_MS 10000 57 | 58 | // ACTION_SPACE is how many actions are allowed in a program. Defaults to 59 | // 5 unless overwritten by user. 60 | #ifndef CALLBACK_FUNCTIONS 61 | #define CALLBACK_FUNCTIONS 1 62 | #endif 63 | 64 | // Don't allow the client to send big frames of data. This will flood the Arduinos 65 | // memory and might even crash it. 66 | #ifndef MAX_FRAME_LENGTH 67 | #define MAX_FRAME_LENGTH 256 68 | #endif 69 | 70 | #define SIZE(array) (sizeof(array) / sizeof(*array)) 71 | 72 | // WebSocket protocol constants 73 | // First byte 74 | #define WS_FIN 0x80 75 | #define WS_OPCODE_TEXT 0x01 76 | #define WS_OPCODE_BINARY 0x02 77 | #define WS_OPCODE_CLOSE 0x08 78 | #define WS_OPCODE_PING 0x09 79 | #define WS_OPCODE_PONG 0x0a 80 | // Second byte 81 | #define WS_MASK 0x80 82 | #define WS_SIZE16 126 83 | #define WS_SIZE64 127 84 | 85 | 86 | class WebSocketClient { 87 | public: 88 | 89 | // Handle connection requests to validate and process/refuse 90 | // connections. 91 | bool handshake(Client &client); 92 | 93 | // Get data off of the stream 94 | bool getData(String& data, uint8_t *opcode = NULL); 95 | 96 | // Write data to the stream 97 | void sendData(const char *str, uint8_t opcode = WS_OPCODE_TEXT); 98 | void sendData(String str, uint8_t opcode = WS_OPCODE_TEXT); 99 | 100 | char *path; 101 | char *host; 102 | char *protocol; 103 | 104 | private: 105 | Client *socket_client; 106 | unsigned long _startMillis; 107 | 108 | const char *socket_urlPrefix; 109 | 110 | // Discovers if the client's header is requesting an upgrade to a 111 | // websocket connection. 112 | bool analyzeRequest(); 113 | 114 | bool handleStream(String& data, uint8_t *opcode); 115 | 116 | // Disconnect user gracefully. 117 | void disconnectStream(); 118 | 119 | int timedRead(); 120 | 121 | void sendEncodedData(char *str, uint8_t opcode); 122 | void sendEncodedData(String str, uint8_t opcode); 123 | }; 124 | 125 | 126 | 127 | #endif -------------------------------------------------------------------------------- /WebSocketServer.cpp: -------------------------------------------------------------------------------- 1 | //#define DEBUGGING 2 | //#define SUPPORT_HIXIE_76 3 | 4 | #include "global.h" 5 | #include "WebSocketServer.h" 6 | 7 | #ifdef SUPPORT_HIXIE_76 8 | #include "MD5.c" 9 | #endif 10 | 11 | #include "sha1.h" 12 | #include "base64.h" 13 | 14 | 15 | bool WebSocketServer::handshake(Client &client) { 16 | 17 | socket_client = &client; 18 | 19 | // If there is a connected client-> 20 | if (socket_client->connected()) { 21 | // Check request and look for websocket handshake 22 | #ifdef DEBUGGING 23 | Serial.println(F("Client connected")); 24 | #endif 25 | if (analyzeRequest(BUFFER_LENGTH)) { 26 | #ifdef DEBUGGING 27 | Serial.println(F("Websocket established")); 28 | #endif 29 | 30 | return true; 31 | 32 | } else { 33 | // Might just need to break until out of socket_client loop. 34 | #ifdef DEBUGGING 35 | Serial.println(F("Disconnecting client")); 36 | #endif 37 | disconnectStream(); 38 | 39 | return false; 40 | } 41 | } else { 42 | return false; 43 | } 44 | } 45 | 46 | bool WebSocketServer::analyzeRequest(int bufferLength) { 47 | // Use String library to do some sort of read() magic here. 48 | String temp; 49 | 50 | int bite; 51 | bool foundupgrade = false; 52 | String oldkey[2]; 53 | unsigned long intkey[2]; 54 | String newkey; 55 | 56 | hixie76style = false; 57 | 58 | #ifdef DEBUGGING 59 | Serial.println(F("Analyzing request headers")); 60 | #endif 61 | 62 | // TODO: More robust string extraction 63 | while ((bite = socket_client->read()) != -1) { 64 | 65 | temp += (char)bite; 66 | 67 | if ((char)bite == '\n') { 68 | #ifdef DEBUGGING 69 | Serial.print("Got Line: " + temp); 70 | #endif 71 | // TODO: Should ignore case when comparing and allow 0-n whitespace after ':'. See the spec: 72 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html 73 | if (!foundupgrade && temp.startsWith("Upgrade: WebSocket")) { 74 | // OK, it's a websockets handshake for sure 75 | foundupgrade = true; 76 | hixie76style = true; 77 | } else if (!foundupgrade && temp.startsWith("Upgrade: websocket")) { 78 | foundupgrade = true; 79 | hixie76style = false; 80 | } else if (temp.startsWith("Origin: ")) { 81 | origin = temp.substring(8,temp.length() - 2); // Don't save last CR+LF 82 | } else if (temp.startsWith("Host: ")) { 83 | host = temp.substring(6,temp.length() - 2); // Don't save last CR+LF 84 | } else if (temp.startsWith("Sec-WebSocket-Key1: ")) { 85 | oldkey[0]=temp.substring(20,temp.length() - 2); // Don't save last CR+LF 86 | } else if (temp.startsWith("Sec-WebSocket-Key2: ")) { 87 | oldkey[1]=temp.substring(20,temp.length() - 2); // Don't save last CR+LF 88 | } else if (temp.startsWith("Sec-WebSocket-Key: ")) { 89 | newkey=temp.substring(19,temp.length() - 2); // Don't save last CR+LF 90 | } 91 | temp = ""; 92 | } 93 | 94 | if (!socket_client->available()) { 95 | delay(20); 96 | } 97 | } 98 | 99 | if (!socket_client->connected()) { 100 | return false; 101 | } 102 | 103 | temp += 0; // Terminate string 104 | 105 | // Assert that we have all headers that are needed. If so, go ahead and 106 | // send response headers. 107 | if (foundupgrade == true) { 108 | 109 | #ifdef SUPPORT_HIXIE_76 110 | if (hixie76style && host.length() > 0 && oldkey[0].length() > 0 && oldkey[1].length() > 0) { 111 | // All ok, proceed with challenge and MD5 digest 112 | char key3[9] = {0}; 113 | // What now is in temp should be the third key 114 | temp.toCharArray(key3, 9); 115 | 116 | // Process keys 117 | for (int i = 0; i <= 1; i++) { 118 | unsigned int spaces =0; 119 | String numbers; 120 | 121 | for (int c = 0; c < oldkey[i].length(); c++) { 122 | char ac = oldkey[i].charAt(c); 123 | if (ac >= '0' && ac <= '9') { 124 | numbers += ac; 125 | } 126 | if (ac == ' ') { 127 | spaces++; 128 | } 129 | } 130 | char numberschar[numbers.length() + 1]; 131 | numbers.toCharArray(numberschar, numbers.length()+1); 132 | intkey[i] = strtoul(numberschar, NULL, 10) / spaces; 133 | } 134 | 135 | unsigned char challenge[16] = {0}; 136 | challenge[0] = (unsigned char) ((intkey[0] >> 24) & 0xFF); 137 | challenge[1] = (unsigned char) ((intkey[0] >> 16) & 0xFF); 138 | challenge[2] = (unsigned char) ((intkey[0] >> 8) & 0xFF); 139 | challenge[3] = (unsigned char) ((intkey[0] ) & 0xFF); 140 | challenge[4] = (unsigned char) ((intkey[1] >> 24) & 0xFF); 141 | challenge[5] = (unsigned char) ((intkey[1] >> 16) & 0xFF); 142 | challenge[6] = (unsigned char) ((intkey[1] >> 8) & 0xFF); 143 | challenge[7] = (unsigned char) ((intkey[1] ) & 0xFF); 144 | 145 | memcpy(challenge + 8, key3, 8); 146 | 147 | unsigned char md5Digest[16]; 148 | MD5(challenge, md5Digest, 16); 149 | 150 | socket_client->print(F("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")); 151 | socket_client->print(F("Upgrade: WebSocket\r\n")); 152 | socket_client->print(F("Connection: Upgrade\r\n")); 153 | socket_client->print(F("Sec-WebSocket-Origin: ")); 154 | socket_client->print(origin); 155 | socket_client->print(CRLF); 156 | 157 | // The "Host:" value should be used as location 158 | socket_client->print(F("Sec-WebSocket-Location: ws://")); 159 | socket_client->print(host); 160 | socket_client->print(socket_urlPrefix); 161 | socket_client->print(CRLF); 162 | socket_client->print(CRLF); 163 | 164 | socket_client->write(md5Digest, 16); 165 | 166 | return true; 167 | } 168 | #endif 169 | 170 | if (!hixie76style && newkey.length() > 0) { 171 | 172 | // add the magic string 173 | newkey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 174 | 175 | uint8_t *hash; 176 | char result[21]; 177 | char b64Result[30]; 178 | 179 | Sha1.init(); 180 | Sha1.print(newkey); 181 | hash = Sha1.result(); 182 | 183 | for (int i=0; i<20; ++i) { 184 | result[i] = (char)hash[i]; 185 | } 186 | result[20] = '\0'; 187 | 188 | base64_encode(b64Result, result, 20); 189 | 190 | socket_client->print(F("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")); 191 | socket_client->print(F("Upgrade: websocket\r\n")); 192 | socket_client->print(F("Connection: Upgrade\r\n")); 193 | socket_client->print(F("Sec-WebSocket-Accept: ")); 194 | socket_client->print(b64Result); 195 | socket_client->print(CRLF); 196 | socket_client->print(CRLF); 197 | 198 | return true; 199 | } else { 200 | // something went horribly wrong 201 | return false; 202 | } 203 | } else { 204 | // Nope, failed handshake. Disconnect 205 | #ifdef DEBUGGING 206 | Serial.println(F("Header mismatch")); 207 | #endif 208 | return false; 209 | } 210 | } 211 | 212 | #ifdef SUPPORT_HIXIE_76 213 | String WebSocketServer::handleHixie76Stream() { 214 | int bite; 215 | int frameLength = 0; 216 | // String to hold bytes sent by client to server. 217 | String socketString; 218 | 219 | if (socket_client->connected() && socket_client->available()) { 220 | bite = timedRead(); 221 | 222 | if (bite != -1) { 223 | if (bite == 0) 224 | continue; // Frame start, don't save 225 | 226 | if ((uint8_t) bite == 0xFF) { 227 | // Frame end. Process what we got. 228 | return socketString; 229 | 230 | } else { 231 | socketString += (char)bite; 232 | frameLength++; 233 | 234 | if (frameLength > MAX_FRAME_LENGTH) { 235 | // Too big to handle! 236 | #ifdef DEBUGGING 237 | Serial.print("Client send frame exceeding "); 238 | Serial.print(MAX_FRAME_LENGTH); 239 | Serial.println(" bytes"); 240 | #endif 241 | return; 242 | } 243 | } 244 | } 245 | } 246 | 247 | return socketString; 248 | } 249 | 250 | #endif 251 | 252 | String WebSocketServer::handleStream() { 253 | uint8_t msgtype; 254 | uint8_t bite; 255 | unsigned int length; 256 | uint8_t mask[4]; 257 | uint8_t index; 258 | unsigned int i; 259 | 260 | // String to hold bytes sent by client to server. 261 | String socketString; 262 | 263 | if (socket_client->connected() && socket_client->available()) { 264 | 265 | msgtype = timedRead(); 266 | if (!socket_client->connected()) { 267 | return socketString; 268 | } 269 | 270 | length = timedRead() & 127; 271 | if (!socket_client->connected()) { 272 | return socketString; 273 | } 274 | 275 | index = 6; 276 | 277 | if (length == 126) { 278 | length = timedRead() << 8; 279 | if (!socket_client->connected()) { 280 | return socketString; 281 | } 282 | 283 | length |= timedRead(); 284 | if (!socket_client->connected()) { 285 | return socketString; 286 | } 287 | 288 | } else if (length == 127) { 289 | #ifdef DEBUGGING 290 | Serial.println(F("No support for over 16 bit sized messages")); 291 | #endif 292 | while(1) { 293 | // halt, can't handle this case 294 | } 295 | } 296 | 297 | // get the mask 298 | mask[0] = timedRead(); 299 | if (!socket_client->connected()) { 300 | return socketString; 301 | } 302 | 303 | mask[1] = timedRead(); 304 | if (!socket_client->connected()) { 305 | 306 | return socketString; 307 | } 308 | 309 | mask[2] = timedRead(); 310 | if (!socket_client->connected()) { 311 | return socketString; 312 | } 313 | 314 | mask[3] = timedRead(); 315 | if (!socket_client->connected()) { 316 | return socketString; 317 | } 318 | 319 | for (i=0; iconnected()) { 322 | return socketString; 323 | } 324 | } 325 | } 326 | 327 | return socketString; 328 | } 329 | 330 | void WebSocketServer::disconnectStream() { 331 | #ifdef DEBUGGING 332 | Serial.println(F("Terminating socket")); 333 | #endif 334 | 335 | if (hixie76style) { 336 | #ifdef SUPPORT_HIXIE_76 337 | // Should send 0xFF00 to server to tell it I'm quitting here. 338 | socket_client->write((uint8_t) 0xFF); 339 | socket_client->write((uint8_t) 0x00); 340 | #endif 341 | } else { 342 | 343 | // Should send 0x8700 to server to tell it I'm quitting here. 344 | socket_client->write((uint8_t) 0x87); 345 | socket_client->write((uint8_t) 0x00); 346 | } 347 | 348 | socket_client->flush(); 349 | delay(10); 350 | socket_client->stop(); 351 | } 352 | 353 | String WebSocketServer::getData() { 354 | String data; 355 | 356 | if (hixie76style) { 357 | #ifdef SUPPORT_HIXIE_76 358 | data = handleHixie76Stream(); 359 | #endif 360 | } else { 361 | data = handleStream(); 362 | } 363 | 364 | return data; 365 | } 366 | 367 | void WebSocketServer::sendData(const char *str) { 368 | #ifdef DEBUGGING 369 | Serial.print(F("Sending data: ")); 370 | Serial.println(str); 371 | #endif 372 | if (socket_client->connected()) { 373 | if (hixie76style) { 374 | socket_client->write(0x00); // Frame start 375 | socket_client->print(str); 376 | socket_client->write(0xFF); // Frame end 377 | } else { 378 | sendEncodedData(str); 379 | } 380 | } 381 | } 382 | 383 | void WebSocketServer::sendData(String str) { 384 | #ifdef DEBUGGING 385 | Serial.print(F("Sending data: ")); 386 | Serial.println(str); 387 | #endif 388 | if (socket_client->connected()) { 389 | if (hixie76style) { 390 | socket_client->write(0x00); // Frame start 391 | socket_client->print(str); 392 | socket_client->write(0xFF); // Frame end 393 | } else { 394 | sendEncodedData(str); 395 | } 396 | } 397 | } 398 | 399 | int WebSocketServer::timedRead() { 400 | while (!socket_client->available()) { 401 | delay(20); 402 | } 403 | 404 | return socket_client->read(); 405 | } 406 | 407 | void WebSocketServer::sendEncodedData(char *str) { 408 | int size = strlen(str); 409 | 410 | // string type 411 | socket_client->write(0x81); 412 | 413 | // NOTE: no support for > 16-bit sized messages 414 | if (size > 125) { 415 | socket_client->write(126); 416 | socket_client->write((uint8_t) (size >> 8)); 417 | socket_client->write((uint8_t) (size && 0xFF)); 418 | } else { 419 | socket_client->write((uint8_t) size); 420 | } 421 | 422 | for (int i=0; iwrite(str[i]); 424 | } 425 | } 426 | 427 | void WebSocketServer::sendEncodedData(String str) { 428 | int size = str.length() + 1; 429 | char cstr[size]; 430 | 431 | str.toCharArray(cstr, size); 432 | 433 | sendEncodedData(cstr); 434 | } 435 | -------------------------------------------------------------------------------- /WebSocketServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Websocket-Arduino, a websocket implementation for Arduino 3 | Copyright 2011 Per Ejeklint 4 | 5 | Based on previous implementations by 6 | Copyright 2010 Ben Swanson 7 | and 8 | Copyright 2010 Randall Brewer 9 | and 10 | Copyright 2010 Oliver Smith 11 | 12 | Some code and concept based off of Webduino library 13 | Copyright 2009 Ben Combee, Ran Talbott 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | 33 | ------------- 34 | Now based off 35 | http://www.whatwg.org/specs/web-socket-protocol/ 36 | 37 | - OLD - 38 | Currently based off of "The Web Socket protocol" draft (v 75): 39 | http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 40 | */ 41 | 42 | 43 | #ifndef WEBSOCKETSERVER_H_ 44 | #define WEBSOCKETSERVER_H_ 45 | 46 | #include 47 | #include 48 | #include "String.h" 49 | #include "Server.h" 50 | #include "Client.h" 51 | 52 | // CRLF characters to terminate lines/handshakes in headers. 53 | #define CRLF "\r\n" 54 | 55 | // Amount of time (in ms) a user may be connected before getting disconnected 56 | // for timing out (i.e. not sending any data to the server). 57 | #define TIMEOUT_IN_MS 10000 58 | #define BUFFER_LENGTH 32 59 | 60 | // ACTION_SPACE is how many actions are allowed in a program. Defaults to 61 | // 5 unless overwritten by user. 62 | #ifndef CALLBACK_FUNCTIONS 63 | #define CALLBACK_FUNCTIONS 1 64 | #endif 65 | 66 | // Don't allow the client to send big frames of data. This will flood the Arduinos 67 | // memory and might even crash it. 68 | #ifndef MAX_FRAME_LENGTH 69 | #define MAX_FRAME_LENGTH 256 70 | #endif 71 | 72 | #define SIZE(array) (sizeof(array) / sizeof(*array)) 73 | 74 | class WebSocketServer { 75 | public: 76 | 77 | // Handle connection requests to validate and process/refuse 78 | // connections. 79 | bool handshake(Client &client); 80 | 81 | // Get data off of the stream 82 | String getData(); 83 | 84 | // Write data to the stream 85 | void sendData(const char *str); 86 | void sendData(String str); 87 | 88 | private: 89 | Client *socket_client; 90 | unsigned long _startMillis; 91 | 92 | const char *socket_urlPrefix; 93 | 94 | String origin; 95 | String host; 96 | bool hixie76style; 97 | 98 | // Discovers if the client's header is requesting an upgrade to a 99 | // websocket connection. 100 | bool analyzeRequest(int bufferLength); 101 | 102 | #ifdef SUPPORT_HIXIE_76 103 | String handleHixie76Stream(); 104 | #endif 105 | String handleStream(); 106 | 107 | // Disconnect user gracefully. 108 | void disconnectStream(); 109 | 110 | int timedRead(); 111 | 112 | void sendEncodedData(char *str); 113 | void sendEncodedData(String str); 114 | }; 115 | 116 | 117 | 118 | #endif -------------------------------------------------------------------------------- /examples/WebSocketClient_Demo/WebSocketClient_Demo.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Here we define a maximum framelength to 64 bytes. Default is 256. 6 | #define MAX_FRAME_LENGTH 64 7 | 8 | // Define how many callback functions you have. Default is 1. 9 | #define CALLBACK_FUNCTIONS 1 10 | 11 | #include 12 | 13 | WiFlyClient client = WiFlyClient(); 14 | WebSocketClient webSocketClient; 15 | 16 | void setup() { 17 | 18 | 19 | Serial.begin(9600); 20 | SC16IS750.begin(); 21 | 22 | WiFly.setUart(&SC16IS750); 23 | 24 | WiFly.begin(); 25 | 26 | // This is for an unsecured network 27 | // For a WPA1/2 network use auth 3, and in another command send 'set wlan phrase PASSWORD' 28 | // For a WEP network use auth 2, and in another command send 'set wlan key KEY' 29 | WiFly.sendCommand(F("set wlan auth 1")); 30 | WiFly.sendCommand(F("set wlan channel 0")); 31 | WiFly.sendCommand(F("set ip dhcp 1")); 32 | 33 | Serial.println(F("Joining WiFi network...")); 34 | 35 | 36 | // Here is where you set the network name to join 37 | if (!WiFly.sendCommand(F("join arduino_wifi"), "Associated!", 20000, false)) { 38 | Serial.println(F("Association failed.")); 39 | while (1) { 40 | // Hang on failure. 41 | } 42 | } 43 | 44 | if (!WiFly.waitForResponse("DHCP in", 10000)) { 45 | Serial.println(F("DHCP failed.")); 46 | while (1) { 47 | // Hang on failure. 48 | } 49 | } 50 | 51 | // This is how you get the local IP as an IPAddress object 52 | Serial.println(WiFly.localIP()); 53 | 54 | // This delay is needed to let the WiFly respond properly 55 | delay(100); 56 | 57 | // Connect to the websocket server 58 | if (client.connect("echo.websocket.org", 80)) { 59 | Serial.println("Connected"); 60 | } else { 61 | Serial.println("Connection failed."); 62 | while(1) { 63 | // Hang on failure 64 | } 65 | } 66 | 67 | // Handshake with the server 68 | webSocketClient.path = "/"; 69 | webSocketClient.host = "echo.websocket.org"; 70 | 71 | if (webSocketClient.handshake(client)) { 72 | Serial.println("Handshake successful"); 73 | } else { 74 | Serial.println("Handshake failed."); 75 | while(1) { 76 | // Hang on failure 77 | } 78 | } 79 | } 80 | 81 | void loop() { 82 | String data; 83 | 84 | if (client.connected()) { 85 | 86 | data = webSocketClient.getData(); 87 | 88 | if (data.length() > 0) { 89 | Serial.print("Received data: "); 90 | Serial.println(data); 91 | } 92 | 93 | // capture the value of analog 1, send it along 94 | pinMode(1, INPUT); 95 | data = String(analogRead(1)); 96 | 97 | webSocketClient.sendData(data); 98 | 99 | } else { 100 | 101 | Serial.println("Client disconnected."); 102 | while (1) { 103 | // Hang on disconnect. 104 | } 105 | } 106 | 107 | // wait to fully let the client disconnect 108 | delay(3000); 109 | } 110 | -------------------------------------------------------------------------------- /examples/WebSocketServer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebSocket Test 7 | 8 | 28 | 29 | 89 | 90 | 91 |

92 | WebSocket Test 93 |

94 | Pin 8 95 | Pin 9 96 | 97 |
Pin 1
98 |
Pin 2
99 |
Pin 3
100 | 101 | 102 | -------------------------------------------------------------------------------- /examples/WebSocketServer_Demo/WebSocketServer_Demo.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Enabe debug tracing to Serial port. 6 | #define DEBUGGING 7 | 8 | // Here we define a maximum framelength to 64 bytes. Default is 256. 9 | #define MAX_FRAME_LENGTH 64 10 | 11 | // Define how many callback functions you have. Default is 1. 12 | #define CALLBACK_FUNCTIONS 1 13 | 14 | #include 15 | 16 | WiFlyServer server(80); 17 | WebSocketServer webSocketServer; 18 | 19 | 20 | // Called when a new message from the WebSocket is received 21 | // Looks for a message in this form: 22 | // 23 | // DPV 24 | // 25 | // Where: 26 | // D is either 'd' or 'a' - digital or analog 27 | // P is a pin # 28 | // V is the value to apply to the pin 29 | // 30 | 31 | void handleClientData(String &dataString) { 32 | bool isDigital = dataString[0] == 'd'; 33 | int pin = dataString[1] - '0'; 34 | int value; 35 | 36 | value = dataString[2] - '0'; 37 | 38 | 39 | pinMode(pin, OUTPUT); 40 | 41 | if (isDigital) { 42 | digitalWrite(pin, value); 43 | } else { 44 | analogWrite(pin, value); 45 | } 46 | 47 | Serial.println(dataString); 48 | } 49 | 50 | // send the client the analog value of a pin 51 | void sendClientData(int pin) { 52 | String data = "a"; 53 | 54 | pinMode(pin, INPUT); 55 | data += String(pin) + String(analogRead(pin)); 56 | webSocketServer.sendData(data); 57 | } 58 | 59 | void setup() { 60 | 61 | 62 | Serial.begin(9600); 63 | SC16IS750.begin(); 64 | 65 | WiFly.setUart(&SC16IS750); 66 | 67 | WiFly.begin(); 68 | 69 | // This is for an unsecured network 70 | // For a WPA1/2 network use auth 3, and in another command send 'set wlan phrase PASSWORD' 71 | // For a WEP network use auth 2, and in another command send 'set wlan key KEY' 72 | WiFly.sendCommand(F("set wlan auth 1")); 73 | WiFly.sendCommand(F("set wlan channel 0")); 74 | WiFly.sendCommand(F("set ip dhcp 1")); 75 | 76 | server.begin(); 77 | Serial.println(F("Joining WiFi network...")); 78 | 79 | 80 | // Here is where you set the network name to join 81 | if (!WiFly.sendCommand(F("join arduino_wifi"), "Associated!", 20000, false)) { 82 | Serial.println(F("Association failed.")); 83 | while (1) { 84 | // Hang on failure. 85 | } 86 | } 87 | 88 | if (!WiFly.waitForResponse("DHCP in", 10000)) { 89 | Serial.println(F("DHCP failed.")); 90 | while (1) { 91 | // Hang on failure. 92 | } 93 | } 94 | 95 | // This is how you get the local IP as an IPAddress object 96 | Serial.println(WiFly.localIP()); 97 | 98 | // This delay is needed to let the WiFly respond properly 99 | delay(100); 100 | } 101 | 102 | void loop() { 103 | String data; 104 | WiFlyClient client = server.available(); 105 | 106 | if (client.connected() && webSocketServer.handshake(client)) { 107 | 108 | while (client.connected()) { 109 | data = webSocketServer.getData(); 110 | 111 | if (data.length() > 0) { 112 | handleClientData(data); 113 | } 114 | 115 | sendClientData(1); 116 | sendClientData(2); 117 | sendClientData(3); 118 | } 119 | } 120 | 121 | // wait to fully let the client disconnect 122 | delay(100); 123 | } 124 | -------------------------------------------------------------------------------- /global.h: -------------------------------------------------------------------------------- 1 | /* GLOBAL.H - RSAREF types and constants */ 2 | 3 | /* PROTOTYPES should be set to one if and only if the compiler 4 | * supports function argument prototyping. 5 | * The following makes PROTOTYPES default to 0 if it has not already 6 | * been defined with C compiler flags. 7 | */ 8 | #ifndef PROTOTYPES 9 | #define PROTOTYPES 0 10 | #endif 11 | 12 | /*Modified by MMoore http://mikestechspot.blogspot.com 13 | Changed typedefs to be fully compatible w/ Arduino 08/09/2010 */ 14 | 15 | /* POINTER defines a generic pointer type */ 16 | typedef unsigned char *POINTER; 17 | 18 | /* UINT2 defines a two byte word */ 19 | typedef unsigned int UINT2; 20 | 21 | /* UINT4 defines a four byte word */ 22 | typedef unsigned long UINT4; 23 | 24 | /* PROTO_LIST is defined depending on how PROTOTYPES is defined above. 25 | * If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it 26 | * returns an empty list. 27 | */ 28 | #if PROTOTYPES 29 | #define PROTO_LIST(list) list 30 | #else 31 | #define PROTO_LIST(list) () 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /sha1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "sha1.h" 5 | 6 | #define SHA1_K0 0x5a827999 7 | #define SHA1_K20 0x6ed9eba1 8 | #define SHA1_K40 0x8f1bbcdc 9 | #define SHA1_K60 0xca62c1d6 10 | 11 | uint8_t sha1InitState[] PROGMEM = { 12 | 0x01,0x23,0x45,0x67, // H0 13 | 0x89,0xab,0xcd,0xef, // H1 14 | 0xfe,0xdc,0xba,0x98, // H2 15 | 0x76,0x54,0x32,0x10, // H3 16 | 0xf0,0xe1,0xd2,0xc3 // H4 17 | }; 18 | 19 | void Sha1Class::init(void) { 20 | memcpy_P(state.b,sha1InitState,HASH_LENGTH); 21 | byteCount = 0; 22 | bufferOffset = 0; 23 | } 24 | 25 | uint32_t Sha1Class::rol32(uint32_t number, uint8_t bits) { 26 | return ((number << bits) | (number >> (32-bits))); 27 | } 28 | 29 | void Sha1Class::hashBlock() { 30 | uint8_t i; 31 | uint32_t a,b,c,d,e,t; 32 | 33 | a=state.w[0]; 34 | b=state.w[1]; 35 | c=state.w[2]; 36 | d=state.w[3]; 37 | e=state.w[4]; 38 | for (i=0; i<80; i++) { 39 | if (i>=16) { 40 | t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15]; 41 | buffer.w[i&15] = rol32(t,1); 42 | } 43 | if (i<20) { 44 | t = (d ^ (b & (c ^ d))) + SHA1_K0; 45 | } else if (i<40) { 46 | t = (b ^ c ^ d) + SHA1_K20; 47 | } else if (i<60) { 48 | t = ((b & c) | (d & (b | c))) + SHA1_K40; 49 | } else { 50 | t = (b ^ c ^ d) + SHA1_K60; 51 | } 52 | t+=rol32(a,5) + e + buffer.w[i&15]; 53 | e=d; 54 | d=c; 55 | c=rol32(b,30); 56 | b=a; 57 | a=t; 58 | } 59 | state.w[0] += a; 60 | state.w[1] += b; 61 | state.w[2] += c; 62 | state.w[3] += d; 63 | state.w[4] += e; 64 | } 65 | 66 | void Sha1Class::addUncounted(uint8_t data) { 67 | buffer.b[bufferOffset ^ 3] = data; 68 | bufferOffset++; 69 | if (bufferOffset == BLOCK_LENGTH) { 70 | hashBlock(); 71 | bufferOffset = 0; 72 | } 73 | } 74 | 75 | size_t Sha1Class::write(uint8_t data) { 76 | ++byteCount; 77 | addUncounted(data); 78 | 79 | return sizeof(data); 80 | } 81 | 82 | void Sha1Class::pad() { 83 | // Implement SHA-1 padding (fips180-2 §5.1.1) 84 | 85 | // Pad with 0x80 followed by 0x00 until the end of the block 86 | addUncounted(0x80); 87 | while (bufferOffset != 56) addUncounted(0x00); 88 | 89 | // Append length in the last 8 bytes 90 | addUncounted(0); // We're only using 32 bit lengths 91 | addUncounted(0); // But SHA-1 supports 64 bit lengths 92 | addUncounted(0); // So zero pad the top bits 93 | addUncounted(byteCount >> 29); // Shifting to multiply by 8 94 | addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as 95 | addUncounted(byteCount >> 13); // byte. 96 | addUncounted(byteCount >> 5); 97 | addUncounted(byteCount << 3); 98 | } 99 | 100 | 101 | uint8_t* Sha1Class::result(void) { 102 | // Pad to complete the last block 103 | pad(); 104 | 105 | // Swap byte order back 106 | for (int i=0; i<5; i++) { 107 | uint32_t a,b; 108 | a=state.w[i]; 109 | b=a<<24; 110 | b|=(a<<8) & 0x00ff0000; 111 | b|=(a>>8) & 0x0000ff00; 112 | b|=a>>24; 113 | state.w[i]=b; 114 | } 115 | 116 | // Return pointer to hash (20 characters) 117 | return state.b; 118 | } 119 | 120 | #define HMAC_IPAD 0x36 121 | #define HMAC_OPAD 0x5c 122 | 123 | void Sha1Class::initHmac(const uint8_t* key, int keyLength) { 124 | uint8_t i; 125 | memset(keyBuffer,0,BLOCK_LENGTH); 126 | if (keyLength > BLOCK_LENGTH) { 127 | // Hash long keys 128 | init(); 129 | for (;keyLength--;) write(*key++); 130 | memcpy(keyBuffer,result(),HASH_LENGTH); 131 | } else { 132 | // Block length keys are used as is 133 | memcpy(keyBuffer,key,keyLength); 134 | } 135 | // Start inner hash 136 | init(); 137 | for (i=0; i 5 | #include "Print.h" 6 | 7 | #define HASH_LENGTH 20 8 | #define BLOCK_LENGTH 64 9 | 10 | union _buffer { 11 | uint8_t b[BLOCK_LENGTH]; 12 | uint32_t w[BLOCK_LENGTH/4]; 13 | }; 14 | union _state { 15 | uint8_t b[HASH_LENGTH]; 16 | uint32_t w[HASH_LENGTH/4]; 17 | }; 18 | 19 | class Sha1Class : public Print 20 | { 21 | public: 22 | void init(void); 23 | void initHmac(const uint8_t* secret, int secretLength); 24 | uint8_t* result(void); 25 | uint8_t* resultHmac(void); 26 | virtual size_t write(uint8_t); 27 | using Print::write; 28 | private: 29 | void pad(); 30 | void addUncounted(uint8_t data); 31 | void hashBlock(); 32 | uint32_t rol32(uint32_t number, uint8_t bits); 33 | _buffer buffer; 34 | uint8_t bufferOffset; 35 | _state state; 36 | uint32_t byteCount; 37 | uint8_t keyBuffer[BLOCK_LENGTH]; 38 | uint8_t innerHash[HASH_LENGTH]; 39 | 40 | }; 41 | extern Sha1Class Sha1; 42 | 43 | #endif 44 | --------------------------------------------------------------------------------