FULL-C crypto functions

Tato stránka popisuje programovací jazyk FULL-C, který je dostupný na vybraných zařízeních SDS. 
Některá zařízení používají SDS-C, pro který máme návody jinde na této WiKi.

Funkce pro symetrickou kryptografii ve FULL-C

FULL-C nabízí akcelerované kryptografické funkce - XTEA-64-CTR a AES-128-CTR.

Základním cílem je zabezpečit komunikaci mezi SDS a (například) webovým serverem na portálu.

Pro použítí symetrických funkcí (enkrypce a dekrypce), je k dispozici toto společné volání:

 unsigned int SDS_crypto(unsigned int Algorithm, unsigned int *CounterBlock, void *PayloadData, unsigned int PayloadDataSize);

Pro převody způsobu uložení dat (převody Base64) lze využít těchto dvou vestavěných funkcí:

 unsigned int SDS_ToBase64(void * inputBuf, void * outputBuf, unsigned int inputLen, unsigned int FLAGS);
 unsigned int SDS_FromBase64(void * inputBuf, void * outputBuf, unsigned int inputLen, unsigned int FLAGS);

Informace

Klíč se nastavuje přímo ve webové administraci, tzn. uživatel si jej nastaví (zabezpečeno přístupovým heslem do SDS) a klíč je uložen v DataFlash (non-volatile memory). Samozřejmostí je pak znalost klíče "na druhé straně" - tzn. stejný unikátní klíč jako je v konkrétním SDS bude i u protistrany (např. webový server se kterým SDS komunikuje a se kterým si vyměňuje šifrovaný obsah přes HTTP GET).

Toto poskytuje nejen utajení zprávy, ale i její autentifikaci (autenticitu) - protože pouze ten kdo má klíč dokáže zprávu zašifrovat a dešifrovat. Podmínkou pro autentifikaci je samozřejmě ověření dešifrovaného obsahu (typicky se, kromě vlastních dat a několik záměrně náhodný znaků pro znesnadnění analýzy, vkládá i smluvené ověřovací slovo - a pouze ten kdo zná klíče ho dokáže správně zašifrovat tak, aby ho mohla protistrana dešifrovat a ověřit).

Klíč je velký 16 bajtů, a uživatel jej vždy zapisuje jako 32 znaků (HEX nibbles). Tato funkce používá klíč převedený z hex-nibble znaků na přímou číselnou hodnotu. Jednotlivé nibble textové znaky jsou čteny zleva doprava, a stejným způsobem převedeny do číselné podoby (vždy dva znaky na jeden bajt) a zapsány do paměti, se vzrůstajícím offsetem.

PRAKTICKÝ PŘÍKLAD:

Textová reprezentace klíče = "A1A2A3A4B1B2B3B4C1C2C3C4D1D2D3D4"

Hex-Nibble | Číselná hodnota |  Pořadí (offset v datové paměti) 
-----------+-----------------+---------------------------------
 A1        | 0xA1            |  [0]
 A2        | 0xA2            |  [1]
 A3        | 0xA3            |  [2]
 A4        | 0xA4            |  [3]
 B1        | 0xB1            |  [4]
 B2        | 0xB2            |  [5]
 B3        | 0xB3            |  [6]
 B4        | 0xB4            |  [7]
 C1        | 0xC1            |  [8]
 C2        | 0xC2            |  [9]
 C3        | 0xC3            |  [10]
 C4        | 0xC4            |  [11]
 D1        | 0xD1            |  [12]
 D2        | 0xD2            |  [13]
 D3        | 0xD3            |  [14]
 D4        | 0xD4            |  [15]

Výsledné číselné hodnoty pro klíč, tak jak jsou následně použity uvnitř FULL-C funkcí : 

LITTLE ENDIAN (všimněte si, jak je datová paměť uspořádána do aktuální dword hodnoty se kterou pracujeme)

 dword key[4]; 

 key[0] = 0xA4A3A2A1;
 key[1] = 0xB4B3B2B1;
 key[2] = 0xC4C3C2C1;
 key[3] = 0xD4D3D2D1;

