STM32 – Random IT Utensils https://blog.adamfurmanek.pl IT, operating systems, maths, and more. Sat, 17 Feb 2018 17:55:27 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 STM32 Key Part 4 — Server https://blog.adamfurmanek.pl/2018/02/17/stm32-key-part-4/ https://blog.adamfurmanek.pl/2018/02/17/stm32-key-part-4/#comments Sat, 17 Feb 2018 09:00:51 +0000 https://blog.adamfurmanek.pl/?p=2356 Continue reading STM32 Key Part 4 — Server]]>

This is the fourth part of the STM32 series. For your convenience you can find other parts in the table of contents in Part 1 – DES implementation

We have all the building blocks for STM32 server, so let’s write it.

Implementation

main.c

/*

*/

/* Includes */
#include <stddef.h>
#include "stm32f10x.h"
#include "STM32vldiscovery.h"
#include "ENC28J60.h"
#include "ip_arp_udp_tcp.h"
#include "simple_server.h"
#include "delay.h"

//NOTE: MAC address in ENC28J60 is byte-backward !!
const uint8_t ENC_MAC[6] = {0x54,0x55,0x58,0x10,0x00,0x24};
const uint8_t ENC_IP[4]={192,168,1,125};
uint8_t PC_MAC[6];
const uint8_t PC_IP[4]={192,168,1,1};

/* Private function prototypes */


extern int simple_server(void);

/* Private functions ---------------------------------------------------------*/

/* MAIN */
int main(void)
{
	Delay(0xFFFFF);

	SPI1_Init();
	enc28j60_init((uint8_t *)ENC_MAC);

	Delay(0xFFFFF);

    init_ip_arp_udp_tcp((uint8_t *)ENC_MAC, (uint8_t *)ENC_IP);

    Delay(0xFFFFF);

	enc28j60getrev();
    simple_server();
}

/*
 * Minimal __assert_func used by the assert() macro
 * */
void __assert_func(const char *file, int line, const char *func, const char *failedexpr)
{
  while(1)
  {}
}

/*
 * Minimal __assert() uses __assert__func()
 * */
void __assert(const char *file, int line, const char *failedexpr)
{
   __assert_func (file, line, NULL, failedexpr);
}

delay.c

#include "stm32f10x.h"
#include "delay.h"

void Delay(__IO uint32_t nTick)
{
  for(; nTick != 0; nTick--);
}

simple_server.c

#include "stdio.h"
#include <string.h>
#include "stm32f10x_conf.h"
#include "ENC28J60.h"
#include "net.h"
#include "ip_arp_udp_tcp.h"
#include "STM32vldiscovery.h"
#include "cipher.h"

#define PSTR(s) s

uint16_t my_udp_port = 50000; // listen port for udp
uint16_t my_udp_portResponse = 50001; // listen port for udp

#define BUFFER_SIZE 1500
static uint8_t buf[BUFFER_SIZE + 1];

uint16_t plen;
uint16_t dat_p;

int simple_server(void) {
	uint8_t cmd_pos = 0;
	uint8_t payloadlen = 0;
	char str[30];

	enc28j60_write_phy(PHLCON, 0x7a4);

	for(;;) {
		plen = enc28j60_recv_packet(buf, BUFFER_SIZE);

		if (plen == 0) {
			continue;
		}

		if (eth_type_is_arp_and_my_ip(buf, plen)) {
			make_arp_answer_from_request(buf);
			continue;
		}

		if (eth_type_is_ip_and_my_ip(buf, plen) == 0) {
			continue;
		}

		if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V
				&& buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V) {
			make_echo_reply_from_request(buf, plen);
			continue;
		}

		uint16_t rPort = buf[UDP_DST_PORT_H_P]*256+buf[UDP_DST_PORT_L_P];
		uint8_t dataBuffer[220] = {0};

		if (buf[IP_PROTO_P] == IP_PROTO_UDP_V) {
			payloadlen = buf[UDP_LEN_L_P] - UDP_HEADER_LEN;

			if(rPort == my_udp_port) {
				cipher(&buf[UDP_DATA_P], dataBuffer, payloadlen, 0xaaaaaaaaaaaaaaaa);
				make_udp_reply_from_request(buf, dataBuffer, payloadlen, &my_udp_portResponse);
			}
			else if(rPort == my_udp_portResponse) {
				decipher(&buf[UDP_DATA_P], dataBuffer, payloadlen, 0xaaaaaaaaaaaaaaaa);
				make_udp_reply_from_request(buf, dataBuffer, payloadlen, &my_udp_port);
			}
		}
	}
}

And it looks like this is it.

Summary

