Google Tink 02:從對(duì)稱加密的實(shí)現(xiàn)開始

對(duì)稱加密算法和 AEAD

對(duì)稱加密

對(duì)稱加密算法就是加密和解密都使用相同的密鑰的算法恩沛。它的加解密流程圖采用的是下面的形式來完成的:

明文 --> 密鑰 --> 密文

解密過程就是:

密文 --> 密鑰 --> 明文

當(dāng)明文完成加密過程之后就會(huì)形成密文亚兄。根據(jù)使用場(chǎng)景的不同账千,密文可以發(fā)送給其它處理對(duì)象券躁,或者是保存到存儲(chǔ)介質(zhì)中凤类,當(dāng)需要使用的時(shí)候扛稽,通過解密步驟就可以得到明文柴灯。

我們所熟悉的 DES物延、3DES宣旱、AES 都是對(duì)稱加密算法,相信稍微有點(diǎn)計(jì)算機(jī)方面知識(shí)和經(jīng)驗(yàn)的同學(xué)都知道在選擇對(duì)稱加密算法的時(shí)候叛薯,首選就是 AES 算法浑吟。一是 AES 是所有程序開發(fā)語言中都會(huì)包含在標(biāo)準(zhǔn)庫(kù)中的算法,同時(shí)耗溜,也是這些算法中推出時(shí)間靠后的密碼算法了组力。

DES 出現(xiàn)在 1974 年,AES 的出現(xiàn)在 2001 年抖拴。

如果沒有什么特殊情況的話燎字,在軟件領(lǐng)域,往往推出時(shí)間越靠后就代表越安全阿宅,比如軟件的版本候衍,后推出的版本就有可能會(huì)解決前期版本中的安全性問題。在密碼算法也大致如此家夺。但這并不是絕對(duì)的脱柱,不過 AES 確實(shí)目前還是安全的、也是首選的對(duì)稱密碼算法拉馋。

但選擇了 AES 作為加解密算法之后榨为,你還將面臨一個(gè)難題,如何在實(shí)際的生產(chǎn)環(huán)境中安全的運(yùn)用它煌茴。作為對(duì)稱加密算法随闺,它屬于塊(block)密碼,所以你還要考慮以下的問題:

  • 加密模式怎么選蔓腐?
  • 加密模式使用的 IV 或 nonce 怎么選矩乐?

當(dāng)然這些最基本的內(nèi)容我在這里不展開了,以后再補(bǔ)寫點(diǎn)基礎(chǔ)知識(shí)。這就是 AES 一次只能處理 128 比特即 16 字節(jié)的數(shù)據(jù)散罕,而需要處理信息有 1024 字節(jié)這么大分歇,怎么處理?

然后需要考慮的問題是對(duì)密文進(jìn)行認(rèn)證欧漱。也就是對(duì)明文使用完對(duì)稱加密算法后會(huì)得到密文职抡,在你不確定生成的密文最終處于什么樣的保護(hù)情況,對(duì)密文進(jìn)行認(rèn)證是一個(gè)非常值得考慮的做法误甚。

什么是保護(hù)情況呢缚甩?舉個(gè)例子,比方說你有套別墅窑邦、有輛幾百萬的保時(shí)捷擅威,每天回家后將車停在自家別墅的車庫(kù)里,鎖不鎖車無所謂冈钦,鎖匙放不放車上也無所謂郊丛,因?yàn)檫@個(gè)時(shí)候環(huán)境是安全的。而你開車上班派继,把車停在了辦公樓下的地下車庫(kù)宾袜,這時(shí)安全的做法就是鎖車拿走鎖匙。

所以驾窟,我們不能只用到對(duì)稱加密算法就行了庆猫,還需要用到簽名算法,對(duì)密文添加完整性和認(rèn)證的檢測(cè)能力绅络,這就是 Authenticated Encryption月培,簡(jiǎn)稱 AE。

Authenticated Encryption

那這個(gè)對(duì)密文簽名需要怎么做恩急?雖然大至有幾種方法杉畜,但最常用的作法就是對(duì)明文加密完成之后的密文運(yùn)用 MAC (消息認(rèn)證碼)算法進(jìn)行簽名,然后將密文+簽名返回衷恭。

