Support Home


Knowledge Base


Documentation


Contact Support


Unofficial Forum

Code Tips

On this page we will share tips and interesting ideas related to programming with Tibbo BASIC. The content of this page is not limited to our own, if you have any interesting ideas or experiences related to programming with Tibbo BASIC that you would like to share with others, please send us an email at support@tibbo.com

Hardware Verification Using MD5

(20 October 2008)

This code tip is aimed mainly at larger system integrators and manufacturers who develop significant Tibbo BASIC applications and wish to protect their work.

Consider the following scenario:

OEM A works hard to develop a product around a Tibbo module, including a unique Tibbo BASIC application. Client B buys this product, and likes it.

Client B now tries to increase profit margins. So they come up with a “bright” idea – why not go to some unscrupulous design house, copy the device, get a cheaper source for Tibbo modules, and just copy the application from the legally-purchased onto the new, illegally replicated, device? “Easy money”, right?

There is a less sinister but also plausible scenario: what if this Tibbo BASIC app was designed for some standard Tibbo hardware like the DS1000? In this case they don’t have to go to any other design house – they can just buy from Tibbo.

To provide OEMs with a means to prevent such “code freeloading”, Tibbo programmable modules such as the EM1000 and EM1202 offer a code protection scheme. At the heart of this scheme is a unique serial number assigned to each module. This number is accessible via the sys.serialnum property:

The serial number is really a 128-byte string which has two 64-byte fields. The first field can be programmed by the user – presumable, OEM “A”. This programming can only take place once. The second field is present at the factory and cannot be changed. The second field contains the serial number which is unique for each Tibbo module.

In a nutshell, the proposed protection method works like this:

  1. Taking the fixed (preset) portion of the serial number;
  2. Combining this fixed number with a secret key (say, a string of 10 characters);
  3. Calculating the MD5 hash of the above combination;
  4. Storing the MD5 result in the one-time-programmable (OTP) field.

Here is how it works in code:

Taking the fixed (preset) portion of the serial number

Read the serial number into a variable:

dim unique as string
unique=sys.serialnum

The last 64 bytes contain the unique serial number, while the first 64 bytes can be programmed once to store encrypted data (a passphrase, in this case). Isolate them:

unique=mid(unique,65,64)

Combining this fixed number with a secret key

Now, create a passphrase and use the MD5 function to generate a 16-byte hash of the serial number and plain-text passphrase.

dim plaintext,crypted as string
plaintext=unique+"Hello World"    ' Hello World is our passphrase.

Calculating the MD5 hash of the above combination

crypted=md5(plaintext,"",MD5_FINISH,Len(plaintext))

Storing the MD5 result in the one-time-programmable (OTP) field

Now, save this hash onto the first 64 bytes in the security register. Remember -– this is a one-time process; that hash may not be re-written later.

Because the generated MD5 string is only 16 bytes long, we’re going to append 48 null bytes, and thus bring the string up to a length of 64 bytes, as required by sys.setserialnum:

dim null_count as byte
dim crypted, null_str, crypted_64 as string
dim save_result as ok_ng
null_count=64-len(crypted)
null_str=strgen(null_count,chr(255)) ' insert null byte in spare area
crypted_64 = crypted +null_str    ' 16 add 48 null bytes to be 64 bytes
save_result=sys.setserialnum(crypted_64)  ' the argument should be 64 bytes

Hardware Verification

Then, when your application is running on the device, it should verify hardware authenticity like this:

  1. Take the fixed portion of the serial number;
  2. Combine this serial number with the secret word which is embedded inside the application but is not known to the outside world;
  3. Calculate the MD5 hash on this combination;
  4. Compare the result with the “programmed” code.
  5. Run normally if there is a match, abort if there is a mismatch.

The efficiency of the above method relies on the fact that each Tibbo module has a different fixed code and, therefore, the programmable portion of the code will be different for each device too. There is no way to subvert this protection method other then by reverse-engineering compiled Tibbo BASIC application binary, which is a tedious and ungratifying job.

Note: This can all be done with SHA1, too. The only difference is that an SHA1 hash is 20 bytes long, not 16.

Efficient Use of Storage Space

(12 November 2007)

Coming from PC based programming background, sometimes our coding habits becomes less efficient when dealing with storage space. This creates a problem of running out of memory when a large amount of data (for an embedded device) needs to be stored, and every byte of storage space counts!

Take for example, when we read / write strings to the EEPROM memory space using the “stor” object, we would have something like the following:

    dim x as string
    stor.setdata("142",1)
    x = stor.getdata(1,3)

The above will store the string 142 into the first position of the EEPROM and we will retrieve this value as a string.

So, what if when we need to store it as a byte instead of string? Most of us would probably just do it like this:

    dim x as byte
    stor.setdata(142,1)
    x = val(stor.getdata(1,3))

