Di masa awal kuliah ilmu komputer, mahasiswa diajari berbagai macam hal dasar mengenai komputer. Hal dasar pertama adalah bahwa data bisa dikonversi menjadi bilangan (data di sini berupa teks, foto, video, suara, dsb). Lalu setiap bilangan bisa dikonversi dalam representasi biner supaya bisa diproses oleh komputer digital. Beberapa pelajaran awal arsitektur komputer akan membahas mengenai gerbang logika, lalu bagaimana gerbang logika ini bisa dipakai untuk menjumlahkan. Setelah kita bisa menjumlahkan maka kita juga bisa mengurangi (di sini akan diajarkan mengenai komplemen 2). Setelah bisa menjumlahkan, kita akan bisa mengalikan.
Biasanya mahasiswa masih mengerti konsep-konsep tersebut ketika diajarkan, walau mungkin belum bisa menyambungkan ilmunya dengan komputer yang ditemui sehari-hari. Di masa-masa akhir kuliah, berbagai topik praktis diajarkan. Di sini mahasiswa akan bisa membuat web atau aplikasi praktis. Tapi biasanya ada gap pengetahuan dari hal yang dasar di tahun pertama dengan hal praktis di tahun terakhir.
Contoh yang sederhana adalah masalah manipulasi bit atau pemahaman bahwa berbagai manipulasi data bisa dipahami dari level biner. Kebanyakan orang akan bingung berhadapan dengan hex editor untuk melihat data sebuah file, dan bahkan menganggap kalau hex editor (saja) cukup untuk menghack apa saja.
Supaya praktis, di posting ini saya hanya akan membahas manipulasi bit untuk encoding base N. Di posting lain saya akan membahas encoding teks di level biner (terutama teks Unicode).
Banyak orang yang akan bingung jika diminta mengimplementasikan base64 tanpa memakai library, meskipun sering kali melihat data dalam bentuk base64 dan mengerti pemakaiannya di berbagai tempat (misalnya di aplikasi web). Dalam kebanyakan bahasa pemrograman, programmer memang tinggal pake dan tidak penting untuk tahu detailnya.
Lalu kenapa perlu tahu implementasi base64 (atau base N secara umum) di bidang security? Jika bidangnya adalah analisis malware, banyak malware memakai tabel base64 yang custom. Untuk satu topik khusus ini (base64) sudah ada banyak pembahasan, tool, dan triknya sehingga gampang dicari. Tapi jika tidak paham prinsipnya, jika ada kasus yang sedikit berbeda maka Anda akan kebingungan.
Base64 saya ambil sebagai contoh untuk satu hal yang berhubungan dengan representasi/manipulasi bit tapi masih cukup sederhana. Banyak hal security yang berhubungan dengan representasi bit, contoh praktis sederhana adalah format private key di sebuah SSL certificate. Contoh yang lebih kompleks adalah row hammer attack untuk melakukan bit-flipping pada RAM atau SPA (simple power analysis) untuk mendapatkan key RSA jika implementasinya tidak aman.
Masih kembali ke contoh praktis pemakaian base N:
- base85: dipakai dalam format PDF
- base64: untuk berbagai data di web (misalnya data URI di web)
- base58: dipakai untuk bitcoin address
- base36: untuk safe url encoding
- base32: dipakai untuk geohash dan di beberapa game Nintendo lama
- base16: representasi heksadesimal (hex), dipakai di mana-mana
- base8: representasi oktal dipakai di mode/permission file di OS UNIX based
Sebenarnya semua di atas hanyalah masalah “basis bilangan”. Dalam biner kita memakai dua simbol saja: 0 dan 1. Dalam oktal kita memakai simbol dari 0 sampai 7. Dalam desimal kita memakai sepuluh simbol dari 0 sampai 9. Dalam heksadesimal, kita menyetujui untuk memakai 0 sampai 9, ditambah dengan a sampai f. Tapi untuk basis bilangan selain itu, ada banyak varian huruf yang bisa kita gunakan.
Untuk contoh berikutnya, saya akan menggunakan data ini yang akan di encode: “Hello”. Untuk memudahkan, saya akan menggunakan encoding ASCII. Di tulisan berikutnya saya akan bahas mengenai berbagai encoding teks yang lain.
Angka di bawah masing-masing huruf adalah nomor huruf tersebut dalam tabel ASCII. Di bawahnya saya tampilkan juga representasi biner dari angka tersebut. Konversi basis 2 ke basis lain yang bisa direpresentasikan dalam 2 pangkat n (8, 16, 32, 64) bisa dilakukan sangat mudah. Representasi oktal jarang digunakan untuk mengencode data, jadi saya akan langsung ke heksadesimal dan base 2 pangkat n berikutnya (base32, base64).
Heksadesimal (base 16)
Dalam heksadesimal (2 pangkat 4), tiap 4 bit kita translasikan menjadi 1 simbol.
Atau singkatnya, kata hello bisa diencode dalam heksadesimal menjadi “48656c6c6f”. Secara praktis ini bisa dilakukan dengan berbagai cara. Misalnya dengan shell command ini:
echo -n Hello| xxd -p
Dan untuk mengembalikannya
echo -n 48656c6c6f|xxd -r -p
Konversi dari biner ke heksadesimal bisa dilakukan dengan tabel semacam ini. Tapi supaya singkat saya bisa mengatakan bahwa simbol untuk base16 adalah: “0123456789abcdef”. Menggunakan string seperti ini akan lebih mudah ketika kita memakai encoding base N yang lebih besar.
Base32
Berikutnya ke base32: untuk ini kita bisa memakai simbol “ABCDEFGHIJKLMNOPQRSTUVWXYZ234567”. Kenapa dipilih karakter-karakter itu, kenapa bukan “ABCDEFGHIJKLMNOPQRSTUVWXYZ012345” (atau angka dulu dan sisanya huruf). Ini dipilih karena beberapa alasan:
- Supaya tidak tertukar antara 0/O, 1/I
- Dalam kasus tertentu jika 0..9 ada di depan, nanti disangka heksadesimal
Tentunya jika kita membuat aplikasi sendiri yang tidak perlu interoperasi dengan aplikasi lain, kita bisa membuat tabel sendiri. Tabel yang standar ini diambil dari http://www.rfc-editor.org/rfc/rfc4648.txt
Karena 32 merupakan 2 pangkat 5, kita bisa membagi per 5 bit. Gambar di bawah ini adalah serangkaian bit di gambar pertama, tapi dikelompokkan jadi 5 bit per group. Jadi di kotak pertama tercantum ‘01001’ atau 9 dalam desimal. Karakter ke 9 (dihitung dari 0) dari string “ABCDEFGHIJKLMNOPQRSTUVWXYZ234567” adalah huruf J. Demikian seterusnya.
Kita bisa mengecek hal di atas dengan menggunakan modul Python base64 (meski namanya base64, modul ini mendukung base 16, 32, 64, dan 85).
import base64 print base64.b32encode("Hello") #hasilnya: JBSWY3DP print base64.b32decode("JBSWY3DP") #hasilnya: Hello
Base64
Contoh base64 juga sangat serupa karena 64 bit juga merupakan 2 pangkat n, dengan n = 6. Di contoh base32 kebetulan jumlah bitnya kelipatan 5, nah bagaimana jika jumlah bit tidak kelipatan n? kita biarkan bagian terakhir kurang dari n bit. Berikut ini contohnya dalam base64:
Sebenarnya string di atas sudah cukup sebagai representasi base64, tapi dalam konvensi biasanya ditambahkan padding karakter ‘=’ jika input bukan kelipatan 3. Contohnya karakter ‘A’ jika diencode akan jadi ‘QQ==’, dan ‘AA’ jadi ‘QUE=’, tapi AAA karena kelipatan 3 jadi ‘QUFB’.
Kita bisa memakai program base64 untuk mengencode dan decode base64:
echo -n Hello| base64
Dan untuk mendecodenya (di Linux gunakan -d huruf kecil, sedangkan gunakan parameter kapital -D jika Anda memakai OS X atau BSD):
echo -n SGVsbG8= | base64 -d
Atau dalam Python:
import base64
print base64.b64encode("Hello") #hasilnya: SGVsbG8=
print base64.b64decode("JBSWY3DP") #hasilnya: Hello
Sebagai catatan: ada banyak varian base64, tergantung dari karakter yang digunakan (lengkapnya bisa dilihat di Wikipedia: https://en.wikipedia.org/wiki/Base64)
Base58
Seperti sudah dibahas sebelumnya: jika basisnya dalam bentuk 2 pangkat n, maka ini mudah dilakukan, cukup dengan memecah per n bit. Dalam implementasinya ini biasanya dilakukan dengan menggunakan operator SHIFT dan AND. Untuk basis lain, kita bisa menggunakan pembagian berulang. Karena pembagian berulang ini kurang efisien, base yang lain biasanya dipakai untuk menyimpan data yang tidak besar (contohnya: bitcoin address memakai base58, integer dalam PDF memakai base85).
Saya akan memberikan contoh encoding base58 untuk data di atas. Pertama kita gabungkan semua digit biner, lalu konversi bilangan biner menjadi desimal. Konversi ke desimal ini agar sekedar mudah dibaca manusia saja. Angka biner 0100100001100101011011000110110001101111 dalam desimal adalah 310939249775. Kita lakukan pembagian berulang dengan 58 dan catat sisanya. Sisanya ini dipakai untuk menjadi indeks bagi tabel: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
Hasilnya kita balik dari bawah ke atas yaitu: 9Ajdvzr. Kita bisa mengecek ini dengan modul base58 di python (perlu diinstall terpisah dengan pip install base58).
import base58
print base58.b58encode("Hello")
print base58.b58decode("9Ajdvzr")
Penutup
Ini merupakan satu bagian saja dari berbagai tulisan dasar mengenai ilmu komputer dan security. Semoga saya bisa terus sabar menuliskan keseluruhan topiknya.
Adakah satu buku yang bisa mengajarkan topik security ini dari level bit? setahu saya tidak ada. Buku Silence on the wire memberikan overview yang bagus mengenai banyak masalah security dari level bit (bahkan membahas singkat dari mulai algabar boolean), tapi topik-topik lain perlu dipelajari di berbagai teks dasar mengenai ilmu komputer.