后面進(jìn)一步的發(fā)展此叠,我們可以添加一些明文的信息到密文中去,這些明文的信息不需要加密随珠,但也需要保證它們的完整性和認(rèn)證能力灭袁,這就是 Authenticated Encryption with Associated Data,簡(jiǎn)稱 AEAD(關(guān)聯(lián)數(shù)據(jù)的認(rèn)證加密)窗看。

上面的這些知識(shí)又需要學(xué)習(xí)大量的密碼學(xué)相關(guān)理論了茸歧,但有了 Tink,上面這些問題你就不用考慮了显沈,Tink 能幫你搞定软瞎。

Tink 的 AEAD primitives 就是為這個(gè)目標(biāo)而誕生的逢唤,讓你不用和這些低層的加密算法 API 打交道也能很好的處理密碼學(xué)相關(guān)的問題。

Tink 將對(duì)稱加密算法包裝成了 AEAD primitives涤浇,是一種對(duì)稱加密的標(biāo)準(zhǔn)實(shí)踐鳖藕,也是目前安全的實(shí)踐。

AEAD 實(shí)現(xiàn)了對(duì)密文的機(jī)密性只锭、完整性和認(rèn)證的功能吊奢,能有效的防止針對(duì)密文的攻擊。比如說選擇密文攻擊纹烹,攻擊者通過修改密文后,提交給加密算法召边,來觀察加密算法的反應(yīng)铺呵,從而通過精心構(gòu)建的密文破解密鑰。

你可以這樣想 AE:

  • DES隧熙、3DES片挂、AES 提供了加密功能;
  • MAC 實(shí)現(xiàn)了完整性和認(rèn)證的功能贞盯。

而 AEAD 則是在上面 AE 的基礎(chǔ)上音念,讓你可以加入一些額外的認(rèn)證數(shù)據(jù),它是 AE 的另一種用法躏敢。

我們來看一下具體使用 Tink AEAD primitives 的方法闷愤。

AEAD 官方示例

準(zhǔn)備及運(yùn)行

目前官方給出的 AEAD 示例代碼如下,創(chuàng)建一個(gè) main.go 的文件件余,然后運(yùn)行看看結(jié)果:

package main

import (
        "fmt"
        "log"

        "github.com/google/tink/go/aead"
        "github.com/google/tink/go/keyset"
)

func main() {

        kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate())
        if err != nil {
                log.Fatal(err)
        }

        a, err := aead.New(kh)
        if err != nil {
                log.Fatal(err)
        }

        ct, err := a.Encrypt([]byte("this data needs to be encrypted"), []byte("associated data"))
        if err != nil {
                log.Fatal(err)
        }

        pt, err := a.Decrypt(ct, []byte("associated data"))
        if err != nil {
                log.Fatal(err)
        }

        fmt.Printf("Cipher text: %s\nPlain text: %s\n", ct, pt)

}

運(yùn)行 main.go 之后讥脐,會(huì)輸出以下的內(nèi)容,你的密文確定不長(zhǎng)成這個(gè)樣子的:

$ go run main.go
Cipher text: ╔E????? c?1?L:?>?Yz??G ??M???ǖP???╗?<V.?L?\?D"?pC$e`?5?D
Plain text: this data needs to be encrypted

一般情況下我不太喜歡將密文直接輸出進(jìn)為字符串的形式啼器,因?yàn)槔锩姘刑嗟牟豢梢娮址蛴〕鰜砀袷角姘俟帧?/p>

所以,我喜歡將包含不可見字符的密文輸出為 HEX(十六進(jìn)制串)或 Base64端壳,所以告丢,這里可以將最后一行語句改成:

fmt.Printf("Cipher text: %x\nPlain text: %s\n", ct, pt)

修改之后,再次運(yùn)行下 main.go 程序:

