00 Crypto API Neden?
Linux kernel'de kriptografik işlemler her yerde gerekir: ağ güvenliği (IPsec, TLS), disk şifrelemesi (dm-crypt), dosya sistemi şifrelemesi (fscrypt), kimlik doğrulama (IMA/EVM). Crypto API bu gereksinimleri tek bir tutarlı çerçeve altında toplar.
Crypto API öncesinde her sürücü kendi şifreleme kodunu yazmak zorundaydı. Bu hem kod tekrarına hem de güvenlik açıklarına neden oluyordu. Kernel 2.5.45 sürümünde tanıtılan Crypto API, algoritma bağımsızlığı ve sürücü tekrarsızlığı sağlayarak bu sorunu köklü biçimde çözdü.
Temel Motivasyonlar
Mimari Genel Bakış
Kullanıcı / Alt Sistem
|
v
Crypto API Arayüzü (crypto_alloc_*, crypto_*_setkey, ...)
|
v
Algoritma Kütüphanesi (cipher, hash, aead, akcipher, kpp)
|
+---> Yazılım Implementasyonu (lib/crypto/, arch/arm64/crypto/)
|
+---> Donanım Sürücüsü (drivers/crypto/*)
|
v
DMA / Donanım Motoru (CAAM, QAT, CE, ...)
Gömülü Linux sistemlerinde Crypto API özellikle önem taşır. Kaynakları kısıtlı bir ARM Cortex-A cihazda yazılımsal AES-256, sıkça bir performans sorunu oluşturur. Ancak SoC üreticilerinin çoğu (NXP, Xilinx, Marvell) donanım kriptografi motorlarıyla birlikte sürücüler sunar. Crypto API'yi doğru kullanan bir uygulama, bu motorları sıfır değişiklikle otomatik olarak kullanır.
01 API Katmanları
Crypto API beş ana şifreleme ailesini ve her aile için senkron/asenkron olmak üzere iki kullanım modelini destekler. Doğru aile ve modu seçmek, hem doğruluk hem performans açısından kritiktir.
Şifreleme Aileleri
| Aile | Tip Sabiti | Kullanım Alanı |
|---|---|---|
| cipher (singleblock) | CRYPTO_ALG_TYPE_CIPHER | Tek blok işlemi; ECB/CBC wrapper'lar için temel |
| skcipher | CRYPTO_ALG_TYPE_SKCIPHER | Simetrik blok/akış şifreleme (AES-CBC, ChaCha20) |
| shash / ahash | CRYPTO_ALG_TYPE_SHASH/AHASH | Özet fonksiyonları (SHA-256, BLAKE2, HMAC) |
| aead | CRYPTO_ALG_TYPE_AEAD | Kimlik doğrulamalı şifreleme (AES-GCM, ChaCha20-Poly1305) |
| akcipher | CRYPTO_ALG_TYPE_AKCIPHER | Asimetrik şifreleme/imza (RSA, ECDSA) |
| kpp | CRYPTO_ALG_TYPE_KPP | Anahtar değişimi (ECDH, Diffie-Hellman) |
Senkron ve Asenkron API
Crypto API her aile için iki arayüz sunar:
Request Yaşam Döngüsü
1. crypto_alloc_*() -- transform nesnesi oluştur
|
2. crypto_*_setkey() -- anahtar yükle
|
3. skcipher_request_alloc() -- istek nesnesi ayır
|
4. skcipher_request_set_callback() -- async için callback
|
5. skcipher_request_set_crypt() -- kaynak/hedef scatter-list, IV
|
6. crypto_skcipher_encrypt/decrypt() -- işlemi başlat
|
7. skcipher_request_free() -- istek nesnesini serbest bırak
|
8. crypto_free_skcipher() -- transform nesnesini serbest bırak
Temel Başlık Dosyaları
#include <crypto/hash.h> /* shash / ahash */
#include <crypto/skcipher.h> /* skcipher */
#include <crypto/aead.h> /* aead */
#include <crypto/akcipher.h> /* akcipher */
#include <linux/crypto.h> /* temel tipler */
#include <linux/scatterlist.h> /* sg_init_one, sg_set_buf */
Tüm Crypto API nesneleri GFP_KERNEL ile tahsis edilir ve referans sayımlıdır. Yanlış serbest bırakma bellek sızıntısına yol açar; crypto_free_* çağrıları her zaman eşleştirilmelidir.
02 Hash Kullanımı
Senkron hash arayüzü (shash), kernel modüllerinde en yaygın kullanılan Crypto API bileşenidir. IMA dosya ölçümünden HMAC kimlik doğrulamasına kadar pek çok alt sistem shash kullanır.
SHA-256 ile Özet Hesaplama
Aşağıdaki örnek, tek bir çağrıyla SHA-256 özeti hesaplar:
#include <linux/module.h>
#include <crypto/hash.h>
#include <linux/err.h>
int crypto_sha256_digest(const u8 *data, unsigned int len,
u8 *out /* 32 bayt */)
{
struct crypto_shash *tfm;
struct shash_desc *desc;
int ret;
/* 1. Transform nesnesi */
tfm = crypto_alloc_shash("sha256", 0, 0);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
/* 2. Descriptor -- inline tahsis (stack güvenli) */
desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm),
GFP_KERNEL);
if (!desc) {
ret = -ENOMEM;
goto out_free_tfm;
}
desc->tfm = tfm;
/* 3. Tek adımda özet */
ret = crypto_shash_digest(desc, data, len, out);
kfree(desc);
out_free_tfm:
crypto_free_shash(tfm);
return ret;
}
Çok Parçalı (Multi-Part) Hash
Büyük veya parçalı veriler için init/update/final döngüsü kullanılır:
ret = crypto_shash_init(desc);
if (ret) goto err;
ret = crypto_shash_update(desc, part1, len1);
if (ret) goto err;
ret = crypto_shash_update(desc, part2, len2);
if (ret) goto err;
ret = crypto_shash_final(desc, out); /* digest yaz */
HMAC Kullanımı
HMAC için algoritma adı "hmac(sha256)" şeklinde belirtilir; anahtar setkey ile yüklenir:
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
if (IS_ERR(tfm)) return PTR_ERR(tfm);
ret = crypto_shash_setkey(tfm, key, key_len);
if (ret) goto err;
/* Ardından normal digest akışı devam eder */
Kullanışlı Yardımcı Fonksiyonlar
Kernel Yerleşik Kısayollar
Linux 5.10+ sürümlerinde lib/crypto/ altında bağımsız kullanılabilir fonksiyonlar vardır. Tam Crypto API iskeleti gerektirmeyen modüller için tercih edilebilir:
#include <crypto/sha2.h>
/* Bağımsız SHA-256 — Crypto API gerektirmez */
sha256(data, len, digest); /* 32 bayt digest */
Ancak bu yol donanım offload'dan yararlanamaz. Donanım hızlandırıcısı olan platformlarda tam Crypto API kullanımı önerilir.
03 Simetrik Şifreleme
skcipher (symmetric key cipher) arayüzü, AES gibi blok şifrelerin CBC, CTR, XTS gibi modlarla kullanımını standartlaştırır. Scatter-list tabanlı API sayesinde donanım DMA ile sorunsuz çalışır.
AES-CBC Şifreleme Örneği
#include <crypto/skcipher.h>
#include <linux/scatterlist.h>
struct crypto_skcipher *tfm;
struct skcipher_request *req;
struct scatterlist src_sg, dst_sg;
u8 key[32]; /* AES-256 anahtarı */
u8 iv[16]; /* CBC başlangıç vektörü */
u8 plaintext[64];
u8 ciphertext[64];
int ret;
/* 1. Transform oluştur */
tfm = crypto_alloc_skcipher("cbc(aes)", 0, 0);
if (IS_ERR(tfm)) return PTR_ERR(tfm);
/* 2. Anahtar yükle */
ret = crypto_skcipher_setkey(tfm, key, sizeof(key));
if (ret) goto free_tfm;
/* 3. İstek nesnesi */
req = skcipher_request_alloc(tfm, GFP_KERNEL);
if (!req) { ret = -ENOMEM; goto free_tfm; }
/* 4. Scatter-list kur */
sg_init_one(&src_sg, plaintext, sizeof(plaintext));
sg_init_one(&dst_sg, ciphertext, sizeof(ciphertext));
/* 5. IV ve veri bağla */
skcipher_request_set_crypt(req, &src_sg, &dst_sg,
sizeof(plaintext), iv);
/* 6. Şifrele */
ret = crypto_skcipher_encrypt(req);
skcipher_request_free(req);
free_tfm:
crypto_free_skcipher(tfm);
return ret;
IV Yönetimi
CBC modunda IV, her şifreleme işleminde rastgele olmalı ve şifreli veriyle birlikte iletilmelidir. Kernel içi güvenli rastgele veri üretimi için:
#include <linux/random.h>
u8 iv[AES_BLOCK_SIZE];
get_random_bytes(iv, sizeof(iv));
/* iv'yi şifreli verinin başına ekle, alıcıda çıkar */
AES-CTR ve ChaCha20
CTR modu dolgu gerektirmez; gömülü sistemlerde akış şifresine ihtiyaç duyulduğunda tercih edilir:
/* AES-CTR */
tfm = crypto_alloc_skcipher("ctr(aes)", 0, 0);
/* ChaCha20 -- donanım implementasyonu çoğu ARM SoC'de mevcut */
tfm = crypto_alloc_skcipher("chacha20", 0, 0);
Algoritma Seçim Kılavuzu
| Algoritma | Anahtar Boyutu | Avantaj | Dikkat |
|---|---|---|---|
| cbc(aes) | 128/192/256 bit | Yaygın donanım desteği | IV tahmin edilemez olmalı |
| ctr(aes) | 128/192/256 bit | Paralel işlenebilir, dolgu yok | Nonce tekrar edilmemeli |
| xts(aes) | 256/512 bit | Disk şifreleme için optimize | Kimlik doğrulama sunmaz |
| chacha20 | 256 bit | Yazılımsal hız, basit impl. | Kimlik doğrulama sunmaz |
Dikkat: CBC ve CTR modları yalnızca gizlilik sağlar, bütünlük doğrulaması yapmaz. Bütünlük gerektiren senaryolarda AEAD kullanılmalıdır.
04 AEAD — Kimlik Doğrulamalı Şifreleme
AEAD (Authenticated Encryption with Associated Data), gizlilik ve bütünlük doğrulamasını tek geçişte sunar. Gömülü güvenli haberleşme protokollerinde (TLS, DTLS, IPsec) standart tercihtir.
AEAD Kavramları
AES-GCM ile Şifreleme
#include <crypto/aead.h>
struct crypto_aead *tfm;
struct aead_request *req;
struct scatterlist sg[3]; /* aad, plaintext, tag */
u8 key[32]; /* AES-256-GCM */
u8 nonce[12]; /* GCM için 12 bayt */
u8 aad[16];
u8 plaintext[64];
u8 ciphertext[64 + 16]; /* veri + 16 bayt tag */
int ret;
tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
if (IS_ERR(tfm)) return PTR_ERR(tfm);
/* Tag boyutunu ayarla (16 = maksimum güvenlik) */
ret = crypto_aead_setauthsize(tfm, 16);
if (ret) goto free_tfm;
ret = crypto_aead_setkey(tfm, key, sizeof(key));
if (ret) goto free_tfm;
req = aead_request_alloc(tfm, GFP_KERNEL);
if (!req) { ret = -ENOMEM; goto free_tfm; }
/* Scatter-list: [aad | plaintext/ciphertext+tag] */
sg_init_table(sg, 3);
sg_set_buf(&sg[0], aad, sizeof(aad));
sg_set_buf(&sg[1], plaintext, sizeof(plaintext));
/* Şifreli çıktı: ciphertext alanı */
sg_set_buf(&sg[2], ciphertext, sizeof(ciphertext));
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_crypt(req, sg + 1, sg + 2,
sizeof(plaintext), nonce);
aead_request_set_ad(req, sizeof(aad));
ret = crypto_aead_encrypt(req);
aead_request_free(req);
free_tfm:
crypto_free_aead(tfm);
return ret;
Şifre Çözme ve Tag Doğrulama
Şifre çözme sırasında AEAD otomatik olarak authentication tag'i doğrular. Tag geçersizse -EBADMSG döner; bu değer kesinlikle kontrol edilmelidir:
ret = crypto_aead_decrypt(req);
if (ret == -EBADMSG) {
pr_err("AEAD: kimlik doğrulama başarısız — veri değiştirilmiş!\n");
return ret;
}
Gömülü Güvenli Haberleşme Akışı
Gönderen Alıcı
-------- ------
get_random_bytes(nonce, 12)
aead_encrypt(key, nonce, aead_decrypt(key, nonce,
aad, plaintext) aad, ciphertext)
| |
v v
[nonce | aad | ciphertext | tag] ---> Doğrulama OK / -EBADMSG
ChaCha20-Poly1305 de popüler bir AEAD tercihidir. Yazılımsal performansı AES-GCM'den üstündür; AES donanım desteği olmayan küçük MCU benzeri sistemlerde avantaj sağlar:
tfm = crypto_alloc_aead("rfc7539(chacha20,poly1305)", 0, 0);
05 Donanım Hızlandırıcı Entegrasyonu
Gömülü SoC'lerin büyük çoğunluğunda kriptografi donanım motorları bulunur. Crypto API'nin crypto_engine altyapısı bu motorları standart bir şekilde entegre etmeyi sağlar.
crypto_engine Altyapısı
crypto_engine (crypto/engine.c), asenkron kriptografi işlemlerini sıraya koyar, DMA transferlerini yönetir ve sürücü yazarına basit bir callback modeli sunar. Sürücü yalnızca donanıma özgü prepare/unprepare ve do_one_request fonksiyonlarını uygulamak zorundadır.
#include <crypto/engine.h>
struct my_crypto_dev {
struct crypto_engine *engine;
void __iomem *base;
struct clk *clk;
struct reset_control *rst;
};
/* Engine oluştur */
dev->engine = crypto_engine_alloc_init(dev, true);
if (!dev->engine) return -ENOMEM;
/* Engine başlat */
ret = crypto_engine_start(dev->engine);
if (ret) {
crypto_engine_exit(dev->engine);
return ret;
}
Algoritma Kaydı
Donanım sürücüsü algoritmayı crypto_engine_op yapısı ile kaydeder:
static struct skcipher_engine_alg my_aes_alg = {
.base = {
.base = {
.cra_name = "cbc(aes)",
.cra_driver_name = "cbc-aes-my-hw",
.cra_priority = 400, /* yazılımdan yüksek öncelik */
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct my_aes_ctx),
.cra_module = THIS_MODULE,
},
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
.setkey = my_aes_setkey,
.encrypt = my_aes_encrypt_req,
.decrypt = my_aes_decrypt_req,
},
.op.do_one_request = my_aes_do_one_request,
};
crypto_engine_register_skcipher(&my_aes_alg);
Fallback Zinciri
Donanım her anahtar boyutunu veya modu desteklemeyebilir. Fallback zinciri bu durumu şeffaf biçimde yazılım implementasyonuna devreder:
struct my_aes_ctx {
struct crypto_skcipher *fallback;
/* ... hw-specific fields ... */
};
/* init'te fallback ayır */
ctx->fallback = crypto_alloc_skcipher("cbc(aes)", 0,
CRYPTO_ALG_NEED_FALLBACK);
/* encrypt'te: donanım desteklemiyorsa fallback kullan */
if (!hw_supports_keylen(ctx, keylen)) {
skcipher_request_set_tfm(&rctx->fallback_req,
ctx->fallback);
return crypto_skcipher_encrypt(&rctx->fallback_req);
}
NXP CAAM Sürücüsü (Örnek Platform)
06 dm-crypt ile Şifreli Depolama
dm-crypt, Linux Device Mapper altyapısı üzerinde blok aygıt düzeyinde şifreleme sağlar. Gömülü sistemlerde hassas verilerin depolandığı bölümler (yapılandırma, kullanıcı verisi) için tercih edilen çözümdür.
Kernel Yapılandırması
CONFIG_BLK_DEV_DM=y # Device Mapper
CONFIG_DM_CRYPT=y # dm-crypt hedefi
CONFIG_CRYPTO_AES=y # AES temel
CONFIG_CRYPTO_XTS=y # XTS modu (disk şifreleme standardı)
CONFIG_CRYPTO_SHA256=y # LUKS2 header doğrulama
CONFIG_CRYPTO_USER_API_SKCIPHER=y # kullanıcı alanı API
CONFIG_KEYS=y # kernel keyring
CONFIG_TRUSTED_KEYS=y # TPM destekli anahtarlar (opsiyonel)
LUKS2 ile Şifreli Bölüm Oluşturma
Bu adımlar hedef sistemde veya çapraz derleme ortamında çalıştırılır:
# Şifreli bölüm oluştur (AES-256-XTS varsayılan)
cryptsetup luksFormat --type luks2 /dev/mmcblk0p3
# Bölümü aç
cryptsetup luksOpen /dev/mmcblk0p3 secure_data
# ext4 dosya sistemi oluştur
mkfs.ext4 /dev/mapper/secure_data
# Bağla
mount /dev/mapper/secure_data /mnt/secure
# Kapat
umount /mnt/secure
cryptsetup luksClose secure_data
Otomatik Açma — systemd-cryptsetup
Gömülü sistemlerde otomatik açma için /etc/crypttab kullanılır:
# /etc/crypttab
# Ad Aygıt Anahtar Seçenekler
secure_data /dev/mmcblk0p3 /etc/dm-key.bin luks,timeout=0
dm-crypt ile dm-verity Birleşimi
Depolama Aygıtı (/dev/mmcblk0p3)
|
v
dm-crypt katmanı <-- AES-256-XTS şifreleme/çözme
|
v
dm-verity katmanı <-- SHA-256 blok doğrulama (salt bozulma tespiti)
|
v
Dosya Sistemi (ext4/squashfs)
Gömülü Kullanım İpuçları
07 Kernel Keyring ile Anahtar Yönetimi
Linux kernel keyring servisi, şifreleme anahtarlarını kullanıcı alanından izole ederek korur. Trusted keys TPM ile, encrypted keys ise master key ile korunur.
Keyring Anahtar Türleri
| Tür | Güven Kaynağı | Kullanım |
|---|---|---|
| user | Kullanıcı alanı | Genel amaç; bellekte plaintext |
| logon | Kullanıcı alanı | Kullanıcı alanına asla okunmaz |
| trusted | TPM PCR | Sealed blob; yalnızca belirli platform durumunda açılır |
| encrypted | Master key (trusted/user) | Çekirdek içi şifrelenmiş; DM/fscrypt ile entegre |
Kullanıcı Anahtarı Yükleme
# keyctl ile kernel session keyring'e anahtar ekle
keyctl add user my_dm_key "$(cat /etc/dm-key.bin | xxd -p)" @s
# Anahtar ID'sini al
KEY_ID=$(keyctl search @s user my_dm_key)
# dm-crypt'e keyring anahtarı bağla
cryptsetup luksOpen /dev/mmcblk0p3 secure_data \
--key-description my_dm_key
Encrypted Key ile dm-crypt
# Master key oluştur (trusted veya user türünde)
keyctl add user master_key "$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | xxd -p)" @s
# Encrypted key oluştur (master ile korunur)
keyctl add encrypted dm_key "new user:master_key 32" @s
# dm-crypt'e bağla
dmsetup create secure_data \
--table "0 $(blockdev --getsz /dev/mmcblk0p3) crypt \
aes-xts-plain64 :32:logon:dm_key 0 /dev/mmcblk0p3 0"
Kernel Modülünden Anahtar Okuma
#include <linux/key.h>
#include <linux/keyctl.h>
#include <keys/user-type.h>
struct key *key;
struct user_key_payload *payload;
/* Anahtarı bul */
key = request_key(&key_type_user, "my_dm_key", NULL);
if (IS_ERR(key)) return PTR_ERR(key);
/* Yük veriye eriş */
payload = key->payload.data[0];
memcpy(out_key, payload->data, payload->datalen);
/* Referansı serbest bırak */
key_put(key);
TPM ile Güvenli Önyükleme Entegrasyonu
Platform Önyüklemesi
|
v
TPM PCR Ölçümleri (bootloader, kernel, initrd)
|
v
trusted key unsealed (PCR değerleri eşleşirse)
|
v
encrypted dm key çözülür
|
v
dm-crypt bölümü açılır (kullanıcı müdahalesi olmadan)
08 Hata Ayıklama ve Benchmark
/proc/crypto, tcrypt modülü ve kernel debug altyapıları, kriptografi performansını ölçmeyi ve sorunları tespit etmeyi kolaylaştırır.
/proc/crypto — Kayıtlı Algoritmalar
# Tüm kayıtlı algoritmaları listele
cat /proc/crypto
# Belirli bir algoritmayı filtrele
grep -A 10 "name.*gcm" /proc/crypto
/proc/crypto her algoritma için tür, blok boyutu, özet boyutu, öncelik ve sürücü adını gösterir. Donanım hızlandırıcısının devrede olup olmadığını driver ve priority alanlarından anlayabilirsiniz.
tcrypt Modülü ile Benchmark
# AES-CBC benchmark (test=10: skcipher testleri)
modprobe tcrypt mode=10 sec=5
# AES-GCM AEAD benchmark
modprobe tcrypt mode=211 sec=5
# SHA-256 hash benchmark
modprobe tcrypt mode=304 sec=5
# Tüm mevcut testler
modprobe tcrypt mode=0
Örnek tcrypt Çıktısı
testing speed of async cbc(aes) (cbc-aes-caam) encryption
test 0 ( 16 byte blocks, 16 bytes per update, 1 updates): 1523456 opers/sec, 24375296 bytes/sec
test 1 ( 128 byte blocks, 16 bytes per update, 8 updates): 897234 opers/sec, 114845952 bytes/sec
test 8 (4096 byte blocks,4096 bytes per update, 1 updates): 312441 opers/sec, 1279848448 bytes/sec
Bellek Sızıntısı Tespiti
Crypto API nesneleri çifter tahsis/serbest bırakma gerektirir. Kasan veya kmemleak ile sızıntılar yakalanabilir:
# Konfigürasyon
CONFIG_KASAN=y
CONFIG_KMEMLEAK=y
# Kmemleak tetikleme
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak
Yaygın Hatalar ve Çözümleri
Üretim Ortamı Notları
# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS -- boot süresini azaltır
# (test vektörleri yükleme sırasında atlanır)
# Üretim görüntülerinde etkinleştirilebilir, geliştirme aşamasında KAPALI tutun
CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
# FIPS modu -- onaylı olmayan algoritmalar engellenir
CONFIG_CRYPTO_FIPS=y
# Önyükleme parametresi: fips=1