Josef Troch (troch@mail.ru)
17.4.2002 (poslední změna 23.4.2002)

Cryptfs - Kryptovaný filesystém

Cryptfs je Open Source kryptovaný filesystém - lze ho stáhnout třeba zde. První implementace byla pro Solaris 2.5.1, pak byl portnut pro Linux 2.0 a pro FreeBSD 3.0.

(Pozn.: Důvody pro Solaris - podporuje standartní vnode-ový interface, autoři měli přístup ke zdrojákům jádra a ostatnímu - zvlášť kvalita dokumentace o poznání lepší než u Linuxu; navíc Solaris podporuje loadable kernel moduly, což je dobré pro ladění - pro vyzkoušení změněného filesystému stači odmountovat starou versi, odstranit z paměti (unload) příslušný modul, nahrát tam novou versi a opět filesystém namountovat.)

Motivace

Uživatelé často požadují nějakou metodu pro zabezpečení svých dat, samozřejmě pro ně co nejkomfortnější a s minimálními požadavky na jejich straně. (Tedy nechtějí, aby si každá aplikace kryptovala data po svém a ani je nechtějí ručně kryptovat a dekryptovat před a po práci s aplikací.) Důležitým faktorem je i rychlost, kryptování může být časově náročné. Výrobci operačních systémů naopak nechtějí příliš utrácet za tvorbu zcela nového filesystému.
Z těchto důvodů vzniknul Cryptfs - filesystém navržený jako "stackable vnode layer loadable kernel module". Co to znamená bude vysvětleno později - pro začátek řekněme, že Cryptfs nějak "obalí" klientský filesystém jakousi vrstvou, která kryptuje a dekryptuje soubory transparentně pro uživatele.

Základní fungování - klíče atd.

Základ fungování je z uživatelského hlediska velmi jednoduchý: filesystém se namountuje do libovolného adresáře a zcela transparentně kryptuje data souborů než je předá pod ním ležícímu filesystému; v opačném směru je zase dekryptuje.
K tomu je ovšem třeba někde získat klíč pro kryptování / dekryptování - proto se uživatelé authentifikují systému s použitím nějakého procesu, který se jich zeptá na heslo, toto heslo zahašuje s použitím MD5 algoritmu, čímž se získá klíč, který je předán Cryptfs a ten si ho pak skladuje v paměti (spolu s informacemi o procesu, který ho dodal - reálné user ID a popř. session ID - viz níže). Žádné informace týkající se kryptování se neukládají permanentně, vše si drží Cryptfs v paměti - důsledkem je větší bezpečnost a snadnější použití.
Když chce proces přistupovat k zakryptovaným datům, Cryptfs musí zvolit podle procesu správný klíč, který se použije pro dekryptování - od procesu samozřejmě žádný nedostane, ten o Cryptfs vůbec neví. Tento klíč se tedy dá získat dle jednoho z těchto módů (dá se Cryptfs nastavit, který použít):

Výhody Cryptfs vůči ostatním kryptovaným filesystémům

Cryptfs by měl být rychlejší než "user-level" (tedy běžící v uživatelském adresovém prostoru) filesystémy či filesystémy založené na NFS, jako je CFS a TCFS, protože běží v kernel space-u a tudíž těží z redukovaného počtu context switch-ů a z běhu v privilegovaném režimu. Autoři tvrdí, že je 2 - 37 krát rychlejší na 1 čtení / zápis, což prý vyústí v 12 - 52 % zrychlení aplikace. Cryptfs nabízí vyšší úroveň bezpečnosti tím, že přístup ke kryptovacím klíčům implicitně zakládá na user ID i na process session ID, a také tím, že veškeré své údaje drží v paměti jádra, která by měla být obtížněji dosažitelná pro případný útok.
Tím, že pracuje na úrovni vnode-ů, Cryptfs je snadněji portovatelné než filesystém, který pracuje přímo s disky atp. Cryptfs může fungovat nad (alespoň dle autorů) libovolným jiným filesystémem - příkladem může být UFS či FFS - a tento filesystém bude stále korektní, neprovedou se na něm žádné principielní změny. Filesystém nad kterým Cryptfs funguje může být i na vzdáleném serveru běžící NFS.