$ go run main.go
Cipher text: 0187574a607e5a6e321cd16ea125546ef145d0f9e250a7ae4ce5ef259a715a7025cacdeb8e004c1bb98b2db96de439ed9023e8c5636b05f462dd298c597789c7
Plain text: this data needs to be encrypted

總算是沒有看到 '?' 這樣的不可見字符了损谦,這樣看起來就舒服多了岖免。

示例執(zhí)行流程

從上面的示例代碼中,也就是通過該加解密過程來分析成翩,在代碼的處理過程中觅捆,一共包含的幾個(gè)步驟,分別用創(chuàng)建的對(duì)象來分析麻敌,結(jié)果如下:

  1. 創(chuàng)建 KeyTemplate栅炒;
    語句 aead.AES256GCMKeyTemplate() 創(chuàng)建了一個(gè)密鑰模板,該模板是定義如何生成密鑰,包含加解密算法赢赊、密鑰長(zhǎng)度乙漓、IV 長(zhǎng)度等值。比如這里定義了一個(gè)基于 AES 的 32 位算法释移,采用 GCM 加密模式生成密鑰叭披;

  2. 通過 KeyTemplate 創(chuàng)建 KeysetHandle;
    根據(jù) KeyTemplate 創(chuàng)建的密鑰(Key)存放在鍵集(Keyset玩讳,也可稱為密鑰集)中涩蜘,KeysetHandle 就是操作密鑰集的,它用來執(zhí)行對(duì)密鑰的打印熏纯、保存和讀取操作同诫,但它會(huì)對(duì)敏感的密鑰信息進(jìn)行保護(hù),比方說你不能直接打印密鑰的具體內(nèi)容樟澜,但可以看到密鑰的元信息(ID號(hào)误窖、狀態(tài)、密鑰前綴)秩贰。

  3. 通過 KeysetHandle 創(chuàng)建 AEAD primitive霹俺;
    現(xiàn)在,就可以通過鍵集(Keyset)中保存的密鑰來創(chuàng)建 AEAD 加密使用的 primitive毒费,它將提供加密(Encrypt)和解密(Decrypt)方法用于后繼的加解密操作丙唧。

  4. 使用 AEAD primitive 執(zhí)行加密或解密操作。

加密和解密接口

上面我們使用 AEAD primitive 的加密(Encrypt)和解密(Decrypt)方法蝗罗,它們的接口定義如下:

type AEAD interface {
    // Encrypt encrypts plaintext with additionalData as additional
    // authenticated data. The resulting ciphertext allows for checking
    // authenticity and integrity of additional data additionalData,
    // but there are no guarantees wrt. secrecy of that data.
    Encrypt(plaintext, additionalData []byte) ([]byte, error)

    // Decrypt decrypts ciphertext with {@code additionalData} as additional
    // authenticated data. The decryption verifies the authenticity and integrity
    // of the additional data, but there are no guarantees wrt. secrecy of that data.
    Decrypt(ciphertext, additionalData []byte) ([]byte, error)
}
  • Encrypt Encrypt(plaintext, additionalData []byte) ([]byte, error)

    • 輸入:明文(plaintext)和額外的驗(yàn)證數(shù)據(jù)(additionalData)艇棕;
    • 返回:密文(ciphertext),或者錯(cuò)誤信息串塑;
  • Decrypt Decrypt(ciphertext, additionalData []byte) ([]byte, error)

    • 輸入:密文(ciphertext)和額外的驗(yàn)證數(shù)據(jù)(additionalData)沼琉;
    • 返回:明文(plaintext),或者錯(cuò)誤信息桩匪;

所有的處理操作都是以 []byte 的形式來處理的打瘪,而現(xiàn)實(shí)世界中一般都是字符串,但在 Go 語言中傻昙,字符串轉(zhuǎn)換成 []byte 是相當(dāng)?shù)暮?jiǎn)單闺骚,只需要 []byte("這是字符串") 就可以了。

你會(huì)發(fā)現(xiàn)妆档,這里有一個(gè)重要的內(nèi)容沒有看到僻爽,就是加解密使用的密鑰。這可以是個(gè)對(duì)密碼體系來說非常關(guān)鍵部分贾惦,怎么就沒有出現(xiàn)了胸梆?難道不需要使用么敦捧?