Takže protože používáme Little Endian reprezentaci čísla, tak textový zápis klíče (tak jak jej provede uživatel ve webové administraci SDS, a tak jak jej také musí identicky provést v nastavení protistrany), je specifickým, přesně určeným způsobem, převeden na aktuální použitá 32-bitová čísla - se kterými teprve crypto funkce pracují.

Informace

Pro použití této funkce je potřeba mít firmware od října 2015 (nebo novější).


SDS_crypto

Tato funkce poskytuje následující možnosti (volba v závislosti na hodnotě "Algorithm"). Protože se jedná o symetrickou kryptografii, je výsledek zapsán do stejného paměťového bufferu "PayloadData", velikost výsledku je identická s velikostí vstupních dat.

Protože výhradně používáme Counter-Mode (CTR), viz technické detaily režimu CTR a technické detaily všech režimů, je funkce SDS_crypto() jedinou společnou funkcí pro šifrování a dešifrování. To znamená, že po prvních průchodu čistých dat touto funkcí jsou data zašifrována, a když tyto zašifrovaná data projdou znovu tou samou funkcí (se stejnými počátečními parametry - viz CounterBlock), tak budou opět k dispozici čisté (dešifrované). A pořád dokola. Tím pádem, pokud obě komunikující strany mají stejnou implementaci SDS_crypto(), tak se mohou přímo bezpečně bavit (předávat si zašifrované data).

Poznámka: XTEA se zde využívá vždy v konfiguraci "encrypt", to je dáno volbou mechanismu CTR. Pokud by jsme používali (což neděláme) mechanismy ECB nebo CBC, pak by samozřejmě bylo potřeba určit směr (decrypt/encrypt) a pak by XTEA použila jeden ze dvou svých interních postupů. Pro mechanismus CTR toto neplatí, používá se pořád a stále XTEA-encrypt postup.


Jednotlivé provozní režimy (dané volbou parametru předaným při volání funkce):


[ALGO == 0x00] transparent pass-through

V tomto režimu funkce neprovede jakoukoliv manipulaci s daty. Výsledek je stejný, jako by se funkce nikdy neprovedla (specifické využití zde existuje je, proto je tato možnost nabízena).


[ALGO == 0x10] XTEA-64-CTR

V tomto režimu funkce provede XTEA šifrování/dešifrování.

Klíč 128 bitů, datový blok 64 bitů, mechanismus CTR. Algoritmus umí encryption + decryption (vzhledem k tomu že je to CTR tak tyto dvě věci jsou ve své funkci identické).

Vstupní sestavení 64bit Counter Bloku provádí volající, přičemž CTR inkrementuje všech 64bitů v Counter Bloku pro každý 64bitový blok dat. Počáteční Counter Block je samozřejmě potřeba, spolu se zašifrovanými daty, přenést na protistranu (kde se provede dešifrování).

Mechanismus CTR pro XTEA-64-CTR pracuje tak, že XTEA-64 zakóduje vždy CounterBlock (jako datový vstup XTEA) klíčem "key" (tento klíč je pevný). Výsledek této operace je aplikován pomocí logické funkce XOR na aktuální datový tok (plain-text), takže se postupně všechny položky v datovém bufferu přepíší (zašifrují). Opětovná aplikace XTEA-64-CTR (s CounterBlock nastaveným na stejnou hodnotu, na které byl při začátku šifrování) opět přepíše všechny položky v datovém bufferu (dešifrování).

