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):
- Cryptfs hledá klíč pouze podle reálného user ID (UID) běžícího procesu (tedy vezme ten klíč,
u něhož se shoduje uložené UID s tímto - existuje-li takový). Toto řešení má tu výhodu, že uživateli
stačí zadat heslo jednou za dobu, kdy je filesystém namountován.
- Cryptfs použije kromě reálného UID ještě session ID (logicky - musí souhlasit obojí).
Tento mód nabízí větší bezpečnost, neboť pouze process, který klíč dodal a jeho child procesy budou
mít přístup ke klíči - protože mají stejné session ID. Pokud se někdo dostane do uživatelova účtu
nebo jeho proces nějak získá příslušné UID, nedostane se jednoduše k zakryptovaným datům, protože se
nepřipojí ke stejné session.
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.
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
- Autoři se rozhodli, že namountovat Cryptfs by měl mít možnost pouze uživatel s
rootovskými právy, ale přesto by neměl mít možnost dekryptovat data jiných
uživatelů.
- Pro přístup se používají reálná UID a ne efektivní UID, protože takto může uživatel spouštět
setuid programy (tedy běžící pod právy jiného uživatele) a tyto programy budou pracovat s
daty (kryptovat a dekryptovat) s použitím klíče dodaného tím, kdo je spustil.
- Program, který se ptá uživatele na heslo pro kryptování požaduje toto heslo alespoň 16 znaků dlouhé
a po zahašování MD5 ho předává Cryptfs použitím speciálního ioctl() (funkce tvořící rozhraní
pro řídící funkce zařízení). Tímto programem lze rovněž Cryptfs sdělit, aby smazal či změnil klíč.
- Nevýhodou řešení je, že pokud má uživatel různé soubory zakryptované různými klíči, musí
měnit klíč, který si o něm "pamatuje" Cryptfs. (Toto nastává typicky u souborů, které má
sdílet nějaká skupina lidí a přesto mají být kryptovány - dle autorů nepříliš časté.)
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í
- Read - dostane-li Cryptfs požadavek na čtení určité části souboru (a je-li autorizovaná),
jednoduše ho protáhne směrem k začátku souboru na hranici celé stránky (kvůli CBC - k dekódování i-
tého bajtu ve stránce potřebuji dekódovat všechny předchozí v dané stránce) a tento požadavek pošle
pod sebou ležícímu filesystému, data od něj dekryptuje a požadovanou část jich dá volajícímu.
- Write - zápis sekvence bajtů je o trochu komplikovanější, neboť uvnitř jedné stránky data
závisí na předchozích a tak je třeba přečíst a dekódovat části stránek před zápisem zbylých jejich
částí.
Příklad řešení požadavku na zápis do již existujícího souboru - zapisujeme pozice 9000 - 25000.
Předpokládejme, že soubor má celkovou velikost větší nebo rovnu 32kB (4 stránky). Stránky číslujeme
od 0.
Cryptfs nejdříve spočte, kterých stránek se zápis dotkne (3 stránky - bajty na offsetu 8192-32767).
Naalokuje 3 prázdné stránky, načte do nich začátek 1. stránky (to, kam se nezapisuje - tedy bajty
8192 - 8999) ze zakryptovaného souboru ve "spodním" filesystému a dekryptuje je.
Pak přeskočí stránky, které se zapisují celé - zde stránku 2 (bajty 16384 - 24575). Poté načte
poslední stránku, které se zápis týká - zde 3. stránka (24576 - 32767) a dekryptuje ji. Zkopíruje do
těchto 3 stránek na příslušné pozice bajty které má zapsat (tj. 9000 - 25000). Nyní máme 3 korektní
stránky nekryptovaných dat - Cryptfs je zakryptuje a zapíše vše od pozice odpovídající 1.
změněnému bajtu (zde 9000) až do konce poslední z měněných stránek (do bajtu 32767). Hotovo.
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:
- Cryptfs: A Stackable Vnode Level
Encryption File System - odtud pochází i oba výše uvedené obrázky.
- Tom Brabec: Šifra
Blowfish.
- 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ů