PyDERASN: carane aku wrote perpustakaan ASN.1 karo slot lan blobs

ASN.1 iki minangka standar (ISO, ITU-T, GOST) saka basa sing njlentrehake informasi terstruktur, uga aturan kanggo ngodhe informasi iki. Kanggo kula, minangka programmer, iki mung format liyane kanggo serializing lan presenting data, bebarengan karo JSON, XML, XDR lan liyane. Iki umum banget ing urip saben dina, lan akeh wong sing nemoni: ing seluler, telpon, komunikasi VoIP (UMTS, LTE, WiMAX, SS7, H.323), ing protokol jaringan (LDAP, SNMP, Kerberos), ing kabeh sing babagan kriptografi (X.509, CMS, standar PKCS), ing kertu bank lan paspor biometrik, lan liya-liyane.

Artikel iki gegayutan karo PyDERASN: Python ASN.1 perpustakaan aktif digunakake ing proyèk related kanggo kriptografi ing Atlas.

PyDERASN: carane aku wrote perpustakaan ASN.1 karo slot lan blobs
Umumé, ASN.1 ora pantes disaranake kanggo tugas kriptografi: ASN.1 lan codec-codec kasebut rumit. Iki tegese kode ora bakal prasaja, lan iki tansah vektor serangan ekstra. Delengen wae menyang dhaptar kerentanan ing perpustakaan ASN.1. Bruce Schneier ing dheweke Teknik kriptografi uga menehi saran supaya ora nggunakake standar iki amarga kerumitan: "Encoding TLV sing paling misuwur yaiku ASN.1, nanging pancen rumit lan kita isin saka iku." Nanging, sayangé, dina iki kita duwe infrastruktur kunci umum ing ngendi dheweke digunakake kanthi aktif Sertifikat X.509, CRL, OCSP, TSP, protokol CMP, CMC, pesen CMS, lan akeh standar PKCS. Mulane, sampeyan kudu bisa nggarap ASN.1 yen sampeyan nindakake apa wae sing ana hubungane karo kriptografi.

ASN.1 bisa dienkode nganggo macem-macem cara/codec:

  • BER (Aturan Encoding Dasar)
  • Cer (Aturan Encoding Kanonik)
  • DER (Aturan Encoding Distinguished)
  • GSER (Aturan Encoding String Umum)
  • JER (Aturan Pengkodean JSON)
  • LWER (Light Weight Encoding Rules)
  • REO (Aturan Pengkodean Oktet)
  • PER (Aturan Pengkodean Paket)
  • SER (Aturan Pengodean Khusus Sinyal)
  • MURID (Aturan Pengkodean XML)

lan sawetara liyane. Nanging ing tugas kriptografi, ing praktik, loro digunakake: BER lan DER. Malah ing dokumen XML sing ditandatangani (XMLDSig, XAdES) isih bakal ana obyek ASN.64 DER sing dienkode Base1, kaya ing protokol berorientasi JSON acme saka Ayo Encrypt. Sampeyan bisa luwih ngerti kabeh codec lan prinsip kode BER/CER/DER ing artikel lan buku: ASN.1 ing tembung prasaja, ASN.1 - Komunikasi antarane sistem heterogen dening Olivier Dubuisson, ASN.1 Lengkap dening Prof John Larmouth.

BER minangka format TLV binar byte-oriented (contone PER, populer ing komunikasi seluler - bit-oriented). Saben unsur dienkode minangka: tag (Tag), ngenali jinis unsur sing bakal dienkode (integer, string, tanggal, lsp), dawa (Length) isi lan isi dhewe (Value). BER opsional ngidini sampeyan ora nemtokake nilai dawa kanthi nyetel nilai dawa tanpa wates khusus lan mungkasi pesen End-Of-Octets kanthi tandha End-Of-Octets. Saliyane enkoding dawa, BER nduweni akeh variasi ing cara ngodhe jinis data, kayata:

  • INTEGER, OBJEK IDENTIFIER, BIT STRING lan dawa unsur ora bisa dinormalisasi (ora dikode ing wangun minimal);
  • BOOLEAN bener kanggo isi non-nol;
  • BIT STRING bisa ngemot "ekstra" nol bit;
  • BIT STRING, OCTET STRING lan kabeh jinis senar sing asale, kalebu tanggal/wektu, bisa dipérang dadi potongan-potongan dawa variabel, sing dawane ora diweruhi sadurunge nalika (de) enkoding;
  • UTCTime/GeneralizedTime bisa uga duwe macem-macem cara kanggo nemtokake zona wektu ngimbangi lan "ekstra" pecahan nol detik;
  • Nilai DEFAULT SEQUENCE bisa dikode utawa ora;
  • Nilai sing dijenengi saka bit pungkasan ing BIT STRING bisa opsional ora dikodekan;
  • SEQUENCE (OF) / SET (OF) bisa duwe urutan unsur.