Zde je velmi jednoduchý příklad (šifrování XTEA-64-CTR):

 // for example, we will now have 32 bytes of data
 #define DATALEN (32) 

 // variables
 char Buf[DATALEN];
 unsigned ret, OutLen;
 unsigned int IV[2];

 .
 .
 .
 // Buf now contains the (plain-text) data

 // encrypt:
 {
   // IV (64bit) - set to some shared starting value, for example 0xFFFFFFFF00000001
   IV[0] = 0xFFFFFFFF;
   IV[1] = 1;

   // encrypt data (inside Buf) using XTEA-64-CTR (0x10)
   ret = SDS_crypto(0x10, (unsigned int *)IV, (void *)Buf, DATALEN);

   // IV has now been incremented accordingly (how much? it is derived from the total data size)
 }

 // Buf now contains (DATALEN bytes) of encrypted data


[ALGO == 0x20] AES-128-CTR

Klíč 128 bitů, datový blok 128 bitů, mechanismus CTR. Algoritmus umí encryption + decryption. Sestavení 128bit Counter Bloku provádí volající, přičemž CTR inkrementuje pouze posledních 32bitů (přesně podle specifikace) v tomto Counter Bloku, pro každých 128bitů dat (datový blok). Počáteční Counter Block je samozřejmě potřeba, spolu se zašifrovanými daty, přenést na protistranu (kde se provede dešifrování).

Úplný příklad je uveden dále na této stránce.


[ALGO == 0x40] CRC-32

Funkce SDS_crypto() umí také spočítat CRC-32 nad blokem dat (velikost dat musí být čistě dělitelná čtyřmi). V tomto případě je položka "CounterBlock" nevyužita.

V tomto režimu vám tato funkce spočítá CRC-32 a výsledek máte k dispozici. Typické využití je pro ochranu dat při přenosu z místa na místo (nebo např. při uložení dat na paměťové médium), aby se dalo detekovat že se data nechtěně změnili (buď je někdo záměrně poškodil při přenosu, nebo se poškodilo paměťové médium).

Pozor, je nezbytné aby "PayloadDataSize" bylo čistě dělitelné 4 - protože CRC-32 pracuje s krokem 32 bitů. Pokud to nedodržíte, nebude vůbec CRC-32 spočteno.

Výsledek (32bit hodnota CRC) je vrácena jako návratová hodnotu funkce SDS_crypto(). Obsah "PayloadData" je nedotčen.

Příklad bude dodán.


[ALGO == 0x80] SHA-256

V tomto režimu funkce provede hash typu "SHA-2" nad uvedeným blokem dat, využita je specificky funkce SHA-256, s odpovídajícím výsledkem 256bit (výsledek má vždy 32 bajtů).

Lze využít k hashování libovolného bloku dat.

V tomto případě je položka "CounterBlock" využita jako ukazatel na pole pro zapsání výsledku hashování - musíte dodat pole/buffer o velikosti 32 bajtů.

Hashovat lze vstupní data od velikosti 0 bajtů až po 64kB, s krokem po 1 bajtu. To znamená "PayloadDataSize" může mít hodnotu od 0x0000 do 0xFFFF.


 // example
 char Message[64];
 unsigned int MessageLength;
 unsigned char Hash[32];

 // write some input / test vector as you like - for example, we use this text as input
 sprintf( Message, "12345678 THIS IS THE TEST VECTOR 12345678");
 MessageLength = strlen(Message);

 // go
 ret = SDS_crypto(0x80, (void *)Hash, (void *)Message, MessageLength);

 // check
 if (32 == ret)
 {
   // Hash -> 32 bytes of SHA-256 HASH of "Message"
 
   printf(" OK ");
 }


Příklad použití režimu AES-128-CTR

Následující příklad provede dvě volání AES-128-CTR, nejprve šifruje 32 bajtů dat, pak výsledek dešifruje. Pro oveření správnosti používáme standardní Testovací Vektory a klíč.

Je nutné, před spuštění programu, v administraci SDS nastavit správný klíč (na hodnotu "2b7e151628aed2a6abf7158809cf4f3c") - jinak výsledky nebudou odpovídat. Samozřejmě, tento klíč je určen jen pro tento příklad! Tento klíč je veřejně známý. V žádném případě jej nepoužívejte pro váši vlastní aplikaci!

