解析localethereum背后的安全架構(gòu)

localethereum簡(jiǎn)介

localethereum.com

localethereum的官網(wǎng)上是這樣介紹自己的:點(diǎn)對(duì)點(diǎn)的以太門戶,即使用以太坊智能合約和點(diǎn)對(duì)點(diǎn)的加密技術(shù)促成ether和法幣在人與人之間的直接兌換,不需要中間人擔(dān)保。這和0x和kyber等直接促成虛擬幣兌換的去中心化交易所不一樣,它主要解決的是去中心的法幣與虛擬幣兌換這個(gè)核心問(wèn)題,并確保這個(gè)過(guò)程是保密和安全的。為此它做了很多努力,包括:

  1. 點(diǎn)對(duì)點(diǎn)的消息加密
  2. 消息前向保密
  3. 財(cái)務(wù)前向保密
  4. 簽名系統(tǒng)

自去年9月上線以來(lái),運(yùn)營(yíng)良好,沒(méi)有出現(xiàn)黑客事件,且體驗(yàn)還可以,流程不算復(fù)雜。首先注冊(cè)一個(gè)賬號(hào),和普通網(wǎng)站一樣,需要用戶名和密碼以及開(kāi)啟兩步驗(yàn)證;掛單者(Maker)掛一個(gè)訂單,這個(gè)單會(huì)出現(xiàn)在網(wǎng)站上;其他受價(jià)者(Taker)可以在網(wǎng)站上接單并發(fā)起交易,localethereum為這筆交易生成一份以太坊智能合約;賣方將ether轉(zhuǎn)到localethereum錢包,然后再?gòu)膌ocalethereum錢包把想要交易的ether轉(zhuǎn)到智能合約,至此,交易的鏈上部分完成,接下來(lái)等待買家鏈下法幣支付;在這個(gè)過(guò)程中雙方可以通過(guò)localethereum的點(diǎn)對(duì)點(diǎn)的加密消息進(jìn)行溝通,比如把銀行賬號(hào)告知對(duì)方以便法幣轉(zhuǎn)賬等等。

總之整個(gè)流程下來(lái),交互習(xí)慣就跟其他中心化交易所一樣,但它卻是去中心化的,點(diǎn)對(duì)點(diǎn)的。你不必?fù)?dān)心黑客侵入盜幣或交易所破產(chǎn),這絕對(duì)是一種技術(shù)進(jìn)步,接下來(lái)我會(huì)為大家一一剖析它的安全機(jī)制。因?yàn)樗皇情_(kāi)源的,所以只能結(jié)合一些開(kāi)放的說(shuō)明資料以及頁(yè)面行為來(lái)加以分析。

賬號(hào)密碼

localethereum用戶需要賬號(hào)和密碼,但是它的密碼和我們通常認(rèn)知的密碼不一樣,它的密碼純粹是本地的。也就是說(shuō)服務(wù)器端不存儲(chǔ)密碼的相關(guān)信息。在我們創(chuàng)建賬號(hào)的時(shí)候,就有一段提示:

Your web browser is going to generate a private key offline, and then encrypt it using AES256-CBC to a PBKDF2-stretched version of your password. 你的瀏覽器將會(huì)離線生成一個(gè)私鑰,這個(gè)私鑰使用AES256-CBC加密,加密秘鑰用你的密碼經(jīng)PBKDF2運(yùn)算后得到。

PBKDF2的作用是“延展”人類的密碼,使之更長(zhǎng),更隨機(jī),不可逆,不容易被猜測(cè)。即使像通常的網(wǎng)站也不直接存儲(chǔ)人類的明文密碼,否則萬(wàn)一數(shù)據(jù)庫(kù)被盜,或者網(wǎng)站監(jiān)守自盜,那么用戶的損失將是巨大的,因?yàn)橛脩裟苡浀拿艽a是有限的,他在很多網(wǎng)站使用的可能是同一個(gè)密碼,一旦一個(gè)地方發(fā)生泄露,所有的這些信息將面臨極大的暴露風(fēng)險(xiǎn)。Like this, no fuck to say!


無(wú)fuck可說(shuō)

AES256-CBC是對(duì)稱加密算法,即它可以做加密解密,是一種安全的可逆運(yùn)算。

創(chuàng)建賬號(hào)的時(shí)候,在Chrome里面也可以看到如下的服務(wù)端請(qǐng)求:

POST https://api.localethereum.com/v1/accounts/new
{
  "username": "jon",
  "email": "jon@f...",
  "account_key_identity_public": "01e355ef0f6b45e7ff86...",
  "account_key_encrypted_root": {
    "ciphertext": "47f57c64067abaa...",
    "iv": "cb331d0e0...",
    "passphrase_salt": "e88be51f58...",
    "pbkdf2_iterations": 50906
  }
}

確實(shí)沒(méi)有上傳密碼,但是account_key_identity_publicaccount_key_encrypted_root是干么用的,光看字面也看不明白。為了更好理解先來(lái)看看localethereum本地的一段js代碼:

密碼校驗(yàn)

這下大致清楚了,反推一下:

var password = "abc"; //明文密碼,記在人腦
var passphrase_salt = "e88be51f58..."; //鹽,創(chuàng)建賬號(hào)的時(shí)候隨機(jī)生成
var pbkdf2_iterations = "50906"; //加鹽迭代次數(shù),創(chuàng)建賬號(hào)的時(shí)候生成

//對(duì)明文密碼進(jìn)行延展
var key = PBKDF2(password, passphrase_salt, {
  keySize: 256 / 32,
  iterations: pbkdf2_iterations
})

var accountKeyRoot = "0f45e6dae7cc40c..." //秘鑰根,創(chuàng)建賬號(hào)的時(shí)候隨機(jī)生成
var iv = "cb331d0e0..."; //初始向量,創(chuàng)建賬號(hào)的時(shí)候隨機(jī)生成

//加密的秘鑰根
var ciphertext = AES.encrypt(accountKeyRoot, key, {
  iv: iv,
  mode: CBC,
  padding: NoPadding
})

var n = ecsign(sha3(accountKeyRoot, 256), "enc");
//用于對(duì)其他私密信息進(jìn)行加密用的秘鑰
var accountKeyEnc = toRpcSig(n.v, n.r, n.s);

//秘鑰對(duì)
var accountKeyIdentityPair = E(accountKeyRoot);

//公鑰
var account_key_identity_public = accountKeyIdentityPair.publicKey;

所以服務(wù)端實(shí)際上存儲(chǔ)的是用戶秘鑰的根(有時(shí)候也稱之為種子),不過(guò),這個(gè)秘鑰的根是經(jīng)過(guò)用戶本地密碼加密保護(hù)過(guò)再給服務(wù)端的。用戶登錄的時(shí)候,雖然是先輸入賬號(hào)和密碼,再輸入二次驗(yàn)證碼,但實(shí)際上,服務(wù)端是先校驗(yàn)二次驗(yàn)證碼,成功之后再?gòu)姆?wù)端獲取用戶的account_key_identity_publicaccount_key_encrypted_root到本地,在本地用密碼解開(kāi)account_key_encrypted_root獲得秘鑰的根accountKeyRoot,存儲(chǔ)到Local Storage:

{
  "username": "jon",
  "accountKeyRoot": "e1dde6d4f...",
  "sessionToken": "MTUwNTQzMn0.kwHcAkDjelD..."
}

accountKeyRoot就是你的數(shù)字身份的DNA,自創(chuàng)建開(kāi)始就不可變更,你可以修改你的密碼,但這僅僅只是改變了存儲(chǔ)在服務(wù)器端的密文,用新密碼解密后還是原來(lái)的內(nèi)容。如果有人直接盜走了accountKeyRoot,他就可以在數(shù)字世界代表你。

掛單者秘鑰(Maker keys)

每個(gè)localethereum用戶都會(huì)先行創(chuàng)建很多簽名的秘鑰對(duì),存在服務(wù)端,結(jié)構(gòu)如:

{
  "public_key": "371854cb5efc...",
  "signature": "0x7ae22085f5047e0c3d5...",
  "encrypted_private": {
    "ciphertext": "54b8c3c2c168778...",
    "iv": "417a9c6e..."
  }
}

這些秘鑰允許人們創(chuàng)建交易、發(fā)送消息、用離線的錢包轉(zhuǎn)賬和部署智能合約,而且能夠保持前向保密(當(dāng)前秘鑰泄露不影響到以前的數(shù)據(jù))。


PREKEYS生成函數(shù)