That would work fine, but notice that it is still taking up 3 bytes of memory space; there is a more memory efficient way to do this:

    dim x as byte
    stor.setdata(chr(142),1)
    x = asc(stor.getdata(1,1))

Now, instead of taking but 3 bytes of storage space, we only use 1 byte by first converting 142 into the ASCII representation before we store it into the EEPROM. When we retrieve back the data, we just convert it back to its original number.



Using sock.redir With UDP

(16 October 2007)

On some of the platforms you can speed up the data transfer by using a technique called buffer redirection, which means that data bypasses the RX buffer and goes directly to the TX buffer of another object. This works great, but with one exception, which is when you are redirecting from a socket that is using UDP, which will generate what seems like “junk data”.

The reason behind this is that since UDP packets has header information containing the size of the packet, without being processed by the BASIC program, the header is sent together with the data to the destination object's TX buffer. So, when working with UDP, please use the on_sock_data_arrival event handler.

Instead of using redirection like this:

    sock.num = 0
    sock.redir(PL_REDIR_SER0)

Change it to this:

    sock.num = 0
    ser.num = 0
    ser.setdata(sock.getdata(ser.txfree-ser.newtxlen))

Now, the socket is ready to handle UDP!



Smart Code That Won't Work

(29 August 2007)

Here's a good example where what seemingly is a smart and short code that just will not work! Orignially, this was the code used during one of our projects:

    dim f as byte
    for f=0 to 3
        ser.num=f
        ser.setdata("test")
        ser.send
    next f

Ok, so this outputs the same string through all four serial port.

Suddenly, I thought… do I really need f? Here is my new way:

    for ser.num=0 to 3
        ser.setdata("test")
        ser.send
    next ser.num

Wonderful! Only, it doesn't work!

For the cycle to be over, the ser.num must increment beyond the limit. That is, the cycle will be over when ser.num becomes 4. But you can't set it to 4 - this property is internally limited (to available serial ports 0…3). So writing ser.num=4 will still leave it at 3. So, the cycle will NEVER end.

Efficient Use of Storage Space

(12 November 2007)

Coming from PC based programming background, sometimes our coding habits becomes less efficient when dealing with storage space. This creates a problem of running out of memory when a large amount of data (for an embedded device) needs to be stored, and every byte of storage space counts!

Take for example, when we read / write strings to the EEPROM memory space using the “stor” object, we would have something like the following:

    dim x as string
    stor.setdata("142",1)
    x = stor.getdata(1,3)

The above will store the string 142 into the first position of the EEPROM and we will retrieve this value as a string.

So, what if when we need to store it as a byte instead of string? Most of us would probably just do it like this:

    dim x as byte
    stor.setdata(142,1)
    x = val(stor.getdata(1,3))

That would work fine, but notice that it is still taking up 3 bytes of memory space; there is a more memory efficient way to do this:

    dim x as byte
    stor.setdata(chr(142),1)
    x = asc(stor.getdata(1,1))

Now, instead of taking but 3 bytes of storage space, we only use 1 byte by first converting 142 into the ASCII representation before we store it into the EEPROM. When we retrieve back the data, we just convert it back to its original number.



Using sock.redir With UDP

(16 October 2007)

On some of the platforms you can speed up the data transfer by using a technique called buffer redirection, which means that data bypasses the RX buffer and goes directly to the TX buffer of another object. This works great, but with one exception, which is when you are redirecting from a socket that is using UDP, which will generate what seems like “junk data”.

The reason behind this is that since UDP packets has header information containing the size of the packet, without being processed by the BASIC program, the header is sent together with the data to the destination object's TX buffer. So, when working with UDP, please use the on_sock_data_arrival event handler.

Instead of using redirection like this:

    sock.num = 0
    sock.redir(PL_REDIR_SER0)

Change it to this:

    sock.num = 0
    ser.num = 0
    ser.setdata(sock.getdata(ser.txfree-ser.newtxlen))

Now, the socket is ready to handle UDP!



Smart Code That Won't Work

(29 August 2007)

Here's a good example where what seemingly is a smart and short code that just will not work! Orignially, this was the code used during one of our projects:

    dim f as byte
    for f=0 to 3
        ser.num=f
        ser.setdata("test")
        ser.send
    next f

Ok, so this outputs the same string through all four serial port.

Suddenly, I thought… do I really need f? Here is my new way:

    for ser.num=0 to 3
        ser.setdata("test")
        ser.send
    next ser.num

Wonderful! Only, it doesn't work!

For the cycle to be over, the ser.num must increment beyond the limit. That is, the cycle will be over when ser.num becomes 4. But you can't set it to 4 - this property is internally limited (to available serial ports 0…3). So writing ser.num=4 will still leave it at 3. So, the cycle will NEVER end.


© Tibbo Technology Inc. 2001-2009   Contact Us | Account