這就是 Tink 設(shè)計(jì)該加密庫(kù)的一個(gè)要點(diǎn),就是因?yàn)槊荑€相當(dāng)重要碰镜,所以必須通過密鑰系統(tǒng)來管理兢卵。后面文章中會(huì)提到如何使用密鑰管理系統(tǒng)來進(jìn)行密鑰管理的內(nèi)容。

額外認(rèn)證數(shù)據(jù)是什么绪颖?

額外認(rèn)證數(shù)據(jù) (additional authenticated data, AAD)秽荤,是在使用 AEAD primitive 時(shí),加密和解密中需要傳入的柠横。按照定義它是可以被用來提供一些包括版本或是以密文相關(guān)內(nèi)容的信息窃款,加密算法不會(huì)加密這類的數(shù)據(jù),但會(huì)對(duì)該數(shù)據(jù)進(jìn)行誰牍氛。

現(xiàn)在雁乡,我們來看看額外誰數(shù)據(jù)的特點(diǎn)。

額外認(rèn)證數(shù)據(jù)是否被加密糜俗?

額外認(rèn)證數(shù)據(jù)不是說以明文的形式存在于密文之中的么,那這些數(shù)據(jù)是插入到密文的前面曲饱?還是后面悠抹?或者說是中間?那現(xiàn)在采用一段代碼來觀察一下效果扩淀。以傳入大量的小寫字母 “a”楔敌,它的十六進(jìn)制數(shù)是 0x61,來看看密文的變化驻谆,有沒有這么多的 0x61 出現(xiàn)卵凑。

package main

import (
    "fmt"

    "github.com/google/tink/go/aead"
    "github.com/google/tink/go/keyset"
)

func main() {

    kh, _ := keyset.NewHandle(aead.AES256GCMKeyTemplate())

    a, _ := aead.New(kh)

    ct, _ := a.Encrypt([]byte("this data needs to be encrypted"), []byte("aaaaaaaaaaaaaaaa"))

    pt, _ := a.Decrypt(ct, []byte("aaaaaaaaaaaaaaaa"))

    fmt.Printf("Cipher text: %x\nPlain text: %s\n", ct, pt)

}

輸出為:

Cipher text: 0177d8f90b3c1ebab79afcb179e564762f71dcf65f98b022cce5c55099deda197dac1d33cd15d6b858050b2a9e153f8cb2997d28c87e79076c3d62761b211ad1
Plain text: this data needs to be encrypted

通過輸出的結(jié)果可以發(fā)現(xiàn),密文中并沒有那么多的 0x61 出現(xiàn)胜臊,那是不是這些數(shù)據(jù)被加密呢勺卢?

其實(shí)這些額外認(rèn)證數(shù)據(jù)并沒有被加密,它們只是和密文做了當(dāng)應(yīng)的運(yùn)算象对,主要是和密文進(jìn)行異或運(yùn)算黑忱。

額外認(rèn)證數(shù)據(jù)不一致能解密么?

如果輸入了錯(cuò)誤的額外認(rèn)證數(shù)據(jù)勒魔,密文是否能正常的解密甫煞?我們使用代碼測(cè)試下:

package main

import (
    "fmt"
    "log"

    "github.com/google/tink/go/aead"
    "github.com/google/tink/go/keyset"
)

func main() {

    kh, _ := keyset.NewHandle(aead.AES256GCMKeyTemplate())

    a, _ := aead.New(kh)

    ct, _ := a.Encrypt([]byte("this data needs to be encrypted"), []byte("associated data"))

    pt, err := a.Decrypt(ct, []byte("changed associated data"))
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Cipher text: %x\nPlain text: %s\n", ct, pt)

}

由于提供了不一樣的額外認(rèn)證數(shù)據(jù),運(yùn)行該代碼之后冠绢,會(huì)得到信息:

aead_factory: decryption failed

現(xiàn)在的結(jié)論就是加密時(shí)抚吠,如果使用和加密不一樣的額外認(rèn)證數(shù)據(jù)是無法將密文還原成明文的。