We have everything in place. We know how to encrypt data using DES, we have Swing chat application for tests and we have application for STM32 to actually do the job. It is easy and most of the stuff is hardcoded, however, it could be easily modified just to do the remaining 20% of the work.

]]>
https://blog.adamfurmanek.pl/2018/02/17/stm32-key-part-4/feed/ 1
STM32 Key Part 3 — ARP, IP, UDP https://blog.adamfurmanek.pl/2018/02/10/stm32-key-part-3/ https://blog.adamfurmanek.pl/2018/02/10/stm32-key-part-3/#comments Sat, 10 Feb 2018 09:00:07 +0000 https://blog.adamfurmanek.pl/?p=2353 Continue reading STM32 Key Part 3 — ARP, IP, UDP]]>

This is the third part of the STM32 series. For your convenience you can find other parts in the table of contents in Part 1 – DES implementation

Today we are going to handle some network packages on the STM32. Let’s go.

Libraries around

I am not going to describe how to handle SPI, LEDs and buttons on your microcontroller. First, because the code is in tutorials you get after buying the hardware, second, because I cannot post the code because of legal restrictions. However, there is no magic in this area.

Code

The code was heavily based on the work of Guido Socher. I would like to thank him for posting this code somewhere because it made the project much easier.

/*********************************************
 * vim:sw=8:ts=8:si:et
 * To use the above modeline in vim you must have "set modeline" in your .vimrc
 *
 * Author: Guido Socher 
 * Copyright: GPL V2
 * See http://www.gnu.org/licenses/gpl.html
 *
 * IP, Arp, UDP and TCP functions.
 *
 * The TCP implementation uses some size optimisations which are valid
 * only if all data can be sent in one single packet. This is however
 * not a big limitation for a microcontroller as you will anyhow use
 * small web-pages. The TCP stack is therefore a SDP-TCP stack (single data packet TCP).
 *
 * Chip type           : ATMEGA88 with ENC28J60
 *********************************************/
#include "net.h"
#include "enc28j60.h"


extern void Delay(__IO uint32_t nTick);

#define  pgm_read_byte(ptr)  ((uint8_t)*(ptr))

//#define uint8_t  uint8_t
//#define uint8_t unisgned int

static uint8_t macaddr[6];
static uint8_t ipaddr[4];
static uint16_t info_hdr_len;
static uint16_t info_data_len;
static uint8_t seqnum; // my initial tcp sequence number = 0xA

// The Ip checksum is calculated over the ip header only starting
// with the header length field and a total length of 20 bytes
// unitl ip.dst
// You must set the IP checksum field to zero before you start
// the calculation.
// len for ip is 20.
//
// For UDP/TCP we do not make up the required pseudo header. Instead we 
// use the ip.src and ip.dst fields of the real packet:
// The udp checksum calculation starts with the ip.src field
// Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len
// In other words the len here is 8 + length over which you actually
// want to calculate the checksum.
// You must set the checksum field to zero before you start
// the calculation.
// len for udp is: 8 + 8 + data length
// len for tcp is: 4+4 + 20 + option len + data length
//
// For more information on how this algorithm works see:
// http://www.netfor2.com/checksum.html
// http://www.msc.uky.edu/ken/cs471/notes/chap3.htm
// The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html
uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type)
{
	// type 0=ip 
	//      1=udp
	//      2=tcp
	uint32_t sum = 0;
	
	//if(type==0){
	//        // do not add anything
	//}
	if(type==1)
	{
		sum+=IP_PROTO_UDP_V; // protocol udp
		// the length here is the length of udp (data+header len)
		// =length given to this function - (IP.scr+IP.dst length)
		sum+=len-8; // = real tcp len
	}
	if(type==2)
	{
		sum+=IP_PROTO_TCP_V; 
		// the length here is the length of tcp (data+header len)
		// =length given to this function - (IP.scr+IP.dst length)
		sum+=len-8; // = real tcp len
	}
	// build the sum of 16bit words
	while(len >1)
	{
		sum += 0xFFFF & (*buf<<8|*(buf+1));
		buf+=2;
		len-=2;
	}
	// if there is a byte left then add it (padded with zero)
	if (len)
		sum += (0xFF & *buf)<<8;
	// now calculate the sum over the bytes in the sum
	// until the result is only 16bit long
	while (sum>>16)
		sum = (sum & 0xFFFF)+(sum >> 16);
	// build 1's complement:
	return( (uint16_t) sum ^ 0xFFFF);
}

// you must call this function once before you use any of the other functions:
void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip)
{
	uint8_t i=0;
	info_hdr_len=0;
	info_data_len=0;
	seqnum=0xA;
	while(i<4)
	{
        ipaddr[i]=myip[i];
        i++;
	}
	i=0;
	while(i<6)
	{
        macaddr[i]=mymac[i];
        i++;
	}
}

uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf, uint8_t len)
{
	uint8_t i=0;
	//  
	if (len<41)
	    return(0);
	if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V)
	    return(0);
	while(i<4)
	{
	    if(buf[ETH_ARP_DST_IP_P+i] != ipaddr[i])
	        return(0);
	    i++;
	}
	return(1);
}

uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint8_t len)
{
	uint8_t i=0;
	//eth+ip+udp header is 42
	if (len<42)
	    return(0);
	if(buf[ETH_TYPE_H_P]!=ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P]!=ETHTYPE_IP_L_V)
	    return(0);
	if (buf[IP_HEADER_LEN_VER_P]!=0x45)
	{
	    // must be IP V4 and 20 byte header
	    return(0);
	}
	while(i<4)
	{
	    if(buf[IP_DST_P+i]!=ipaddr[i])
	        return(0);
	    i++;
	}
	return(1);
}

// make a return eth header from a received eth packet
void make_eth(uint8_t *buf)
{
	uint8_t i=0;
	//
	//copy the destination mac from the source and fill my mac into src
	while(i<6)
	{
        buf[ETH_DST_MAC +i]=buf[ETH_SRC_MAC +i];
        buf[ETH_SRC_MAC +i]=macaddr[i];
        i++;
	}
}

void fill_ip_hdr_checksum(uint8_t *buf)
{
	uint16_t ck;
	// clear the 2 byte checksum
	buf[IP_CHECKSUM_P]=0;
	buf[IP_CHECKSUM_P+1]=0;
	buf[IP_FLAGS_P]=0x40; // don't fragment
	buf[IP_FLAGS_P+1]=0;  // fragement offset
	buf[IP_TTL_P]=64; // ttl
	// calculate the checksum:
	ck=checksum(&buf[IP_P], IP_HEADER_LEN,0);
	buf[IP_CHECKSUM_P]=ck>>8;
	buf[IP_CHECKSUM_P+1]=ck& 0xff;
}

// make a return ip header from a received ip packet
void make_ip(uint8_t *buf)
{
	uint8_t i=0;
	while(i<4)
	{
        buf[IP_DST_P+i]=buf[IP_SRC_P+i];
        buf[IP_SRC_P+i]=ipaddr[i];
        i++;
	}
	fill_ip_hdr_checksum(buf);
}

// make a return ip header from a received ip packet for echo reply
void make_ip_echo(uint8_t *buf)
{
	uint8_t i=0;
	uint16_t ck;
	while(i<4)
	{
        buf[IP_DST_P+i]=buf[IP_SRC_P+i];
        buf[IP_SRC_P+i]=ipaddr[i];
        i++;
	}
	// clear the 2 byte checksum
	i = buf[IP_CHECKSUM_P];
	buf[IP_CHECKSUM_P]=0;
	buf[IP_CHECKSUM_P+1]=0;
	buf[IP_FLAGS_P]=0x40; // don't fragment
	buf[IP_FLAGS_P+1]=0;  // fragement offset
	buf[IP_TTL_P]=64; // ttl
	// calculate the checksum:
	ck=checksum(&buf[IP_P], IP_HEADER_LEN,0);
	buf[IP_CHECKSUM_P]=i;
	buf[IP_CHECKSUM_P+1]=ck& 0xff;
}

void make_arp_answer_from_request(uint8_t *buf)
{
	uint8_t i=0;
	//
	make_eth(buf);
	buf[ETH_ARP_OPCODE_H_P]=ETH_ARP_OPCODE_REPLY_H_V;
	buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V;
	// fill the mac addresses:
	while(i<6)
	{
        buf[ETH_ARP_DST_MAC_P+i]=buf[ETH_ARP_SRC_MAC_P+i];
        buf[ETH_ARP_SRC_MAC_P+i]=macaddr[i];
        i++;
	}
	i=0;
	while(i<4)
	{
        buf[ETH_ARP_DST_IP_P+i]=buf[ETH_ARP_SRC_IP_P+i];
        buf[ETH_ARP_SRC_IP_P+i]=ipaddr[i];
        i++;
	}
	// eth+arp is 42 bytes:
	enc28j60PacketSend(42,buf);
}

void make_echo_reply_from_request(uint8_t *buf,uint16_t len)
{
	make_eth(buf);
	make_ip(buf);
	buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREPLY_V;
	//////////////////////////////////////////////////////////////////////////////////
	// we changed only the icmp.type field from request(=8) to reply(=0).
	// we can therefore easily correct the checksum:
	if (buf[ICMP_CHECKSUM_P] > (0xff-0x08))
	    buf[ICMP_CHECKSUM_P+1]++;
	buf[ICMP_CHECKSUM_P]+=0x08;
	//
	enc28j60PacketSend(len,buf);
}

