Nginx+Openssl+Intel QAT異步密碼調(diào)用研究

在我們還在關(guān)注網(wǎng)絡(luò)IO的時候,Intel已經(jīng)在搞密碼IO了。不得不說老外在技術(shù)上確實比較NB。國內(nèi)的密碼應(yīng)用比老外還是差了一截。

前段時間基于openssl(版本比較老,好像是1.0.x吧,等我做完已經(jīng)1.1.1了)開發(fā)了個SSL產(chǎn)品,稍微考慮和實現(xiàn)了一下密碼計算的IO問題。

技術(shù)的創(chuàng)新(如果這也算的話)都是逼出來的,大致上都與性能有關(guān)。在開發(fā)時,采用的是流水線工作方式,一個線程綁定一個核(Tilera平臺)。最后發(fā)現(xiàn)一個問題:CPU核不夠用。為啥:一個核對應(yīng)了一路硬件資源(加解密),要把所有的硬件資源用起來,CPU核不夠了。

于是先解決SSL加解密核不夠用的問題,想法很簡單:加解密不能阻塞。SSL調(diào)用加解密后,立即返回,后面再繼續(xù)往下做。這樣,每個核可以對應(yīng)多個加解密硬件資源,減少核需求。

那么,問題來了:

問題1 :如何保存現(xiàn)場和恢復(fù)現(xiàn)場。怎么解決呢?現(xiàn)在想來還是有點幼稚(知識面還是窄),也很老土(后面有更好的方法),也是相當(dāng)?shù)穆闊?,那就是:在SSL中添加數(shù)據(jù)結(jié)構(gòu),手工保存需要保存的一些變量。折騰了一下,也算完成。

問題2:什么時候去恢復(fù)現(xiàn)場?Openssl的SSL_read和SSL_write函數(shù),在遇到網(wǎng)絡(luò)IO時,會返回PENDING,然后需要用戶二次調(diào)用。我也學(xué)它吧。修改源碼,在加密和解密時會也返回一個ENC/DEC_PENGDING錯誤,加解密函數(shù)需要后面繼續(xù)二次調(diào)用,才能完成?;謴?fù)現(xiàn)場的時機(jī),在我這里其實變成了輪詢。

當(dāng)加解密搞完之后,本想把SSL握手過程也這么搞一遍。發(fā)現(xiàn)這是一個不可能完成的任務(wù),因為握手里面的狀態(tài)(以調(diào)用密碼運算為分割點)太多了。

后來查了下網(wǎng)上的資料,發(fā)現(xiàn)intel已經(jīng)搞定了。

如果密碼接口這一層要改成異步的話,所有的上層調(diào)用都需要支持。

intel 提供QAT硬件,并開源了軟件實現(xiàn);

openssl從1.1.0開始,支持異步,很好的解決了保存現(xiàn)場,恢復(fù)現(xiàn)場,以及何時去恢復(fù)現(xiàn)場的問題;

intel 提供了qat的openssl引擎;

intel為nginx做了修改,基于事件驅(qū)動,將openssl引擎用了起來,支持異步。

先說說intel的QAT是如何支持異步的。他所有的密碼運算接口,都有一個回調(diào)函數(shù)和上下文。當(dāng)密碼運算完成時,該回調(diào)函數(shù)會被調(diào)用。在intel 的qat引擎中,該函數(shù)會寫一個由eventfd創(chuàng)建的句柄,通知fd監(jiān)視者,這樣就解決了“恢復(fù)時機(jī)”的問題。

至于保存和恢復(fù)現(xiàn)場,由openssl的異步接口實現(xiàn):ASYNC_start_job和ASYNC_pause函數(shù)實現(xiàn)。ASYNC_start_job用于啟動或者恢復(fù)任務(wù)。ASYNC_pause用于纖程切換,在將數(shù)據(jù)發(fā)給硬件之后,調(diào)用 ASYNC_pause,保存現(xiàn)場,返回到ASYNC_start_job,進(jìn)而返回到上層。上層再次調(diào)用 ASYNC_start_job時,會恢復(fù)由ASYNC_pause所保存的現(xiàn)場,繼續(xù)往下執(zhí)行。

如下圖所示:


過程如下:

1.nginx調(diào)用SSL_accept做握手,openssl用ASYNC_start_job啟動握手過程,這時其實有個纖程切換,切換到具體的ssl握手函數(shù);

2.握手函數(shù)執(zhí)行硬件計算,調(diào)用qat引擎,最終調(diào)用qat的rsa密碼函數(shù)(在之前會通過eventfd創(chuàng)建一個句柄)直接返回,然后調(diào)用ASYNC_pause,執(zhí)行纖程切換,由回到ASYNC_start_job調(diào)用的地方,返回上層SSL_ERROR_WANT_ASYNC;

3.nginx發(fā)現(xiàn)密碼運行處于pending狀態(tài),調(diào)用 ngx_ssl_async_process_fds,將前面eventfd創(chuàng)建的句柄加入到epoll的監(jiān)視集合;并將其事件設(shè)置為ssl握手;

4.硬件完成后,調(diào)用rsa密碼函數(shù)設(shè)置的回調(diào)函數(shù),write一下那個fd;

5.nginx監(jiān)視到該fd可讀,讀出數(shù)據(jù),繼續(xù)調(diào)用握手函數(shù),進(jìn)而調(diào)用ASYNC_start_job恢復(fù)由ASYNC_pause保存的現(xiàn)場,程序往下執(zhí)行.

展望:intel的QAT看上去還是挺復(fù)雜的,需要制定一個類似pkcs11一樣的比較簡單的異步密碼運算接口。目前主流的密碼運算接口還都是阻塞式調(diào)用。在我實現(xiàn)的加解密接口中,參考了linux aio接口的命名方式。大致如下:

int AIO_sign(aioctx *ctx);

int AIO_verify(aioctx *ctx);

int AIO_encrypt(aioctx *ctx);

int AIO_decrypt(aioctx *ctx);

int AIO_return(aioctx *ctx);

int AIO_cancle(aioctx *ctx);

這里對內(nèi)存有要求,輸入輸出內(nèi)存必須是malloc出來的堆內(nèi)存,以確保內(nèi)存是有效內(nèi)存。由于要支持加解密接口輸入大數(shù)據(jù),會多包發(fā)送,當(dāng)有錯誤時,可通過AIO_cancle來取消后續(xù)的數(shù)據(jù)傳輸和計算(避免浪費硬件資源)。對于上下文的保存,可與硬件進(jìn)行約定,預(yù)留8字節(jié)(void *),這樣很容易恢復(fù)上下文(intel qat怎么做的不清楚)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,598評論 0 6
  • 加密與解密 對稱密碼體制的保密性主要取決于密鑰的保密性,與算法的保密性無關(guān) 非對稱加密算法與對稱加密算法的區(qū)別: ...
    peerless_1024閱讀 2,228評論 0 0
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,626評論 1 32
  • 他依稀聽見一支用口哨吹出的充滿活力的歌在耳邊回響。這是贊美青春和生命的歌?!镀椒驳氖澜纭?掩卷。啊,一百萬字,...
    李曉欣閱讀 1,407評論 18 21
  • 也可以使用電腦和可移動設(shè)備哦,這樣也可以做出漂亮的手賬。 有時候我們發(fā)現(xiàn)自己被困原地、裹足不前,其實我們離目標(biāo)...
    up方方閱讀 169評論 0 0

友情鏈接更多精彩內(nèi)容