// global
unsigned char IV[16];

// reset IV
void setup_iv(void)
{
 // setup the initial IV value

 /* this gives the same result as the followin IV[] setup below
 unsigned int IV4[4];
 IV4[0] = 0xf3f2f1f0; // constant (NONCE)
 IV4[1] = 0xf7f6f5f4; // constant (NONCE)
 IV4[2] = 0xfbfaf9f8; // constant (NONCE)
 IV4[3] = 0xfffefdfc; // only this one (IV[3]) is incremented by 1 on each 16B block
 */

 // First "init.couter" setup, per the F5.1
 IV[0] = 0xF0;
 IV[1] = 0xF1;
 IV[2] = 0xF2;
 IV[3] = 0xF3;
 IV[4] = 0xF4;
 IV[5] = 0xF5;
 IV[6] = 0xF6;
 IV[7] = 0xF7;
 IV[8] = 0xF8;
 IV[9] = 0xF9;
 IV[10] = 0xFA;
 IV[11] = 0xFB;
 IV[12] = 0xFC;
 IV[13] = 0xFD;
 IV[14] = 0xFE;
 IV[15] = 0xFF;
}

// go
void main(void)
{

 unsigned char Buf[32];

 unsigned int i, ret;

 printf("TEST AES-128 ENC/DEC (NIST F5.1 Vectors)\n");
 printf("\n");
 printf("in web-admin, GO set the CRYPT-16B KEY (32-chars) to \"2b7e151628aed2a6abf7158809cf4f3c\" \n");
 printf("\n");

 // unsigned int SDS_crypto(unsigned int Algo, unsigned int *CounterBlock, void *PayloadData, unsigned int PayloadDataSize);

 // NIST 800-38A 2001 - paragraph F5.1 - CTR-AES128 Encrypt

 setup_iv();

 // setup the input data - 32 bytes of plain-text data

 // Block #1 - 6bc1bee22e409f96e93d7e117393172a
 Buf[0] = 0x6b;
 Buf[1] = 0xc1;
 Buf[2] = 0xbe;
 Buf[3] = 0xe2;
 Buf[4] = 0x2e;
 Buf[5] = 0x40;
 Buf[6] = 0x9f;
 Buf[7] = 0x96;
 Buf[8] = 0xe9;
 Buf[9] = 0x3d;
 Buf[10] = 0x7e;
 Buf[11] = 0x11;
 Buf[12] = 0x73;
 Buf[13] = 0x93;
 Buf[14] = 0x17;
 Buf[15] = 0x2a;
 // Block #2 - ae2d8a571e03ac9c9eb76fac45af8e51
 Buf[16] = 0xae;
 Buf[17] = 0x2d;
 Buf[18] = 0x85;
 Buf[19] = 0x57;
 Buf[20] = 0x1e;
 Buf[21] = 0x03;
 Buf[22] = 0xac;
 Buf[23] = 0x9c;
 Buf[24] = 0x9e;
 Buf[25] = 0xb7;
 Buf[26] = 0x6f;
 Buf[27] = 0xac;
 Buf[28] = 0x45;
 Buf[29] = 0xaf;
 Buf[30] = 0x8e;
 Buf[31] = 0x51;

 // encryption -----------------------------------------------------------------

 // debug
 printf("(plain-text) input   = ", Buf);
 for (i = 0; i < 32; i++)
 {
   printf("%02x", Buf[i]);
   if (i == 15) printf(" ");
 }
 printf("\n");

 // AES-128 encrypt (0x20)
 ret = SDS_crypto(0x20, (void *)IV, (void *)Buf, 32);

 // debug
 printf("(encrypted) ret-val = %d\n", ret);
 printf("(encrypted) result  = ");
 for (i = 0; i < 32; i++)
 {
   printf("%02x", Buf[i]);
   if (i == 15) printf(" ");
 }
 printf("\n");


 // decryption -----------------------------------------------------------------

 // Buf[] now contains 32 bytes of AES128-CTR ENCRYPTED data

 // reset the IV to decrypt from start
 setup_iv();

 // AES-128 decrypt
 // (yes, 0x20 is used for both AES-CTR encrypt and decrypt,
 //  due to the way the CTR works)
 ret = SDS_crypto(0x20, (void *)IV, (void *)Buf, 32);

 // debug
 printf("(decrypted) ret-val = %d\n", ret);
 printf("(decrypted) result  = ");
 for (i = 0; i < 32; i++)
 {
   printf("%02x", Buf[i]);
   if (i == 15) printf(" ");
 }
 printf("\n");

 // done -----------------------------------------------------------------------

 printf("\n");

}