翻譯過(guò)來(lái)就是:

  1. 生成隨機(jī)的256位secp256k1秘鑰對(duì)(MakerKey-privateMakerKey-public
  2. 用賬號(hào)的私鑰accountKeyIdentityPrivate對(duì)SHA3(MakerKey-public)的結(jié)果進(jìn)行ECDSA簽名(MakerKey-signature
  3. MakerKey-private用一個(gè)隨機(jī)的IVaccountKeyEnc進(jìn)行AES-256加密之后,連同MakerKey-publicMakerKey-signature發(fā)到服務(wù)端保存。

錢包地址

和Maker keys一樣,localethereum也會(huì)預(yù)先生成很多的以太坊地址:

{
  "n": 1,
  "address": "0xfe742544...",
  "signature": "0xe03a28b6ec8a1...",
  "encrypted_private": {
    "ciphertext": "77d8cc2e176c...",
    "iv": "df4193c2..."
  }
}
生成錢包地址

不一樣的地方在于每個(gè)地址有一個(gè)鏈私鑰,它由前一個(gè)鏈私鑰經(jīng)過(guò)以下算法得來(lái):

var chainPrivateKey=HmacSHA256(previousChainPrivateKey, [2]);
var privateKey=HmacSHA256(chainPrivateKey, [0,1]);
var address = publicToAddress(privateToPublic(privateKey));

最后將chainPrivateKey加密連同其他相信息存到服務(wù)器端。

交易

假設(shè)Bob在網(wǎng)站上看到Alice掛的單,想要受價(jià)交易,他就向localethereum請(qǐng)求獲取Alice的Maker key和簽名的錢包地址,localethereum會(huì)給他一個(gè)Alice全新未使用的MakerKey-publicMakerAddress-address。

當(dāng)Bob用Alice的公鑰驗(yàn)證這兩個(gè)簽名之后,確定key和錢包地址確實(shí)是屬于Alice的,他就可以向她轉(zhuǎn)賬和發(fā)送消息。但事情并非就此簡(jiǎn)單。Bob還需要做三件事:

  1. 他也要生成自己的全新的一次性secp256k1秘鑰對(duì)(TakerKey-privateTakerKey-public
  2. 他從服務(wù)端拿一個(gè)自己未使用的錢包地址(TakerAddress
  3. Bob將這些信息捆綁在一起,并用他自己的私鑰accountKeyIdentityPrivate對(duì)SHA3(MakerKey-public + MakerAddress-address + TakerKey-public + TakerAddress)的結(jié)果簽名(TradeSignature

他將TradeSignature, TakerKey-publicTakerAddress發(fā)給服務(wù)端,當(dāng)Alice上線后她就可以驗(yàn)證簽名。雙方開(kāi)始安全交易。

創(chuàng)建交易的時(shí)候可以看到Chrome發(fā)起的請(qǐng)求(貌似可以不需要MakerAddress-address):

POST https://api.localethereum.com/v1/trades/new
{
  "offer_id": "d2d8bcc5-9b1...",
  "signature": "0x69a1b74997...",
  "maker_key_public_key": "28b75263cf15bb9d432...",
  "taker_address": "0xfe725e38b24925a2a7895...",
  "taker_key_public_key": "30e43861d4c616...",
  "taker_key_encrypted_private": {
    "iv": "52bc72e0d...",
    "ciphertext": "48764d5..."
  }
}

一個(gè)成功了的交易結(jié)構(gòu)是這樣的:

GET https://api.localethereum.com/v1/trades/ef3771d7-51c...
{
  "id": "ef3771d7-51c...",
  "state": "released",
  "maker_account_id": "63abacee-0cc...",
  "taker_account_id": "d9058c2a-23e...",
  "maker_username": "Din",
  "taker_username": "Vager",
  "maker_fee": 0.25,
  "taker_fee": 0.75,
  "maker_account_key_identity_public": "f19bb6af3eed...",
  "taker_account_key_identity_public": "bbac5c7e884eecae5dd...",
  "offer_id": "0d52e362-887...",
  "offer_headline": "",
  "offer_terms_of_trade": "",
  "created_at": "2017-12-14T06:06:57.067Z",
  "wei_amount_for_buyer": "1000000000000000000",
  "wei_amount_for_seller": "1010110000000000000",
  "local_currency_amount": 8898.1,
  "local_currency_code": "CNY",
  "direction": "maker_seller_taker_buyer",
  "maker_address": "0x0a2ffbabdc7f5f0...",
  "maker_address_signature": "0x1d5dc75a401e1209f6fad...",
  "taker_address": "0x3fa8a7bf52ec2a7eb09...",
  "taker_signature": "0xbf90926a5681bc7508e6e6375f...",
  "maker_key": {
    "public_key": "57cf2817a7bcf0880aef18fd1fb...",
    "encrypted_private": null
  },
  "taker_key": {
    "signature": null,
    "public_key": "6eb485558f07c8e0ac3d9934a06e..."
  },
  "reported_by_you": false,
  "contract_escrow": {
    "trade_hash": "fe6fc04f7b06932c4...",
    "contract_address": "0x09678741bd...",
    "seller_can_cancel_after": 0,
    "total_gas_fees_spent_by_relayer": "0",
    "currently_exists": false,
    "events": [{
      "observed_at": "2017-12-14T06:13:36.036Z",
      "block_number": 4729641,
      "transaction_hash": "0x0abdd751a...",
      "event_name": "Created"
    }, {
      "observed_at": "2017-12-14T06:18:14.786Z",
      "block_number": 4729661,
      "transaction_hash": "0xd3a800117e83...",
      "event_name": "Released"
    }],
    "relays": [{
      "created_at": "2017-12-14T06:17:22.889Z",
      "sent_at": "2017-12-14T06:17:39.103Z",
      "confirmed_at": "2017-12-14T06:18:12.796Z",
      "confirmed_at_block": 4729661,
      "action_byte": "05",
      "transaction_hash": "0xd3a800117..."
    }]
  },
  "sent_transactions": [{
    "trade_hash": "fe6fc04f7b06932...",
    "name": "createEscrow",
    "transaction_hash": "0x0abdd751...",
    "sent_at": "2017-12-14T06:12:58.309Z"
  }],
  "maker_address_encrypted_private": {
    "ciphertext": "db93a6462e0a...",
    "iv": "0a52107d14afd..."
  },
  "marked_as_paid_at": null,
  "marked_for_released_at": "2017-12-14T06:17:22.889Z",
  "marked_as_cancelled_at": null,
  "cancelled_at": null,
  "cancelled_by_account_id": null,
  "disputed_at": null,
  "disputed_by_account_id": null,
  "left_feedback": "good",
  "payment_method": {
    "id": 7,
    "name": "Alipay",
    "description_for_buyer": "You’ll send the seller money online via Alipay.",
    "description_for_seller": "The buyer will send you money online via Alipay.",
    "payment_window_in_minutes": 120
  },
  "delete_party_key_at": "2017-12-21T06:18:14.786Z",
  "hash": 2554751258,
  "last_message_id": "bd2c3e2a-bdc..."
}

消息

點(diǎn)對(duì)點(diǎn)加密消息用的是Elliptic curve Diffie–Hellman (ECDH)異步秘鑰交換協(xié)議,這個(gè)協(xié)議允許雙方用一方的私鑰和另一方的公鑰派生一個(gè)共享秘鑰。ECDH的美妙之處在于:

SharedSecret-root = ECDH(MakerKey-public, TakerKey-private) = ECDH(TakerKey-public, MakerKey-private)

接著用SharedSecret-root通過(guò)HKDF算法生成更加安全的秘鑰(SharedSecret-encSharedSecret-mac)。發(fā)送消息的時(shí)候,Alice和Bob用SharedSecret-enc和隨機(jī)值IV通過(guò)AES256-CBC加密消息,且每條消息都用各自的accountKeyIdentityPrivate簽名。為了做進(jìn)一步認(rèn)證和完整性檢查,還會(huì)用SharedSecret-mac對(duì)每個(gè)加密消息進(jìn)行HMAC-SHA256哈希校驗(yàn)。

一旦發(fā)生爭(zhēng)議,一方只要提交SharedSecret-root讓網(wǎng)站做仲裁即可。

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

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 一、快速術(shù)語(yǔ)檢索 比特幣地址:(例如:1DSrfJdB2AnWaFNgSbv3MZC2m74996JafV)由一串...
    不如假如閱讀 16,556評(píng)論 4 87
  • 禪繞畫Q&A Q:為什么覺(jué)得被暗線限制 A:美華語(yǔ)錄:暗線是個(gè)支持,不是限制。我的暗線畫好後,填入圖樣時(shí),用了區(qū)塊...
    木之慧閱讀 1,104評(píng)論 0 8
  • 1.在shell中基本關(guān)系符有:\>、\<、\>=、\<=、\=、\!=(不等于) 2.用法: 例:value1=...
    踩在浪花上00閱讀 318評(píng)論 0 1
  • 俗話說(shuō)的好:工欲善其事,必先利其器。作為技術(shù)的工匠來(lái)說(shuō),不僅僅需要好的開(kāi)發(fā)工具,好的開(kāi)發(fā)工具可以提高我們的工作效率...
    非著名程序員閱讀 3,439評(píng)論 7 54

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