關(guān)于安全性的問題

雖然了解到上面的兩點(diǎn)內(nèi)容弟胀,包括不知道額外認(rèn)證數(shù)據(jù)不能解密密文楷力,但最終我們需要知道的是喊式,AEAD 的安全性還是會(huì)體現(xiàn)在密鑰的安全性上,如果密鑰被他人獲取弥雹,你是無法通過額外認(rèn)證數(shù)據(jù)獲得任何安全上的保障的垃帅。

在對(duì)稱加密體系中,安全保障是通過密鑰來提供的剪勿,這點(diǎn)是需要牢記的贸诚。

你能正常的解密么?

通過上面的示例和對(duì)該示例主要步驟的分解厕吉,讓你可以掌握基礎(chǔ)的 Tink 在對(duì)稱加密方面的使用酱固。

但是這還遠(yuǎn)遠(yuǎn)不夠。你可以運(yùn)行下面的兩段代碼來體驗(yàn)下在使用過程中遇到的問題头朱。也就是加解密分離运悲,因?yàn)樯厦婺嵌问纠a只是示例使用,不可能用到任何環(huán)境项钮,因?yàn)槟悴豢赡芸吹郊用芎篑R上又解密的班眯。

一般來說,我們的應(yīng)用場(chǎng)景可能是下面這樣:

  • 一個(gè)文件或字符串烁巫,通過加密算法加密后署隘,保存到文件系統(tǒng)或數(shù)據(jù)庫(kù)中,等到使用的時(shí)候再讀出來解密后使用亚隙;
  • 一段消息磁餐,通過加密算法加密后發(fā)送到指定的授受者,然后授受者使用事先約定好的密鑰解密后查看阿弃;

為了展示起來清晰诊霹,我將加解密的過程分開到兩段不同的代碼中,這樣你可以完全的了解代碼所需要做的事情渣淳。

加密信息

首先脾还,過程 Encrypt 接口對(duì)一段明文信息 “this data needs to be encrypted” 進(jìn)行加密:

package main

import (
    "fmt"
    "log"

    "github.com/google/tink/go/aead"
    "github.com/google/tink/go/keyset"
)

func main() {

    kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate())
    if err != nil {
        log.Fatal(err)
    }

    a, err := aead.New(kh)
    if err != nil {
        log.Fatal(err)
    }

    ct, err := a.Encrypt([]byte("this data needs to be encrypted"), []byte("associated data"))
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Cipher text: %x\n", ct)

}

運(yùn)行后可以得到密文:

$ go run main.go
Cipher text: 01ec5977d49fd2565fa00dd241b1d8dcbd073b497cdf4d8454dcc15dbf5e9705831125e99ac523d4f787e5e7a2b7bb51248ea4459ef6db749b2cbd0b321dcd40

解密密文

當(dāng)?shù)玫矫芪闹螅梢酝ㄟ^使用 Decrypt 進(jìn)行解密入愧。由于得到的是二進(jìn)制字符串荠呐,在 Go 中需要使用 hex.DecodeString 方法將其轉(zhuǎn)換成 []byte 類型:

package main

import (
    "encoding/hex"
    "fmt"
    "log"

    "github.com/google/tink/go/aead"
    "github.com/google/tink/go/keyset"
)

func main() {

    kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate())
    if err != nil {
        log.Fatal(err)
    }

    a, err := aead.New(kh)
    if err != nil {
        log.Fatal(err)
    }

    pt := "01ec5977d49fd2565fa00dd241b1d8dcbd073b497cdf4d8454dcc15dbf5e9705831125e99ac523d4f787e5e7a2b7bb51248ea4459ef6db749b2cbd0b321dcd40"
    ptByte, err := hex.DecodeString(pt)
    if err != nil {
        log.Fatal(err)
    }

    ct, err := a.Decrypt(ptByte, []byte("associated data"))
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Cipher text: %x\n", ct)

}

運(yùn)行代碼對(duì)上面的密文進(jìn)行解密:

$ go run main.go
aead_factory: decryption failed
exit status 1

然后,我們發(fā)現(xiàn)解密失敗砂客。這是為什么呢泥张?

其實(shí)這是因?yàn)?strong>解密使用的密鑰和加密使用的密鑰不一樣,加密使用的密鑰其實(shí)是隨機(jī)生成的鞠值,而并沒有保存下來媚创,加密代碼運(yùn)行完成之后,密鑰也就消失了彤恶。

后面會(huì)提到使用密鑰管理系統(tǒng)來處理密鑰問題钞钙。

重要的概念

  • KeysetHandle:用來處理鍵集(Keyset)的對(duì)象鳄橘;
  • Keyset:鍵集,由密鑰(Key)構(gòu)成的一個(gè)集合芒炼;
  • Key:密鑰瘫怜,在對(duì)稱加密算法中,加密和解密密鑰是一樣的本刽;
  • 密鑰管理系統(tǒng)(KMS):一個(gè)用來保存和管理密鑰生命周期的系統(tǒng)鲸湃;
  • Primitives(原語):這里指定的是與密碼學(xué)處理相關(guān)的接口;

總結(jié)

通過上面的 AEAD 加解密流程和注意事項(xiàng)等內(nèi)容子寓,我想你也應(yīng)該能了解到 Tink 里加解密相關(guān)設(shè)計(jì)和實(shí)現(xiàn)的思路暗挑。這樣的設(shè)計(jì)和實(shí)現(xiàn)的方式可以大大的減化你編寫對(duì)稱加密實(shí)現(xiàn)自身業(yè)務(wù)的難度。

在這里我們了解到使用 AEAD Primitives 來處理對(duì)稱加解密的過程斜友,并了解到了額外認(rèn)證數(shù)據(jù)(add)使用的注意事項(xiàng)炸裆。

不過 Tink 不是萬能的,它解決了這方面的問題鲜屏,但密鑰管理的問題還是留給你了烹看,因?yàn)樗吘怪皇且粋€(gè)密碼庫(kù)。Tink 提供了相應(yīng)的 API 連接 KMS (密鑰管理系統(tǒng))洛史,但密鑰管理的問題還是需要你去規(guī)劃和實(shí)施的部分听系,以后會(huì)介紹。

現(xiàn)在虹菲,你可以自己嘗試一下,如果加密時(shí)不使用額外認(rèn)證數(shù)據(jù)掉瞳,比如使用 nil 或 []byte("") 毕源,得到密文后,能不能使用 nil 或 []byte("") 正解解密密文陕习。

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霎褐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子该镣,更是在濱河造成了極大的恐慌冻璃,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件损合,死亡現(xiàn)場(chǎng)離奇詭異省艳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嫁审,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門跋炕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人律适,你說我怎么就攤上這事辐烂《舨澹” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵纠修,是天一觀的道長(zhǎng)胳嘲。 經(jīng)常有香客問我,道長(zhǎng)扣草,這世上最難降的妖魔是什么了牛? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮德召,結(jié)果婚禮上白魂,老公的妹妹穿的比我還像新娘。我一直安慰自己上岗,他們只是感情好福荸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肴掷,像睡著了一般敬锐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呆瞻,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天台夺,我揣著相機(jī)與錄音,去河邊找鬼痴脾。 笑死颤介,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赞赖。 我是一名探鬼主播滚朵,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼前域!你這毒婦竟也來了辕近?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤匿垄,失蹤者是張志新(化名)和其女友劉穎移宅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體椿疗,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漏峰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了届榄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芽狗。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖痒蓬,靈堂內(nèi)的尸體忽然破棺而出童擎,到底是詐尸還是另有隱情滴劲,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布顾复,位于F島的核電站班挖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芯砸。R本人自食惡果不足惜萧芙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望假丧。 院中可真熱鬧双揪,春花似錦、人聲如沸包帚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渴邦。三九已至疯趟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谋梭,已是汗流浹背信峻。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓮床,地道東北人盹舞。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像隘庄,于是被迫代替她去往敵國(guó)和親踢步。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356