// you can send a max of 220 bytes of data
void make_udp_reply_from_request(uint8_t *buf, uint8_t *data,uint8_t datalen,uint16_t* port)
{
	uint8_t i=0;
	uint16_t ck;
	make_eth(buf);
	if (datalen>220)
	    datalen=220;
	// total length field in the IP header must be set:
	buf[IP_TOTLEN_H_P]=0;
	buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen;
	make_ip(buf);
	buf[UDP_DST_PORT_H_P]=*port >> 8;
	buf[UDP_DST_PORT_L_P]=*port & 0xff;
	// source port does not matter and is what the sender used.
	// calculte the udp length:
	buf[UDP_LEN_H_P]=0;
	buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen;
	// zero the checksum
	buf[UDP_CHECKSUM_H_P]=0;
	buf[UDP_CHECKSUM_L_P]=0;
	// copy the data:
	while(i<datalen)
	{
        buf[UDP_DATA_P+i]=data[i];
        i++;
	}
	ck=checksum(&buf[IP_SRC_P], 16 + datalen,1);
	buf[UDP_CHECKSUM_H_P]=ck>>8;
	buf[UDP_CHECKSUM_L_P]=ck& 0xff;
	enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf);
}


// do some basic length calculations and store the result in static varibales
void init_len_info(uint8_t *buf)
{
    info_data_len=(buf[IP_TOTLEN_H_P]<<8)|(buf[IP_TOTLEN_L_P]&0xff);
    info_data_len-=IP_HEADER_LEN;
    info_hdr_len=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes;
    info_data_len-=info_hdr_len;
    if (info_data_len<=0)
        info_data_len=0;
}
/* end of ip_arp_udp.c */

Summary

We have most of the code in place. Next time we are going to implement a server for encryption and decryption.

]]>
https://blog.adamfurmanek.pl/2018/02/10/stm32-key-part-3/feed/ 1
STM32 Key Part 2 — Chat application https://blog.adamfurmanek.pl/2018/02/03/stm32-key-part-2/ https://blog.adamfurmanek.pl/2018/02/03/stm32-key-part-2/#comments Sat, 03 Feb 2018 09:00:00 +0000 https://blog.adamfurmanek.pl/?p=2348 Continue reading STM32 Key Part 2 — Chat application]]>

This is the second part of the STM32 series. For your convenience you can find other parts in the table of contents in Part 1 – DES implementation

Today we are going to write very simple Java application to be able to see the traffic and communicate with our STM32. Let’s begin.

Implementation

We assume that our computer has an address 192.168.1.1 and that the STM32 uses 192.168.1.125. For encryption we use port 50000, for decryption 50001. Below are all required files:

package chat;

import gui.MainWindow;
import logic.Communicator;
import logic.Converter;
import logic.Encryption;
import logic.MessageReceivedEventArgs;
import logic.MessageReceivedListener;

public class Chat {

    private final int PORT_FOR_ENCRYPTION = 50000;
    private final int PORT_FOR_DECRYPTION = 50001;
    private final MainWindow window;
    private final Communicator communicator;
    private final Encryption mode;

    public Chat(String address, Encryption encryption) {
        this.mode = encryption;
        window = new MainWindow(this);
        window.setVisible(true);
        switch (encryption) {
            case ENCRYPT:
                communicator = new Communicator(address, PORT_FOR_ENCRYPTION, PORT_FOR_ENCRYPTION);
                break;
            case DECRYPT:
                communicator = new Communicator(address, PORT_FOR_DECRYPTION, PORT_FOR_DECRYPTION);
                break;
            default:
                throw new AssertionError(encryption.name());
        }

        communicator.addMessageReceivedListener(new MessageReceivedListener() {
            @Override
            public void messageReceived(MessageReceivedEventArgs e) {
                receiveMessage(e.getMessageText());
            }
        });
    }

    public static void main(String[] args) {
        if (args.length > 1 && args[1].equals("-d")) {
            new Chat(args[0], Encryption.DECRYPT);
        } else {
            new Chat(args[0], Encryption.ENCRYPT);
        }
    }

    public void sendMessage(String text) {
    	byte[] buffer = null;
        if (mode == Encryption.DECRYPT) {
            buffer = Converter.convertFromHexToBytes(text);
            for(int i=0; i < buffer.length; ++i)
        		System.out.print(String.format("%x", buffer[i]));
        }
        else {
        	buffer = text.getBytes();
        }
        System.out.println(text);
        communicator.sendMessage(buffer);
    }