Amarga kabeh kasebut ing ndhuwur, ngodhe data supaya padha karo wangun asli ora mesthi bisa. Mulane, subset saka aturan iki nemokke: DER - strictly ngatur mung siji cara enkoding bener, kang kritis kanggo tugas cryptographic ngendi, contone, ngganti siji dicokot bakal nggawe teken utawa checksum ora sah. DER duwe kerugian sing signifikan: dawa kabeh unsur kudu dingerteni luwih dhisik ing wektu enkoding, sing ora ngidini serialisasi stream data. Codec CER ora duwe kekurangan iki, uga njamin perwakilan data sing ora ambigu. Sayange (utawa untung yen kita ora duwe dekoder sing luwih rumit?), Ora dadi populer. Mula, ing praktik kita nemoni panggunaan "campuran" data sing dikodekan BER lan DER. Amarga CER lan DER minangka subset saka BER, dekoder BER bisa nangani.

Masalah karo pyasn1

Ing karya kita nulis akeh program Python sing gegandhengan karo kriptografi. Lan sawetara taun kepungkur, meh ora ana pilihan perpustakaan gratis: iki minangka perpustakaan tingkat rendah sing ngidini sampeyan mung encode / decode, contone, integer lan header struktur, utawa perpustakaan iki. pyasn1. Kita manggon ing sawetara taun lan ing wiwitan kita seneng banget, amarga ngidini sampeyan nggarap struktur ASN.1 kaya karo obyek tingkat dhuwur: contone, obyek sertifikat X.509 sing didekode ngidini sampeyan ngakses lapangan liwat antarmuka kamus: cert["tbsCertificate"] ["SerialNumber"] bakal nuduhake nomer seri sertifikat iki. Kajaba iku, sampeyan bisa "nglumpukake" obyek Komplek kanthi nggarap minangka dhaptar, kamus, lan banjur mung nelpon fungsi pyasn1.codec.der.encoder.encode lan njaluk perwakilan serialized saka document.

Nanging, kekurangan, masalah lan watesan wis dicethakaké. Ana lan, sayangé, isih ana kasalahan ing pyasn1: nalika nulis, salah sawijining jinis dhasar ing pyasn1 yaiku GeneralizedTime, salah decoded lan encoded.

Ing proyèk kita, kanggo ngirit ruang, kita kerep nyimpen mung path file, offset lan dawa ing bita saka obyek sing arep kita referensi. Contone, file sing ditandatangani kanthi sewenang-wenang bakal ana ing struktur CMS SignedData ASN.1:

  0     [1,3,1018]  ContentInfo SEQUENCE
  4     [1,1,   9]   . contentType: ContentType OBJECT IDENTIFIER 1.2.840.113549.1.7.2 (id_signedData)
 19-4   [0,0,1003]   . content: [0] EXPLICIT [UNIV 16] ANY
 19     [1,3, 999]   . . DEFINED BY id_signedData: SignedData SEQUENCE
 23     [1,1,   1]   . . . version: CMSVersion INTEGER v3 (03)
 26     [1,1,  19]   . . . digestAlgorithms: DigestAlgorithmIdentifiers SET OF
                           [...]
 47     [1,3, 769]   . . . encapContentInfo: EncapsulatedContentInfo SEQUENCE
 51     [1,1,   8]   . . . . eContentType: ContentType OBJECT IDENTIFIER 1.3.6.1.5.5.7.12.2 (id_cct_PKIData)
 65-4   [1,3, 751]   . . . . eContent: [0] EXPLICIT OCTET STRING 751 bytes OPTIONAL

                 ТУТ СОДЕРЖИМОЕ ПОДПИСЫВАЕМОГО ФАЙЛА РАЗМЕРОМ 751 байт