Tento příklad má mít takovýto výstup do konzole (typicky 192.168.1.250/echo.htm):

TEST AES-128 ENC/DEC (NIST F5.1 Vectors) in web-admin, GO set the CRYPT-16B KEY (32-chars) to "2b7e151628aed2a6abf7158809cf4f3c"
(plain-text) input = 6bc1bee22e409f96e93d7e117393172a ae2d85571e03ac9c9eb76fac45af8e51
(encrypted) ret-val = 1
(encrypted) result = 874d6191b620e3261bef6864990db6ce 9806f96b7970fdff8617187bb9fffdff
(decrypted) ret-val = 1
(decrypted) result = 6bc1bee22e409f96e93d7e117393172a ae2d85571e03ac9c9eb76fac45af8e51

Příklad nejprve provedl šifrování, a pak dešifrování. Všimněte si, že "(decrypted) result" odpovídá vstupnímu "(plaintext) input".


Base64

Převody do a z Base64 jsou velmi užitečné. Typicky když chcete posílat binární data pomocí REST API (v URL) a spousta dalších příkladů se dá dále najít.

Nejčastější použití u SDS bude odesílání a přijímání dat do a ze serveru (pomocí HTTP GET funkce), např. pro ePortál.


  • Převod dat do Base64

Využijte funkci SDS_ToBase64(). Musíte poskytnou vstupní data, dostatečný buffer pro výstupní data, a konfiguraci.


FLAGS:

  • 0x0 - základní převod dle RFC
  • 0x1 - základní převod s přidanou funkcí safeURL ( převádí výsledek navíc takto: '+' na '-', '/' na '_' )
  • 0x2 - základní převod s přidanou funkcí extended-safeURL ( převádí výsledek navíc takto: '+' na '-', '/' na '_', '=' na '.' )

Pokud chcete výsledný Base64 přenášet v URL, doporučuji nastavit FLAGS na 0x2.

Příklad kódování binárního obsahu v poli "INP[]" do outBuf:

{
 printf("TEST BASE64 ENCODER\n");
 
 char INP[32] = "This is a test."; // example . . .

 char *outBuf, *outBuf2;

 unsigned int outLen, outLen2, outputLength;
 unsigned int inpLen;
 
 // original input length
 inpLen = strlen(INP);
 
 // memory for the output
 outputLength = (((4 * inpLen) / 3) + 3) & (~3);
 outBuf = (char *)malloc(outputLength+1);


 // FLAGS:
 //  0x0 : default Base64 encode
 //  0x1 : Base64 + safeURL 
 //  0x2 : Base64 + extended-safeURL
 outLen = SDS_ToBase64((void *)INP, (void *)outBuf, inpLen, 0x00000002);

 // print results:
 printf("To Base64 + safeURL: \n");
 printf("inp_len = %u \n", inpLen);
 printf("inp_str = %s \n", INP);
 printf("out_len = %u \n", outLen);
 printf("out_str = %s \n\n", outBuf);


 // done
 free(outBuf); outBuf = 0;
}


  • Převod dat z Base64

Využijte funkci SDS_FromBase64(). Musíte poskytnou vstupní data, dostatečný buffer pro výstupní data. Tato funkce nemá konfiguraci, parametr FLAGS nastavte vždy na 0x0.