Stackable vnode interface

Cryptfs je implementován pomocí "stackable vnode interface". Vnode-m (virtual node) se myslí datová struktura používaná na unixovských systémech pro reprezentaci otevřeného souboru, zařízení, soketu či jiného objektu, ke kterému lze přistupovat přes filesystém. Podstatným vylepšením konceptu vnode-ů je technika nazvaná "vnode stacking" - jde o to, že vnode-ový interface má možnost volat jiný vnode-ový interface "na nižší úrovni". Tedy může existovat několik vnode-ových interfaců (rozhraní) tak, že kód nějaké operace na n-té stack úrovni typicky volá příslušnou operaci z vnode-ového interfacu na n-1 úrovni.
obrázek stackable vnode filesystému Takto tedy vypadá jednoduchý jednoúrovňový stackable kryptovaný filesystém - tedy Cryptfs. Systémová volání (syscalls) jsou převáděny na volání na úrovni vnode-ového interfacu a tento pak volá ekvivalenty příslušných funkcí v Cryptfs. Cryptfs pak vyvolá potřebnou generickou vnode-ovou operaci a ta pak zavolá příslušné nízko-úrovňové operace specifické pro filesystém, nad kterým to běží (třeba UFS).

Fungování Cryptfs podrobněji

Práce s klíči a bezpečnost podrobněji

Kryptovací algoritmus, bloky atp.

Pro zajištění dostatečného zabezpečení je třeba data kryptovat po co největších "kusech" - aby každý znak závisel na mnoha předchozích a tak byl obtížně dešifrovatelný. Extrémem by bylo kryptovat vždy celý soubor dohromady - nevýhoda je zřejmá - pro přečtení 1 znaku z rozsáhlého souboru by bylo třeba celý soubor dekryptovat.
Autoři se rozhodli kryptovat data po "kusech" o velikosti přirozené pro příslušný operační systém - tedy po 4kB či 8kB stránkách. (V závislosti na architektuře - snad:-) )

Autoři zavrhli všechny patentované a licensované algoritmy, také zavrhli DES, protože ho považovali za příliš složitý a pomalý. Vybrán byl nakonec Blowfish - jedná se o symetrickou blokovou šifru s velikostí bloku 64 bitů a délkou klíče nejvýše 448 bitů (tj. 56B). Tento algoritmus je rychlý (prý 8,3 MB / s na Pentiu 150MHz), jednoduchý a dosud nebyl prolomen (v klasické 16ti-kroké variantě). V Cryptfs se používají klíče délky 128 bitů, což je pro Blowfish implicitní hodnota.
Konkrétně Cryptfs používá Blowfish v módu Cipher Block Chaining (CBC) - pro zvýšení bezpečnosti (kód i-tého bloku závisí na i-1-ním bloku). Ovšem CBC se používá pouze uvnitř jednotlivých stránek (4 kB / 8 kB), aby bylo možno kryptovat či dekryptovat každou stránku zvlášť. (Což má i tu výhodu, že pokud nějak dojde ke změně 1 bajtu v souboru, bude možné dekryptovat celý soubor s vyjímkou toho jednoho bloku, který obsahuje změněný znak.)

Blowfish má ještě jednu podstatnou výhodu pro použití ve filesystému - neprodlužuje délku souboru. To je samozřejmě samo o sobě užitečné, ale hlavní výhoda spočívá v tom, že není třeba přepočítávat offsety při přístupu na nějakou pozici do souboru. (Řada kryptovacích algoritmů totiž data zvětšuje, což může způsobit obtížné zjišťování, který z kryptovaných bloků dekryptovat a kde v něm požadovaná data najít.) Další výhodou je, že některé operace jako je stat (zjišťuje velikost, časy, vlastníka ...) mohou být jen jednoduše předány pod Cryptfs ležícímu filesystému.

Jména souborů