820     [1,2, 199]   . . . signerInfos: SignerInfos SET OF
823     [1,2, 196]   . . . . 0: SignerInfo SEQUENCE
826     [1,1,   1]   . . . . . version: CMSVersion INTEGER v3 (03)
829     [0,0,  22]   . . . . . sid: SignerIdentifier CHOICE subjectKeyIdentifier
                               [...]
956     [1,1,  64]   . . . . . signature: SignatureValue OCTET STRING 64 bytes
                     . . . . . . C1:B3:88:BA:F8:92:1C:E6:3E:41:9B:E0:D3:E9:AF:D8
                     . . . . . . 47:4A:8A:9D:94:5D:56:6B:F0:C1:20:38:D2:72:22:12
                     . . . . . . 9F:76:46:F6:51:5F:9A:8D:BF:D7:A6:9B:FD:C5:DA:D2
                     . . . . . . F3:6B:00:14:A4:9D:D7:B5:E1:A6:86:44:86:A7:E8:C9

lan kita bisa njaluk file mlebu asli ing ngimbangi 65 bait, 751 bait dawa. pyasn1 ora nyimpen informasi iki ing obyek decoded sawijining. Sing diarani TLVSeeker ditulis - perpustakaan cilik sing ngidini sampeyan decode tag lan dawa obyek, ing antarmuka sing diprentahake "go to the next tag", "go inside the tag" (mlebu ing obyek SEQUENCE), "menyang tag sabanjure", "marang offset lan dawa obyek ing ngendi kita." Iki minangka "manual" lumaku liwat data ASN.1 DER-serialized. Nanging ora bisa nggarap data BER-serialisasi kanthi cara iki, amarga, contone, string byte OCTET STRING bisa dienkode ing wangun sawetara potongan.

Kelemahan liyane kanggo tugas pyasn1 yaiku ora bisa ngerti saka obyek sing didekode manawa ana lapangan sing ana ing SEQUENCE utawa ora. Contone, yen struktur ngemot lapangan SEQUENCE OF Smth OPTIONAL lapangan, banjur bisa rampung absen saka data mlebu (OPTIONAL), utawa bisa saiki, nanging dawa nol (dhaftar kosong). Umumé, iki ora bisa ditemtokake. Lan iki perlu kanggo verifikasi ketat validitas data sing ditampa. Mbayangno yen sawetara panguwasa sertifikasi bakal ngetokake sertifikat kanthi data sing "ora sakabehe" bener saka sudut pandang skema ASN.1! Contone, wewenang sertifikasi "TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı" ngluwihi watesan sing diidinake ing sertifikat root. RFC 5280 watesan ing dawa komponen subyek - ora bisa jujur ​​decoded miturut skema. Codec DER mbutuhake lapangan sing nilaine padha karo DEFAULT ora dikode nalika transmisi - dokumen kasebut kedadeyan sajrone urip, lan versi pisanan PyDERASN malah sengaja ngidini prilaku ora sah kasebut (saka sudut pandang DER) kanggo kepentingan kompatibilitas mundur.

Watesan liyane yaiku ora bisa ngerteni kanthi gampang ing bentuk apa (BER/DER) obyek tartamtu sing dikode ing struktur kasebut. Contone, standar CMS nyatakake yen pesen kasebut dienkode BER, nanging kolom sing ditandatanganiAttrs, sing digawe tandha kriptografi, kudu ana ing DER. Yen kita decode karo DER, kita bakal gagal ing pangolahan CMS dhewe; yen kita decode karo BER, kita ora bakal ngerti apa wangun signedAttrs. Akibaté, TLVSeeker (sing ora ana analog ing pyasn1) kudu nggoleki lokasi saben lapangan Atttrs sing ditandatangani, lan kanthi kapisah, njupuk metu saka perwakilan serial, decode karo DER.