    private void receiveMessage(byte[] text) {
    	for(int i=0; i < text.length; ++i)
    		System.out.print(String.format("%x", text[i]));
    	System.out.println("");
    	String message = new String(text);
        if (mode == Encryption.DECRYPT) {
        	message = Converter.convertFromTextToHex(text);
        }
        window.showNewMessage("Friend: " + message);
    }
}

package gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import chat.Chat;

public class MainWindow extends javax.swing.JFrame {

    private Chat chat;

    public MainWindow(Chat chat) {
        initComponents();
        this.chat = chat;
        
        inputField.addActionListener(new ActionListener(){
        	@Override
        	public void actionPerformed(ActionEvent e){
        		sendButtonActionPerformed(e);
        	}
        });
    }

    @SuppressWarnings("unchecked")
    // < editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        chatField = new javax.swing.JTextArea();
        sendButton = new javax.swing.JButton();
        inputField = new javax.swing.JTextField();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        chatField.setEditable(false);
        chatField.setColumns(20);
        chatField.setRows(5);
        jScrollPane1.setViewportView(chatField);

        sendButton.setText("SEND");
        sendButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sendButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(inputField)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(sendButton))
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 271, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(sendButton)
                    .addComponent(inputField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
        );

        pack();
    }// < /editor-fold>//GEN-END:initComponents

    private void sendButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendButtonActionPerformed
        chat.sendMessage(inputField.getText());
        showNewMessage("You: " + inputField.getText());
        inputField.setText("");
    }//GEN-LAST:event_sendButtonActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextArea chatField;
    private javax.swing.JTextField inputField;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton sendButton;
    // End of variables declaration//GEN-END:variables

    public void showNewMessage(String text) {
        chatField.append(text + "\n");
    }
}

MainWindow.form designer:

< ?xml version="1.0" encoding="UTF-8" ?>

< Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
  < Properties>
    < Property name="defaultCloseOperation" type="int" value="3"/>
  < /Properties>
  < SyntheticProperties>
    < SyntheticProperty name="formSizePolicy" type="int" value="1"/>
  < /SyntheticProperties>
  < AuxValues>
    < AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
    < AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
    < AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
    < AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
    < AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
    < AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
  < /AuxValues>

  < Layout>
    < DimensionLayout dim="0">
      < Group type="103" groupAlignment="0" attributes="0">
          < Group type="102" alignment="1" attributes="0">
              < Component id="inputField" max="32767" attributes="0"/>
              < EmptySpace max="-2" attributes="0"/>
              < Component id="sendButton" min="-2" max="-2" attributes="0"/>
          < /Group>
          < Component id="jScrollPane1" alignment="0" pref="400" max="32767" attributes="0"/>
      < /Group>
    < /DimensionLayout>
    < DimensionLayout dim="1">
      < Group type="103" groupAlignment="0" attributes="0">
          < Group type="102" alignment="0" attributes="0">
              < Component id="jScrollPane1" pref="271" max="32767" attributes="0"/>
              < EmptySpace max="-2" attributes="0"/>
              < Group type="103" groupAlignment="3" attributes="0">
                  < Component id="sendButton" alignment="3" min="-2" max="-2" attributes="0"/>
                  < Component id="inputField" alignment="3" min="-2" max="-2" attributes="0"/>
              < /Group>
          < /Group>
      < /Group>
    < /DimensionLayout>
  < /Layout>
  < SubComponents>
    < Container class="javax.swing.JScrollPane" name="jScrollPane1">
      < AuxValues>
        < AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
      < /AuxValues>

      < Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
      < SubComponents>
        < Component class="javax.swing.JTextArea" name="chatField">
          < Properties>
            < Property name="editable" type="boolean" value="false"/>
            < Property name="columns" type="int" value="20"/>
            < Property name="rows" type="int" value="5"/>
          < /Properties>
        < /Component>
      < /SubComponents>
    < /Container>
    < Component class="javax.swing.JButton" name="sendButton">
      < Properties>
        < Property name="text" type="java.lang.String" value="SEND"/>
      < /Properties>
      < Events>
        < EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sendButtonActionPerformed"/>
      < /Events>
    < /Component>
    < Component class="javax.swing.JTextField" name="inputField">
    < /Component>
  < /SubComponents>
< /Form>