Protože uživatelé často používají jména souborů (a adresářů), která v podstatě popisují obsah souboru, je rovněž vhodné je kryptovat a tedy Cryptfs je kryptuje.
Mírný problém je v tom, že výstupem kryptovacího algoritmu je posloupnost bajtů, která může obsahovat i znaky, jež jsou nekorektní v UNIXovkých jménech souborů (zpětné lomítko, znak s číslem 0). Kdybychom tedy přímo ukládali takto vzniklá jména souborů, poškozovali bychom filesystém, nad kterým Cryptfs běží. Řada dalších netisknutelných znaků by mohla způsobovat určité problémy při ls atp. Proto Cryptfs po zakryptování jména souboru ještě převede každé 3 znaky na 4 pomocí techniky nazývané uuencode (každý z nových znaků má kód mezi 48 a 111, což odpovídá 6ti bitům). Dekóduje se logicky opačně. Jména souborů se tedy prodlouží o třetinu, což ale nevadí, jelikož se každé z nich čte vždy celé - žádné offsety.

Výše uvedeným způsobem se ale nezpracovávají položky "." a ".." - jednak kdyby v pod Cryptfs ležícím filesystému nebyly, operace jako přesun do nadřazeného adresáře by vedly k chybě, druhak každý ví, že v každém adresáři jsou obsaženy a případný útočník by tedy znal několik dekryptovaných údajů, které by byly v adresáři uloženy a měl by tedy lepší pozici k prolomení šifrování.

Symbolické linky (symlinks) jsou taktéž kryptovány - jak jejich název, tak hodnoty jejich odkazů.

Mountování - překrytí

Je-li Cryptfs namountován, překryje pro uživatele se správným klíčem i adresář, ve kterém jsou na "spodním" filesystému zakryptovaná data (tedy uživatel vidí v obou adresářích totéž). Pro neauthentifikované (tedy bez klíče) rootovské uživatele se tento adresář se zakryptovanými daty hlásí jako read-only filesystém - což umožní zálohování kryptovaných dat (dekryptovat je před zálohováním není rozumné). Pro všechny ostatní uživatele Cryptfs zakáže jakýkoli přístup do zakryptovaného i dekryptovaného adresáře.

Vnitřní fungování read a write operací

Append

Soubory otevřené pouze pro append ("připojování dat na konec souboru") neposkytují vnode-ovému interfacu žádné informace o velikosti souboru ani informace kde zápis začíná. Pokud velikost souboru neodpovídala hranici stránky, nemůžeme bajty k zápisu korektně kryptovat.
Autoři problém vyřešili tím, že při pokusu o takovéto otevření souboru při předávání "spodnímu" filesystému flag zruší a nastaví otevření pro read & write; zároveň si Cryptfs pamatuje, že soubor měl být původně otevřen pro append. Při zápisu do takovéhoto souboru Cryptfs nejdříve zavolá getattr "spodního" filesystému, čímž zjistí velikost souboru a tu pak přičítá k offsetům, na které se zapisuje.

Obsah adresáře a více klíčů

Problém nastává při výpisu adresáře, který obsahuje jména souborů (a potažmo soubory) zakryptované pomocí různých klíčů. Při dekryptování některé z těchto jmen souborů dekryptujeme "jiným" klíčem, což vyústí v získání zcela jiného řětězce, než byl původně zakryptován. Takovýto výsledek může samozřejmě obsahovat i nekorektní znaky (zpětné lomítko, znak 0).
Tento problém Cryptfs řeší dvoubajtovým kontrolním součtem a jednobajtovou délkou nekryptovaného jména, které se přidají k zakryptované položce adresáře, před jejím uuencodováním. Při čtení obsahu adresáře se každá položka uudecoduje, příslušná část se dekryptuje a porovná se s kontrolním součtem. Nesedí-li kontrolní součet, jméno souboru bylo dekryptováno jiným klíčem, a tak se soubor při zjišťování obsahu adresáře přeskočí. Tedy uživatel vidí v daném adresáři jen soubory zakryptované jeho aktuálním klíčem. K ostatním souborům se tedy nemůže dostat a tudíž by problémy s dekryptováním jiným klíčem neměly nastat u obsahu souborů.

Paměťově mapované soubory