Kemampuan kanggo otomatis proses DEFINED BY lapangan, kang kedaden asring banget, banget seng di pengeni kanggo kita. Sawise dekoding struktur ASN.1, kita bisa uga ditinggalake karo akeh lapangan ANY sing kudu diproses luwih lanjut miturut skema sing dipilih adhedhasar IDENTIFIER OBJEK sing ditemtokake ing kolom struktur. Ing kode Python, iki tegese nulis yen banjur nelpon decoder kanggo ANY lapangan.

Munculé PyDERASN

Ing Atlas, kita ajeg ngirim patch menyang ndhuwur nalika kita nemokake sawetara masalah utawa nambah program gratis sing digunakake. We diajukake dandan kanggo pyasn1 kaping pirang-pirang, nanging kode pyasn1 ora paling gampang mangertos lan kadhangkala ana owah-owahan API kompatibel sing ngalahake kita mudhun. Kajaba iku, kita wis biasa nulis tes kanthi tes generatif, sing ora ana ing pyasn1.

Sawijining dina nggoleki aku mutusaké sing aku wis cukup iki lan iku wektu kanggo nyoba nulis perpustakaan dhewe karo __slot__s, offsets lan apik ditampilake blobs! Mung nggawe codec ASN.1 ora cukup - kita kudu nransfer kabeh proyek sing gumantung, lan iki atusan ewu baris kode sing kebak karya karo struktur ASN.1. Yaiku, salah sawijining syarat: gampang nerjemahake kode pyasn1 saiki. Sawise ngentekake kabeh liburan, aku nulis perpustakaan iki lan nransfer kabeh proyek kasebut. Amarga padha duwe jangkoan meh 100% karo tes, iki tegese perpustakaan wis operasional.

PyDERASN, uga, wis meh 100% jangkoan test. Migunakake tes generatif kanthi perpustakaan sing apik hipotesis. Iki uga ditindakake murub py-afl- Aku mangan ing 32 mesin nuklir. Sanajan kasunyatane meh ora ana kode Python2, PyDERASN isih tetep kompatibilitas lan amarga iki mung duwe siji-sijine. enem ketagihan. Menapa malih, iku dites marang ASN.1:2008 paket uji kepatuhan.

Prinsip nggarap iku padha karo pyasn1 - nggarap obyek Python tingkat dhuwur. Katrangan saka skema ASN.1 padha.

class TBSCertificate(Sequence):
    schema = (
        ("version", Version(expl=tag_ctxc(0), default="v1")),
        ("serialNumber", CertificateSerialNumber()),
        ("signature", AlgorithmIdentifier()),
        ("issuer", Name()),
        ("validity", Validity()),
        ("subject", Name()),
        ("subjectPublicKeyInfo", SubjectPublicKeyInfo()),
        ("issuerUniqueID", UniqueIdentifier(impl=tag_ctxp(1), optional=True)),
        ("subjectUniqueID", UniqueIdentifier(impl=tag_ctxp(2), optional=True)),
        ("extensions", Extensions(expl=tag_ctxc(3), optional=True)),
    )

Nanging, PyDERASN duwe sawetara mirip ngetik sing kuat. Ing pyasn1, yen lapangan jenis CMSVersion(INTEGER), banjur bisa diutus int utawa INTEGER. PyDERASN strictly mbutuhake obyek diutus persis CMSVersion. Saliyane nulis kode Python3, kita uga nggunakake ngetik anotasi, dadi fungsi kita ora bakal duwe argumen sing ora jelas kaya def func(serial, contents), nanging def func(serial: CertificateSerialNumber, isi: EncapsulatedContentInfo), lan PyDERASN mbantu njaga kode kasebut.

Ing wektu sing padha, PyDERASN nduweni konsesi sing trep banget kanggo ngetik iki. pyasn1 ora ngidini SubjectKeyIdentifier (). subtype (implicitTag = Tag (...)) lapangan kanggo nemtokake obyek menyang SubjectKeyIdentifier () (tanpa TAG IMPLICIT perlu) lan iku perlu kanggo asring nyalin lan nggawé ulang obyek mung amarga saka tag IMPLICIT/EXPLICIT diganti. PyDERASN strictly mirsani mung jinis dhasar - iku bakal kanthi otomatis ngganti tags saka skema ASN.1 struktur wis ana. Iki banget nyederhanakake kode aplikasi.