package logic;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Communicator {

    private int sendPort;
    private String address;
    private DatagramSocket socket;

    public Communicator(String address, int sendPort, int receivePort) {
        try {
            messageReceivedListeners = new ArrayList< MessageReceivedListener>();
            this.address = address;
            this.sendPort = sendPort;
            socket = new DatagramSocket();
            Thread receiver = new Receiver(receivePort, this);
            receiver.start();
        } catch (SocketException ex) {
            Logger.getLogger(Communicator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    private ArrayList< MessageReceivedListener> messageReceivedListeners;

    public void sendMessage(byte[] buffer) {
    	try {
            buffer = prepareMsgToSend(buffer);
            
            InetAddress inetAddress = InetAddress.getByName(address);
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, inetAddress, sendPort);
            socket.send(packet);
        } catch (IOException ex) {
            Logger.getLogger(Communicator.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("ERROR");
        }
    }

	private byte[] prepareMsgToSend(byte[] buffer) {
		int length = buffer.length + (8 - buffer.length % 8) % 8;
		
		byte[] reversedBuffer = new byte[length];
		
		for(int i=0;i < buffer.length;++i){
			reversedBuffer[length-i-1] = buffer[i];
		}
		
		return reversedBuffer;
	}

    public void addMessageReceivedListener(MessageReceivedListener listener) {
        messageReceivedListeners.add(listener);
    }

    public void removeMessageReceivedListener(MessageReceivedListener listener) {
        messageReceivedListeners.remove(listener);
    }

    public void onMessageReceived(byte[] message) {
        MessageReceivedEventArgs args = new MessageReceivedEventArgs(message);

        for (MessageReceivedListener listener : messageReceivedListeners) {
            listener.messageReceived(args);
        }
    }
}

package logic;

public class Converter {

    public static String convertFromTextToHex(byte[] text) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < text.length; ++i) {
            builder.append(String.format("%02x", text[i]));
        }

        return builder.toString().toUpperCase();
    }

    public static String convertFromHexToText(String text) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < text.length() - 1; i += 2) {
            builder.append((char) Integer.parseInt(text.substring(i, i + 2), 16));
        }
    	
    	

        return builder.toString();
    }
    
    public static byte[] convertFromHexToBytes(String text) {
    	byte[] result = new byte[text.length()/2];
    	
        for (int i = 0; i < text.length() - 1; i += 2) {
        	System.out.println(text.substring(i, i+2));
             result[i/2] = (byte) Integer.parseInt(text.substring(i, i + 2), 16);
             System.out.println(result[i/2]);
        }
        
        return result;
    }
}

package logic;

public enum Encryption{
    ENCRYPT,
    DECRYPT
}

package logic;

public class MessageReceivedEventArgs {

    public MessageReceivedEventArgs(byte[] messageText) {
        this.messageText = messageText;
    }
    byte[] messageText;

    public byte[] getMessageText() {
        return messageText;
    }
}

package logic;

public interface MessageReceivedListener {

    public void messageReceived(MessageReceivedEventArgs e);
}

package logic;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

class Receiver extends Thread {

    private int receivePort;
    private Communicator communicator;
    private DatagramSocket socket;
    private BufferedReader reader;

    public Receiver(int receivePort, Communicator communicator) {
        this.receivePort = receivePort;
        this.communicator = communicator;
    }