Pro spouštění souborů bylo třeba implementovat i vnode-ové fce pro mapování souborů. Ctyptfs si pro zvýšení rychlosti drží ve vlastní cachi dekryptované stránky takovýchto souborů.
Při výpadku stránky jádro volá vnode-ovou operaci getpage, ta načte 1 či více stránek ze souboru - tato je v Cryptfs napsána pomocí funkcí Paged vnode interfacu (Solaris specific - snižuje přenositelnost, v portech jinak), které volají fci getapage pro získání 1 stránky. getapage funguje logicky - máme-li stránku v cachi - přímo vrátí, jinak pomocí "spodního" filesystému stránku načte, dekryptuje, dá si do cache a vrátí ji obsluze výpadku stránky (+ nějaká kopírování, mapování a odmapování). Obdobně funguje putpage.
Další detaily, které se zde řešily: stránka může mít různé zámky - Cryptfs je musí držet a odemykat ve správném čase a pořadí; MMU si drží o stránkách různé flagy (referenced, modified) - tyto bity se musí updatovat a synchronizovat se svou HW analogií. Filesystém se tedy musí zabývat detaily příslušejícími ke stránkování!

Zero filled pages - některé UNIXy podporují děravé soubory - t.zn. některé stránky souborů nemusí být na disku uloženy, neboť obsahují samé nuly. Má-li být takováto stránka načtena do paměti, systém virtuální paměti ji naplní nulami. Ovšem Cryptfs, když požádá pod ním ležící filesystém o stránku, očekává stránku dříve zakryptovanou (přísl. algoritmem a klíčem) a nemá jak zjistit, že stránka byla zero filled - a při pokusu o dekryptování by vznikla nesprávná data. Proto Cryptfs děravé soubory zakazuje - tento může vzniknout jen pomocí lseek() za konec souboru a zapsání něčeho tam. Tento stav Cryptfs při write detekuje a vynutí zapsání all-zero stránek (po jejich zakryptování).

Srovnání s jinými filesystémy

Autoři ukazují rozsáhlé srovnání Cryptfs nad nativním filesystémem na daném OS s CFS a TCFS a několika nekryptovanými filesystémy na všech podporovaných platformách. Základem jsou dvě sady testů - první je sada obvyklých opeŕací - čtení a zápisů souborů různých délek; druhou pak rozsáhlý build package v tomto filesystému.

Např. u Linuxu na x86 platformě vychází autorům při čtení (část 1. sady testů) u sad krátkých souborů (4 kB) Cryptfs asi 1,5 x pomalejší než u ext2fs (ale asi 3 x rychlejší než CFS a asi 25 x než TCFS), u sad dlouhých souborů (1 MB) stejně rychlé (ale zhruba 26 x rychlejší než CFS a 23 x než TCFS). U zápisu vychází Cryptfs asi 3 x pomalejší než ext2fs, ale stále asi 6 x krát rychlejší než nejlepší výsledek CFS či TCFS. Čtení obsahu adresáře vychází asi 2 x pomalejší než u ext2fs, ale zhruba 2 x rychlejší než TCFS a 10 x než CFS.
Z druhé sady testů na stejné platformě vychází Cryptfs asi 1,12 x pomalejší nž ext2fs, ale asi 1,3 x rychlejší než CFS a 2,1 x než TCFS.

Zdroje a odkazy:

  1. Cryptfs: A Stackable Vnode Level Encryption File System - odtud pochází i oba výše uvedené obrázky.
  2. Tom Brabec: Šifra Blowfish.
  3. Blowfish na stránkách Counterpane Labs.

Tento článek smí být používán libovolně, za předpokladu, že nebude modifikován a že takto bude učiněno se zmínkou o autorovi a odkazem na stránku http://jt.sf.cz, odkud by měla být dostupná aktuální verse tohoto článku.
Snažím se, aby informace zde uvedené byly pravdivé a pokud možno přesné, nicméně nenesu žádnou odpovědnost za to, že tomu tak opravdu je, ani za jakékoli škody, které by někomu v důsledku případných špatných či nepřesných informací z tohoto článku mohly vzniknout.



Jakékoliv dotazy či připomínky mi můžete poslat mailem.

Zpět na stránku referátů a jiných výtvorů