Yen ana kesalahan nalika dekoding, banjur ing pyasn1 ora gampang dingerteni ing ngendi persis kedadeyan kasebut. Contone, ing sertifikat Turki sing wis kasebut ing ndhuwur, kita bakal nampa kesalahan ing ngisor iki: UTF8String (tbsCertificate: issuer: rdnSequence:3:0:value:DEFINED BY 2.5.4.10:utf8String) (ing 138) wates sing ora puas: 1 ⇐ 77 ⇐ 64 Nalika nulis struktur ASN .1, wong bisa nggawe kesalahan, lan iki nggawe luwih gampang kanggo debug aplikasi utawa ngerteni masalah karo dokumen kode pihak liya.

Versi pisanan PyDERASN ora ndhukung enkoding BER. Katon mengko lan isih ora ndhukung pangolahan UTCTime/GeneralizedTime karo zona wektu. Iki bakal teka ing mangsa ngarep, amarga proyek kasebut ditulis utamane ing wektu luang.

Uga, ing versi pisanan ora ana karya karo lapangan DEFINED BY. Sawetara sasi mengko iki kesempatan muncul lan wiwit digunakake kanthi aktif, nyuda kode aplikasi kanthi signifikan - ing siji operasi dekoding, kabeh struktur bisa dibongkar nganti jero banget. Kanggo nindakake iki, skema kasebut nemtokake kolom "nemtokake" apa. Contone, deskripsi skema CMS:

class ContentInfo(Sequence):
    schema = (
        ("contentType", ContentType(defines=((("content",), {
            id_authenticatedData: AuthenticatedData(),
            id_digestedData: DigestedData(),
            id_encryptedData: EncryptedData(),
            id_envelopedData: EnvelopedData(),
            id_signedData: SignedData(),
        }),))),
        ("content", Any(expl=tag_ctxc(0))),
    )

ngandika yen contentType ngandhut OID karo nilai id_signedData, banjur kolom isi (dumunung ing urutan padha) kudu decoded miturut skema SignedData. Kok akeh tanda kurung? Sawijining lapangan bisa "nemtokake" sawetara kolom bebarengan, kaya sing ana ing struktur EnvelopedData. Kothak sing ditetepake diidentifikasi kanthi jalur decode sing diarani - nemtokake lokasi sing tepat saka unsur apa wae ing kabeh struktur.

Sampeyan ora tansah pengin utawa ora tansah duwe kesempatan kanggo langsung nambah iki nemtokake kanggo diagram. Bisa uga ana kasus khusus aplikasi nalika OID lan struktur mung dikenal ing proyek pihak katelu. PyDERASN nyedhiyakake kemampuan kanggo nyetel definisi kasebut kanthi bener nalika dekoding struktur:

ContentInfo().decode(data, ctx={"defines_by_path": ((
    (
        "content", DecodePathDefBy(id_signedData),
        "certificates", any, "certificate", "tbsCertificate",
        "extensions", any, "extnID",
    ),
    ((("extnValue",), {
        id_ce_authorityKeyIdentifier: AuthorityKeyIdentifier(),
        id_ce_basicConstraints: BasicConstraints(),
        [...]
        id_ru_subjectSignTool: SubjectSignTool(),
    }),),
),)})

Ing kene kita ujar manawa ing CMS SignedData kanggo kabeh sertifikat sing dilampirake, decode kabeh ekstensi kasebut (AuthorityKeyIdentifier, BasicConstraints, SubjectSignTool, lsp.). Kita nunjukake liwat path decode unsur sing kudu "diganti" karo definisi, kaya kasebut ing skema.

Akhire, PyDERASN nduweni kemampuan kanggo mbukak saka baris printah kanggo dekoding file ASN.1 lan wis sugih printing cantik. Sampeyan bisa decode ASN.1 sembarang, utawa sampeyan bisa nemtokake skema sing jelas lan ndeleng kaya iki:

PyDERASN: carane aku wrote perpustakaan ASN.1 karo slot lan blobs

