Securitatea ca cerință de nivel înalt devine din ce în ce mai importantă pentru tot mai multe sisteme încorporate, pe măsură ce acestea evoluează de la aplicații autonome la sisteme conectate capabile să stocheze, să primească și să transmită date, să se actualizeze cu cele mai recente versiuni de software, să fie monitorizate de la distanță și așa mai departe. Astfel de cerințe se propagă rapid chiar și în cele mai mici implementări, în ciuda resurselor de memorie și a puterii de calcul limitate.
Unul dintre primii pași pentru a securiza accesul la un bun valoros, este de a-l face disponibil în cadrul unei politici de utilizare specificate. O astfel de politică ar putea, de exemplu, să restricționeze ce parte a aplicației software îl poate utiliza, forțând-o prin intermediul unei interfețe funcționale definite care nu poate fi ocolită și, în cel mai bun caz, implementată în hardware.
Un exemplu de astfel de capabilitate de izolare este oferit de tehnologia ARM TrustZone, care permite separarea aplicației utilizatorului într-un așa-numit mediu “securizat” și “nesecurizat”, prin definirea unei astfel de stări suplimentare în contextul CPU. În esență, spațiul de memorie al microcontrolerului moștenește un astfel de atribut de “securitate” care, la rândul său, definește politica de acces la o anumită adresă de memorie mapată, atât pentru executarea codului, cât și pentru citirea/scrierea datelor. Dar unde este executată implementarea acestei politici?
Concordanța dintre starea de securitate curentă a procesorului și politica de acces la o anumită adresă este evaluată de o “etapă” de securitate dedicată, situată în interiorul nucleului CPU. Această evaluare se face înainte ca operația de adresare a microcontrolerului să fie propagată către sistemul de magistrale interne (mai multe despre acest aspect mai târziu). În cazul în care politica de acces este încălcată, se lansează o excepție (adică o notificare de întrerupere), conform figurii 1 de mai jos:
În consecință, aplicația poate reacționa la aceasta și, în funcție de necesități, poate efectua acțiuni definite de utilizator (cum ar fi repornirea unui anumit serviciu, înregistrarea evenimentului, semnalizarea unei defecțiuni către alte echipamente etc.).
Privind figura 1, este ușor de observat că software-ul executat în modul “securizat” are acces nerestricționat la date, dar poate prelua și executa direct instrucțiuni numai din zonele de program definite ca fiind securizate. În schimb, software-ul nesecurizat poate accesa numai date nesecurizate și poate executa numai programe nesecurizate. Acesta nu poate accesa nicio resursă securizată.
Dar atunci, cum dialoghează cele două entități între ele? Cazul în care fiecare mediu este complet independent și autonom față de celălalt pare să nu fie cu adevărat semnificativ; este de așteptat ca software-ul de aplicație nesecurizat să aibă nevoie să acceseze resurse și “servicii” care se află în domeniul securizat. Scopul final este de a avea un mod controlat, definit de utilizator, pentru a permite aplicațiilor securizate și nesecurizate să interacționeze între ele.
Din fericire, există un mecanism pentru a face acest lucru. Pentru a executa orice funcție securizată, unitatea centrală de procesare trebuie să își schimbe atributul de securitate în securizat și, pentru a face acest lucru, utilizează o instrucțiune specială dedicată numită “secure gateway” (SG). O instrucțiune SG este asociată cu o instrucțiune de ramificare (adică de “salt”) imediat următoare la adresa funcției securizate dorite; aplicația nesecurizată poate folosi această “trambulină” pentru a-și schimba mai întâi starea în securizată (prin executarea SG) și apoi pentru a trece la funcția dorită, fără a emite o excepție. La întoarcerea funcției securizate, starea procesorului este comutată înapoi în modul nesecurizat.
Un exemplu de alocare a resurselor între mediul securizat și cel nesecurizat este prezentat în figura 2:
Toate perechile de instrucțiuni “SG + adresă de ramificare” trebuie alocate într-o zonă specială definită ca fiind apelabilă nesecurizată. O zonă nesecurizată care poate fi apelată este securizată și are acest atribut special.
De dragul detaliilor, toți parametrii de funcție, cum ar fi indicatoarele de memorie și referințele buffer transmise de funcțiile apelabile nesecurizate pot fi testate în privința atributului lor de securitate, pentru a se asigura că funcția apelantă are într-adevăr permisiunile de acces adecvate (de exemplu, pentru a verifica dacă un buffer de memorie este într-adevăr localizat în memoria nesecurizată și nu se suprapune peste memoria securizată, astfel încât să nu existe riscul de scurgere de date).Această verificare poate fi efectuată prin intermediul unei instrucțiuni specializate “test target”.
În sfârșit, este de asemenea posibil ca, în timp ce unitatea centrală de procesare se află în modul securizat, o funcție nesecurizată să trebuiască să fie apelată înapoi. Acesta ar fi un caz tipic de utilizare pentru a notifica funcția apelantă cu privire la starea cererii, pentru a emite unele notificări legate de RTOS și așa mai departe. Lanțul de instrumente de compilare poate gestiona acest lucru și poate genera o instrucțiune de ramificare specială corespunzătoare care să treacă la starea nesecurizată înainte de apel și să împingă adresa de întoarcere în stiva securizată.
Sistemele embedded sunt puternic bazate pe întreruperi și, într-un astfel de scenariu, trebuie să ne gândim la ce se întâmplă atunci când o întrerupere este emisă în momentul în care procesorul se află într-o anumită stare. În cazul în care o întrerupere nesecurizată are loc în timp ce CPU se află în modul securizat, regiștrii sunt stivuiți în mod implicit pe stiva securizată, iar conținutul ei este eliminat automat. Aceasta pentru a preveni scurgerea neintenționată de informații din zona securizată. Partiționarea excepțiilor (întreruperi periferice) care urmează să fie alocate mediului securizat sau nesecurizat este susținută prin intermediul unui tabel de vectori de întreruperi dedicat și separat în cadrul fiecărui domeniu. În mod similar, există o implementare separată a indicatorilor de stivă, a temporizatoarelor systick și așa mai departe.
Toate acestea sună foarte bine, dar cum sunt definite aceste zone și limite de memorie securizate? Există două unități care sunt interogate în paralel: SAU (security attribution unit) și IDAU (implementation-defined attribution unit). La fiecare accesare a CPU, ambele unități efectuează o căutare a adresei și răspund cu atributul de securitate asociat acelei adrese. Răspunsul celor două unități este combinat pentru a defini atributul adresei; principiul general este că are prioritate cea mai restrictivă dintre cele două unități (ceea ce înseamnă că nu este posibil să se “anuleze” o setare a unei regiuni sigure cu un atribut mai puțin sigur). Până la urmă, atributul de securitate combinat pentru acea adresă este evaluat în funcție de politica definită în tabelul 1. În cazul în care accesul este legitim, acesta poate continua – în caz contrar, este blocat, iar o excepție (întrerupere securizată) este ridicată la nivelul unității centrale de procesare.
În mod evident, configurația SAU (câte regiuni sunt acceptate, setările implicite etc.) poate fi definită în momentul proiectării, iar implementarea IDAU este definită la nivel de execuție, adică este lăsată la latitudinea producătorului dispozitivului.
Pentru o partiționare suplimentară a sarcinilor aplicației, în cadrul fiecărui domeniu pot fi utilizate unități de protecție a memoriei (MPU), pentru a proteja thread-urile individuale unele de altele și pentru a spori robustețea generală a software-ului împotriva erorilor. Figura 3 arată cum ar putea fi partiționată aplicația software pe un microcontroler care suportă TrustZone:
Am înregistrat până acum vreun obiectiv legat de securitate? De fapt, nu încă. TrustZone asigură izolarea între thread-urile aplicației care se execută în mediul nesecurizat și cele care se execută în mediul nesecurizat. Dar nu oferă nicio “securitate” propriu-zisă și nu poate controla ce fire de execuție accesează mediul securizat, adică nu poate impune legitimitatea; mai degrabă previne utilizarea neintenționată sau accesul direct la resurse.
În cele din urmă, decizia de a alege ce parte a aplicației trebuie să fie izolată aparține dezvoltatorului, iar acest lucru depinde în mare măsură de aplicație. Izolarea TZ poate fi utilizată pentru a proteja orice resurse și pentru a spori fiabilitatea unei aplicații, în comparație cu o abordare clasică bazată pe MPU, care depinde doar de un nivel de privilegii (dar este destul de ușor de modificat și de evitat în software). Pe de altă parte, plasarea în interiorul zonei TrustZone a operațiunilor legate de criptografie pare o idee bună pentru majoritatea aplicațiilor care necesită criptografie. Important este că sistemul aplică orice astfel de setări chiar de la începutul execuției (la resetare), iar configurarea acestor limite nu poate fi modificată. Acestea ar putea, de exemplu, să fie stocate într-o zonă specială de memorie care nu poate fi modificată direct de către CPU.
Pentru cerințele tipice legate de securitate (criptografică), bunele practici ar sugera menținerea nivelului de funcționalitate implementat în cadrul mediului securizat cât mai esențial și minimal posibil. Aceasta pentru a limita posibilitatea unui comportament necorespunzător din cauza unor erori de implementare a software-ului, a unor erori de execuție și a exploatării malițioase a oricăror defecte software de către un atacator care încearcă să obțină acces neautorizat la resursele microcontrolerului. Ca efect secundar, respectarea acestui principiu face, de asemenea, ca validarea funcționalității să fie mult mai ușoară în timpul depanării și testării, deoarece există mai puține lucruri de testat.
Ce resurse trebuie să furnizeze un microcontroler capabil să asigure criptografie? Depinde de complexitatea aplicației, deoarece pentru o soluție ‘entry-level’ se poate utiliza și o rutină pur software. Dar având suport pentru algoritmi criptografici în hardware are multe avantaje în ceea ce privește consumul de energie, dimensiunea codului și răspunsul sistemului datorită unei viteze de execuție mai mari.
Acestea fiind spuse, primul bloc de construcție al majorității protocoalelor criptografice este un TRNG (generator de numere aleatorii adevărate – true random number generator), care trebuie validat și testat pentru proprietățile sale entropice și pentru calitatea caracterului aleatoriu (deoarece un RNG prost construit poate strica securitatea oricărui algoritm care îl utilizează).
Pentru stocarea locală, este aproape obligatoriu să se suporte algoritmi simetrici precum AES cu mai multe moduri de operare, pentru a cripta și decripta o mare parte a datelor. În combinație cu algoritmi de ‘hashing’ (un fel de sume de control securizate criptografic) precum SHA-2 sau SHA-3 se pot efectua controale simple de autentificare și se poate verifica dacă conținutul datelor nu a fost modificat.
Pentru o conectivitate mai avansată, dispozitivul trebuie să suporte algoritmi de criptare asimetrică precum RSA sau ECC (eliptic curve cryptography) pentru a susține verificarea identității în conexiunile client/server, pentru a deriva secrete pentru a genera chei de sesiune efemere sau pentru a verifica sursa și legitimitatea unei actualizări de firmware, de exemplu. Toate aceste acceleratoare trebuie să fie capabile, de asemenea, să genereze chei de utilizator pe cip, pentru utilizare locală.
În orice caz, problema dificilă care stă la baza utilizării lor este gestionarea cheilor. Activul cel mai valoros reprezentat de cheia utilizatorului trebuie protejat prin păstrarea confidențialității sale (neexpunerea valorii sale), a integrității sale (nepermiterea modificării imperceptibile a cheii) și asigurarea disponibilității sale. Acest lucru deschide o mulțime de scenarii de luat în considerare, de la momentul în care cheia este injectată (stocată) în sistem, la modul în care este transportată acolo, la modul în care este încărcată în hardware-ul criptografic și protejată împotriva scurgerilor în timpul funcționării.
În mod ideal, materialul de chei nu trebuie să fie manipulat de software-ul de aplicație în text simplu, adică în format clar, deoarece va fi expus în mod periculos. O modalitate simplă de a preveni acest lucru ar putea fi manipularea cheii în zona securizată definită de TrustZone, dar cel mai bine ar fi să se utilizeze cheile numai în cadrul unui subsistem dedicat, izolat de celelalte. O problemă similară se ridică atunci când cheile sunt (de obicei) stocate în memoria nevolatilă; pentru a evita încălcarea confidențialității, tehnici precum “wrapping” (în esență, criptarea cheilor) pot ajuta la protejarea confidențialității cheilor utilizatorului. Întocmirea datelor ‘împachetate’ ca fiind unice pe fiecare microcontroler ajută și mai mult la evitarea scurgerilor de chei partajate și elimină riscul de copiere (clonare) a cheilor de la un sistem existent la altul. Evident, pentru a implementa un astfel de mecanism, este obligatorie o “rădăcină de încredere” pentru stocare, prin urmare subsistemul criptografic trebuie să aibă acces exclusiv la o “cheie de criptare a cheilor” unică pentru fiecare microcontroler. Deoarece rădăcina de încredere este unică pentru fiecare microcontroler, se evită ca o compromitere a unui anumit dispozitiv să permită un atac “de clasă” asupra tuturor echipamentelor care aparțin aceleiași unități.
Un alt aspect important este evaluarea robusteții subsistemului de criptare împotriva atacurilor DPA și SPA, care înregistrează și analizează urmele de consum de putere și pot face inginerie inversă a valorii cheii. Acest tip de atacuri devin din ce în ce mai ieftine și mai rapid de implementat, chiar și pentru atacatorii care nu sunt foarte calificați și care dispun de resurse limitate. În cazul în care accesul fizic la dispozitiv reprezintă o preocupare și nu există alte mijloace de control al accesului în sistem, ar trebui să fie disponibile și utilizate contramăsuri împotriva acestor amenințări. În plus, este de dorit, ca orice funcție de detecție rapidă care poate monitoriza IO conectate la carcasa echipamentului, să genereze o notificare către sistem și, eventual, să preia o marcă temporală atunci când este detectată o încercare de intruziune.
Pentru a ușura viața utilizatorului, subsistemul trebuie să permită o modalitate de a proviziona, adică de a “injecta” în dispozitiv cheile alese de utilizator și de a le stoca în siguranță, pregătindu-le pentru utilizarea ulterioară a aplicației. Microcontrolerul ar trebui să suporte o interfață care să permită furnizarea cheilor atât pe teren, cât și în fabrică, permițând o primă producție simplificată, precum și furnizarea și actualizarea ulterioară pe teren. Această etapă de provizionare trebuie să fie, de asemenea, securizată, adică să nu expună conținutul cheilor în timp ce acestea sunt în tranzit către microcontroler.
În ceea ce privește implementarea software-ului, abordările menționate până acum s-au concentrat asupra unității centrale de procesare principale, care execută software-ul aplicației. Dar în microcontrolerele moderne, alte entități funcționale sunt capabile să transfere în mod autonom date către și dinspre memorie sau periferice, pentru a îmbunătăți performanța prin utilizarea mai eficientă a lățimii de bandă disponibile. Printre exemple se numără motoarele DMA, controlerele grafice, controlerele ethernet și altele asemenea. Toate caracteristicile de izolare legate de TrustZone sunt lipsite de sens pentru acești agenți, deoarece ei pot emite tranzacții pe magistrala principală de interconectare în mod autonom și independent de unitățile de atribuire a securității CPU, fără alte contramăsuri. Prin urmare, este de o importanță vitală ca microcontrolerul să implementeze opțiuni pentru a defini atributul de securitate al fiecăruia dintre aceste canale periferice principale și să dispună de “filtre” specifice amplasate pe partea secundară (slave) a comunicației (în fața memoriilor și a perifericelor mapate în memorie). Încălcările politicilor de acces la nivel de sistem trebuie, de asemenea, să poată declanșa excepții (adică notificări de întrerupere) către unitatea centrală de procesare principală, pentru a lua măsuri corective.
Orice sistem cu microcontroler ar fi aproape inutil fără capabilitatea de a efectua operații de intrare și ieșire pe semnale digitale și analogice externe. Protejarea unor astfel de interfețe împotriva utilizării abuzive este, de asemenea, o cerință fundamentală pentru a preveni intervențiile frauduloase, deoarece acesta este, în mod evident, modul predefinit de interacțiune cu microcontrolerul. Proiectantul precaut trebuie să se asigure că microcontrolerul poate restricționa nivelurile de acces la porturile de intrare/ieșire și la periferice, pentru a împiedica software-ul să “preia controlul” în mod rău intenționat asupra unei interfețe, să interfereze cu aceasta sau să intercepteze comunicația (încălcând astfel politica de securitate). Funcționalitatea implementată trebuie să permită izolarea în siguranță a perifericelor și a porturilor acestora unele de altele. Ținând cont de constrângerile de spațiu, în special în cazul microcontrolerelor de mici dimensiuni, numărul de interfețe funcționale poate fi mult mai mare decât numărul de pini fizici disponibili pe capsulă; multe dintre acestea sunt multiplexate împreună pentru ca utilizatorul să poată alege dintre ele.
În timpul fazei de dezvoltare, pentru a testa software-ul și a verifica dacă se comportă conform așteptărilor, o sondă de depanare Jtag este aproape obligatorie. Prin însăși definiția sa, o astfel de interfață este capabilă să acceseze aproape toate resursele de pe cip și, prin urmare, constituie o ușă de rezervă semnificativă pentru orice aplicație care este ulterior implementată pe teren. Cazurile de utilizare pentru securizarea Jtag ar putea fi foarte diferite: unii ar dori să o securizeze permanent, alții ar putea dori să mențină capabilitatea de depanare pe teren și să protejeze pur și simplu accesul. Oricare ar fi strategia aleasă, nu trebuie să fie posibilă ocolirea unei protecții permanente sau accesul la acesta fără o autorizație corespunzătoare; un cod sau o cheie de autentificare deținută de dezvoltator trebuie să fie necesar într-un mecanism de tip provocare-răspuns, iar acesta din urmă trebuie finalizat cu succes pentru a permite orice comunicație succesivă. În fine, dispozitivul trebuie să suporte un mecanism securizat care să permită trimiterea dispozitivului înapoi la fabrică pentru o analiză suplimentară, în cazul în care se suspectează un defect al produsului; acest lucru ar putea implica ștergerea tuturor activelor secrete stocate, menținând totuși interfața securizată.
După dezvoltare, imaginea finală a aplicației este pregătită pentru a fi implementată pe teren. O parte din aceasta ar putea fi supusă unor actualizări ulterioare, dar o parte din ea trebuie să fie imuabilă pentru a se asigura că aplicația sau codul programului de încărcare la pornire se află într-o stare bine cunoscută în orice moment. Pentru a susține această cerință, microcontrolerul trebuie să aibă capabilitatea de a proteja permanent părțile definite de utilizator ale memoriei nevolatile împotriva modificării.
Nu în ultimul rând, fiecare microcontroler este supus unui lung proces de testare în fabrică, pentru a verifica funcționarea sa corectă în conformitate cu specificațiile tehnice. În același timp, multe dintre rezultatele acestor teste (cum ar fi valorile de ajustare, datele specifice producției etc.) și alte setări legate de microcontroler sunt stocate pe dispozitiv în timpul testelor din fabrică. Acest mod special de testare nu are nicio semnificație pentru un utilizator final, dar fiind foarte puternic, poate accesa, controla și, eventual, manipula toate resursele cipului. Din punct de vedere al securității, acesta este un alt potențial acces secundar, iar producătorul ar trebui să se asigure că un mod de testare nu poate fi introdus din greșeală sau cu rea intenție, odată ce dispozitivul a ieșit din fabrică și se află în mâinile clientului.
Căutarea microcontrolerului adecvat care să susțină toate sau majoritatea cerințelor de mai sus poate fi o sarcină descurajantă. Din fericire, Renesas a proiectat seria RA de microcontrolere exact cu astfel de obiective. Seriile RA6 și RA4 de microcontrolere Renesas includ dispozitive care dispun de un CPU ARM Cortex-M33 cu TrustZone și unități de protecție a memoriei securizate. Acestea permit programarea granițelor și a setărilor securizate și nesecurizate pentru toate tipurile de memorie încorporate într-un mod simplu și ușor. Acestea încorporează Secure Crypto Engine, un subsistem criptografic (ilustrat în figura 4) care oferă o funcționalitate comparabilă a elementelor securizate la o performanță mai mare și un cost mai mic al listei de materiale. SCE include acceleratoare de algoritmi criptografici de ultimă generație, un TRNG, generarea de chei, injectarea de chei în fabrică, contramăsuri SPA/DPA și implementează o rădăcină de încredere hardware securizată prin intermediul unei chei hardware unice. La nivelul sistemului, controlerele DMA, magistralele master, perifericele și pinii I/O au atribute de securitate dedicate, fiind implementată o funcționalitate de detectare a falsificării. Microcontrolerul implementează managementul ciclului de viață al dispozitivului cu gestionarea integrată a depanării securizate/neprotejate, a programării securizate/neprotejate, a procedurii de returnare a materialului și a protecției modului de testare. Orice bloc de memorie nevolatilă poate fi protejat permanent la discreția utilizatorului, permițând astfel o definire flexibilă a rădăcinii de încredere (Root Of Trust). Pentru mai multe informații despre caracteristicile de securitate ale familiei RA, vă rugăm să vizitați www.renesas.com/RA
Renesas Electronics Europe | https://www.renesas.com
Bibliografie
Evaluation Kit for RA6M4 MCU Group.
R01UH0890EJ0110 RA6M4 Group User’s Manual: Hardware.
R01AN5562EJ0100 Standard Boot Firmware for the RA family MCUs Based on Arm® Cortex®-M33