    @Override
    public void run() {
        super.run();
        try {
			socket = new DatagramSocket(receivePort);
		} catch (SocketException e) {
			e.printStackTrace();
		}

        for (;;) {
            try {
                byte[] buffer = new byte[256];
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                socket.receive(packet);
                onMessageReceived(Arrays.copyOf(buffer, packet.getLength()));
            } catch (IOException ex) {
                Logger.getLogger(Receiver.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void onMessageReceived(byte[] message) {
    	 byte[] reversedBuffer = new byte[message.length];
         for(int i=0;i < message.length;++i){
         	reversedBuffer[message.length - i -1] = message[i];
         }
        communicator.onMessageReceived(reversedBuffer);
    }
}

As a side note: I remember I wrote some unit tests for converting text to bytes and hexes but apparently I lost it. It’s been 5 years since I was working on this project.

Summary

Next time we will see very simple code for handling network packets on STM side.

]]>
https://blog.adamfurmanek.pl/2018/02/03/stm32-key-part-2/feed/ 1
STM32 Key Part 1 — DES implementation https://blog.adamfurmanek.pl/2018/01/27/stm32-key-part-1/ https://blog.adamfurmanek.pl/2018/01/27/stm32-key-part-1/#comments Sat, 27 Jan 2018 09:00:00 +0000 https://blog.adamfurmanek.pl/?p=2338 Continue reading STM32 Key Part 1 — DES implementation]]>

This is the first part of the STM32 Key series. For your convenience you can find other parts using the links below (or by guessing the address):
Part 1 — DES implementation
Part 2 — Chat application
Part 3 — ARP, IP, UDP
Part 4 — Server

Today we start a new series in which we will make an encryption key using STM32 microcontroller. Let’s begin.

Introduction

We assume that we want to communicate with someone in the same LAN and we would like to have our traffic completely encrypted. We get two identical STM32 microcontrollers to use them as an encryption keys — we stick one of them in our machine, our friend does the same with his own. Next, we send packet from our machine to STM, it encrypts it and sends to another STM which in turn decrypts it and delivers to friend application.

Since this is just an academic project (I was implementing it few years ago during my CS studies), we don’t want to over-engineer it so we will use DES as an encryption algorithm and we will only handle UDP traffic. Also, here and there we will hard-code some things just to make them easier but also (what’s more important) to not exceed the limit of source code allowed in free environment.

This is the idea, implementation will be a little different. We will have chat application operating in one of two modes: encryption and decryption. Encryption will take plaintext and send it to STM on port A. STM will encrypt the data and send it back from port B. Chat application in decryption mode will listen on port B and present received data in hex format. If we send anything from decryption chat, it will go to port B of STM, be decrypted there and sent back from port A. So we cant test STM on one machine.

I will not describe all the details since right now they are probably outdated. I will just focus on the core details of logic, not necessarily on the SPI communication and technical stuff.

DES

DES is simple enough to be implemented in small amount of code and yet not too naive. Do not use it in production anymore since it is not secure but in academic environment we might ignore this little obstacle. The code goes like this:

#include <stdio.h>
#include <stdlib.h>

static unsigned char pc1[56] = { 57, 49, 41, 33, 25, 17, 9,
	//PC1
	1, 58, 50, 42, 34, 26, 18,
	10, 2, 59, 51, 43, 35, 27,
	19, 11, 3, 60, 52, 44, 36,
	63, 55, 47, 39, 31, 23, 15,
	7, 62, 54, 46, 38, 30, 22,
	14, 6, 61, 53, 45, 37, 29,
	21, 13, 5, 28, 20, 12, 4};
static unsigned char pc2[48] = { 14, 17, 11, 24, 1, 5,
	//PC2
	3, 28, 15, 6, 21, 10,
	23, 19, 12, 4, 26, 8,
	16, 7, 27, 20, 13, 2,
	41, 52, 31, 37, 47, 55,
	30, 40, 51, 45, 33, 48,
	44, 49, 39, 56, 34, 53,
	46, 42, 50, 36, 29, 32};
static unsigned char ip[64] = { 58, 50, 42, 34, 26, 18, 10, 2,//Init. Permutation
	60, 52, 44, 36, 28, 20, 12, 4,
	62, 54, 46, 38, 30, 22, 14, 6,
	64, 56, 48, 40, 32, 24, 16, 8,
	57, 49, 41, 33, 25, 17, 9, 1,
	59, 51, 43, 35, 27, 19, 11, 3,
	61, 53, 45, 37, 29, 21, 13, 5,
	63, 55, 47, 39, 31, 23, 15, 7};
static unsigned char ep[48] = { 32, 1, 2, 3, 4, 5, //Exp. Permutation
	4, 5, 6, 7, 8, 9,
	8, 9, 10, 11, 12, 13,
	12, 13, 14, 15, 16, 17,
	16, 17, 18, 19, 20, 21,
	20, 21, 22, 23, 24, 25,
	24, 25, 26, 27, 28, 29,
	28, 29, 30, 31, 32, 1};
static unsigned char s[8][4][16] = {
	{ {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,},//S1
	{0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,},
	{4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,},
	{15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,},},
	{ {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,},//S2
	{ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,},
	{ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,},
	{13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,},},
	{ {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,},//S3
	{13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,},
	{13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,},
	{ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,},},
	{ { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,},//S4
	{13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,},
	{10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,},
	{ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,},},
	{ { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,},//S5
	{14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,},
	{ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,},
	{11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,},},
	{ {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,},//S6
	{10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,},
	{ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,},
	{ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,},},
	{ { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,},//S7
	{13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,},
	{ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,},
	{ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,},},
	{ {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,},//S8
	{ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,},
	{ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,},
	{ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
static unsigned char p[32] = { 16, 7, 20, 21, //Permutation
	29, 12, 28, 17,
	1, 15, 23, 26,
	5, 18, 31, 10,
	2, 8, 24, 14,
	32, 27, 3, 9,
	19, 13, 30, 6,
	22, 11, 4, 25};
static unsigned char iip[64] = { 40, 8, 48, 16, 56, 24, 64, 32,//Inv. Permutation
	39, 7, 47, 15, 55, 23, 63, 31,
	38, 6, 46, 14, 54, 22, 62, 30,
	37, 5, 45, 13, 53, 21, 61, 29,
	36, 4, 44, 12, 52, 20, 60, 28,
	35, 3, 43, 11, 51, 19, 59, 27,
	34, 2, 42, 10, 50, 18, 58, 26,
	33, 1, 41, 9, 49, 17, 57, 25};

unsigned long long transform(unsigned long long pln_txt, unsigned long long key, int ch){
	unsigned long long k[16], cd = 0, temp = 0, des_out = 0, ep_out48 = 0;
	unsigned int l = 0, r = 0, c0 = 0, d0 = 0, sb_out32 = 0, p_out32 = 0;
	unsigned char sdata = 0, row = 0, col = 0;
	int  j = 0, i = 0, round = 0;
	////////////////////////Key Schedule//////////////////////////////////// 
	for (j=27;  j>=0;j--)
		//PC1
	{
		c0 = c0 ^ (((key >> (64-pc1[(27-j)])) & 0x1) <<  j);
		d0 = d0 ^ (((key >> (64-pc1[(55-j)])) & 0x1) <<  j);
	}
	for (i=1; i<=16;i++) k[i-1] = 0;  //initiallizing key array
	for (i=1; i<=16;i++)
	{
		if  ((i==1) | (i==2) | (i==9) | (i==16))
		{
			c0 = ((c0 << 1) | (c0 >> 27)) & 0x0FFFFFFF;
			d0 = ((d0 << 1) | (d0 >> 27)) & 0x0FFFFFFF;
		}
		else
		{
			c0 = ((c0 << 2) | (c0 >> 26)) & 0x0FFFFFFF;
			d0 = ((d0 << 2) | (d0 >> 26)) & 0x0FFFFFFF;
		}
		cd = 0;  // initiallizing for new value
		cd = ((cd ^ c0) << 28) ^ d0;
		for (j=47;  j>=0;j--)
			//PC2
				k[i-1] = k[i-1] ^ (((cd >> (56-pc2[(47-j)])) & 0x1) <<  j);
	}  ///////////////////////End Key Schedule//////////////////////////////////// 

	////////////////////////Initial Permutation//////////////////////////////// 
	for (j=31;  j>=0;j--)
	{
		l = l ^ (((pln_txt >> (64-ip[(31-j)])) & 0x1) <<  j);
		r = r ^ (((pln_txt >> (64-ip[(63-j)])) & 0x1) <<  j);
	}
	/////////////////////////// 16 Rounds /////////////////////////// 
	for (round=0; round<16; round++)
	{  /////////////////////Expansion Permutation///////////////// 
		ep_out48 = 0;
		for (j=47;  j>=0;j--)
			ep_out48 = ep_out48 ^ ((long long)((r >> (32-ep[(47-j)])) & 0x1) <<  j);
		////////////////////End exp Perm////////////////////////// 
		if  (ch == 1) ep_out48 = ep_out48 ^ k[round];  //Round Key Xor for cipher

		if  (ch == 2) ep_out48 = ep_out48 ^ k[15-round];  //Round Key Xor for decipher
		/////////////////////////S Box Reduction/////////////////// 
		sb_out32 = 0;
		for (i=7; i>=0;i--)
		{
			row = 0;
			col = 0;
			sdata = 0;
			sdata = (ep_out48>>(i*6)) & 0x3F;
			//accessing 6 bits at a time starting from MSB
			row = row ^ (sdata & 0x1);
			row = row ^ (((sdata >> 5) & 0x1) << 1);
			col = (sdata >>1) & 0x0F;
			sb_out32= sb_out32 ^ ((int)(s[7-i][row][col] << (4*i)));
		}
		/////////////////////Permutation////////////////////////// 
		p_out32 = 0;
		for (j=31;  j>=0;j--) p_out32 = p_out32 ^ (((sb_out32 >> (32-p[(31-j)])) & 0x1) <<  j);
		p_out32 = p_out32 ^ l;
		//Xor with left half 
		l = r; r = p_out32;
	}
	/////////////////////// End 16 Rounds ////////////////// 
	temp = 0; temp = ((temp ^ r) << 32) ^ l;  //32bit SWAP
	/////////////////////Inverse Initial Permutation////////////////////////// 
	des_out = 0;
	for (j=63;  j>=0;j--)
		des_out = des_out ^ (((temp >> (64-iip[(63-j)])) & 0x1) <<  j);
	return des_out;
}

int main(void) {
	unsigned long long ch, pln_txt, key;

	printf ("Please select from menu:\n");
	printf ("1. Cipher\n");
	printf ("2. Decipher\n");
	printf ("Choice: ");
	scanf ("%d",&ch);
	printf ("Enter 64bit text in Hex : ");

	scanf ("%llx",&pln_txt);
	printf ("Enter 64bit Key in Hex : ");
	scanf ("%llx",&key);

	printf ("des_out = %llx \n", transform(pln_txt, key, ch));

	return 0; 
}

The application works in one of two modes, it either encrypts or decrypts data.

Encryption

Decryption

Summary

In next part we are going to write very simple chat application using Java, Swing and Netbeans. So old!

]]>
https://blog.adamfurmanek.pl/2018/01/27/stm32-key-part-1/feed/ 3