• I wanted to share with you where I ended with my research how to send data securely over untrusted unsecured networks using only http and sha1 - because that is all we have on those limited in memory boards. And that is what I've got.

    KEY is the same on ESP and API used only to calculate SHA1.
    API calculates SHA1 from data + KEY and compares it to signature.
    If it is the same - that is our board.

    Also it is good to have Date.now() somewhere in data to prevent API from being hacked and receive multiple copies of the same correct request. Date will prevent adding duplicated data to DB with the same date.

    CLOAK is used to have little more complex API endpoint route than some obvious one.

    If this helps someone, I'd be glad that I could help :)

    Big thanks goes to CanyonCasa for the idea: https://forum.espruino.com/comments/1346­2885/

    const crypto = require('crypto');
    const http = require('http');
    const wifi = require('Wifi');
    
    const KEY = 'e6a7b3d2-18f4-4c2b-9d67-4f5e1c7a8b3d';
    const HOST = 'someapithing.com';
    const API_VERSION = 'v2';
    const CLOAK = 'f47ac10b-58cc-4372-a567-0e02b2c3d479';
    const LOCATION = 'see-3';
    const NAME = 'oh-7';
    
    // Salt
    const generateSalt = (minimumLength, maximumLength) => {
      if (minimumLength < 1 || maximumLength < minimumLength) {
        throw new Error('Invalid length range: minLen must be >= 1 and maxLen must be >= minLen');
      }
    
      const characters = '0123456789abcdef';
      const charactersLength = characters.length;
      const range = maximumLength - minimumLength + 1;
      const saltLength = minimumLength + Math.floor(Math.random() * range);
      let salt = '';
    
      for (let i = 0; i < saltLength; i++) {
        salt += characters.charAt(Math.floor(Math.random­() * charactersLength));
      }
    
      return salt;
    };
    
    // Send Data Securely
    const sendData = data => {
      // Prepare data
      data.salt = generateSalt(16, 16);
      data.location = LOCATION;
      data.name = NAME;
    
      const stringifiedData = JSON.stringify(data);
      const signature = crypto.SHA1(stringifiedData + KEY).toString('hex');
      const stringifiedDataWithSignature = JSON.stringify([
        {
          data: data,
          signature: signature,
        },
      ]);
      console.log(stringifiedDataWithSignature­);
    
      // Send
      const options = url.parse(`http://${HOST}/${API_VERSION}­/${CLOAK}/device`);
      options.method = 'POST';
      options.headers = {
        'Content-Type': 'application/json',
        'Content-Length': stringifiedDataWithSignature.length,
      };
    
      const req = http.request(options, res => {
        console.log(`[ STATUS ] ${res.statusCode}`);
        let data = '';
        res.on('data', chunk => (data += chunk));
        res.on('end', () => console.log('[ END ]'));
      });
    
      req.on('error', err => console.error('[ ERROR ]', err));
      req.on('close', () => console.log('[ CLOSE ]', data));
      req.write(stringifiedDataWithSignature);­
      req.end();
    };
    
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Securely sending data to API over unsecured networks using http and crypto

Posted by Avatar for Jurand @Jurand

Actions