Informasi sing ditampilake: offset obyek, dawa tag, dawane dawa, dawa isi, anané EOC (end-of-octets), atribut encoding BER, atribut encoding dawa tanpa wates, dawa lan offset tag EXPLICIT (yen ana), ambane nesting saka obyek ing struktur, nilai tag IMPLICIT / EXPLICIT, jeneng obyek miturut skema, jinis dhasar ASN.1, nomer urutan ing SEQUENCE / SET OF, nilai PILIHAN (yen ana), jeneng sing bisa diwaca manungsa INTEGER / ENUMERATED / BIT STRING miturut skema, Nilai saka sembarang tipe basa , DEFAULT / flag pilihan saka rencana, tandha sing obyek iki kanthi otomatis decoded minangka DEFINED BY lan amarga OID iki kedaden, manungsa-diwaca OID.

Sistem pencetakan cantik dirancang khusus supaya bisa ngasilake urutan obyek PP sing digambarake nggunakake alat sing kapisah. Gambar nuduhake renderer ing teks colored prasaja. Ana uga renderer ing format JSON/HTML, supaya bisa dideleng kanthi nyorot ing browser ASN.1, kaya ing asn1js proyek.

Pustaka liyane

Iki dudu tujuane, nanging PyDERASN dadi nyata luwih cepet tinimbang pyasn1. Contone, dekoding file CRL kanthi ukuran megabyte bisa suwe nganti sampeyan kudu mikir babagan format panyimpenan data penengah (cepet) lan ngganti arsitektur aplikasi. pyasn1 decode CRL CACert.org ing laptop njupuk luwih saka 20 menit, nalika PyDERASN njupuk mung 28 detik! Ana proyek asn1crypto, ngarahke ing karya cepet karo struktur cryptographic: decodes (rampung, ora puguh) CRL padha ing 29 detik, nanging nganggo meh kaping pindho minangka akeh RAM nalika mlaku ing Python3 (983 MiB mungsuh 498), lan ing 3.5 kaping ing Python2 (1677). lawan 488), dene pyasn1 ngonsumsi 4.3 kaping luwih (2093 lawan 488).

Kita ora nganggep asn1crypto, sing dakkandhakake, amarga proyek kasebut isih cilik lan durung krungu babagan iki. Saiki, kita uga ora bakal ndeleng arahe, amarga aku langsung nemokake manawa GeneralizedTime sing padha ora njupuk bentuk sing sewenang-wenang, lan sajrone serialisasi, kanthi meneng mbusak bagian sekedhik. Iki ditrima kanggo nggarap sertifikat X.509, nanging umume ora bisa digunakake.

Ing wayahe, PyDERASN paling ketat free Python / Go DER decoder aku ngerti. Ing perpustakaan enkoding/asn1 saka Go kekasihku ora mriksa ketat OBJECT IDENTIFIER lan string UTCTime/GeneralizedTime. Kadhangkala kekakuan bisa ngalangi (utamane amarga kompatibilitas mundur karo aplikasi lawas sing ora ana sing bisa ndandani), saengga PyDERASN bisa lulus. macem-macem setelan mriksa weakening.

Kode proyek nyoba dadi prasaja sabisa. Kabeh perpustakaan iku siji file. Kode kasebut ditulis kanthi penekanan kanggo gampang dingerteni, tanpa optimasi kinerja sing ora perlu lan kode KERING. Ora, kaya sing wis dakkandhakake, ndhukung dekoding BER lengkap saka strings UTCTime / GeneralizedTime, uga REAL, RELATIVE OID, EXTERNAL, INSTANCE OF, EMBEDDED PDV, CHARACTER STRING jinis data. Ing kabeh kasus liyane, aku wong ora weruh titik nggunakake perpustakaan liyane ing Python.

Kaya kabeh proyekku, kaya PyGOST, GoGOST, NCCP, GoVPN, PyDERASN rampung piranti lunak gratis, mbagekke miturut syarat LGPLv3+, lan kasedhiya kanggo download gratis. Ana conto panggunaan kene lan ing tes PyGOST.

Sergey Matveev, cypherpunk, anggota Yayasan SPO, pangembang Python/Go, spesialis pangareping FSUE "STC "Atlas".

Source: www.habr.com

Add a comment