本小节主要是介绍使用tls链路传输时,是如何对数据帧进行加密的?
我们以客户端一侧发送数据帧为例。
假设,将创建好的数据帧交由帧发送器进行发送,看看是如何对数据帧进行加密的?
1、分析入口?帧发送器里的processData |
分析入口是grpc-go/internal/transport/controlbuf.go文件中的processData方法里:
func (l *loopyWriter) processData() (bool, error) {
//---省略不相关代码
err := l.framer.fr.WriteData(dataItem.streamID, endStream, buf[:size]);
//---省略不相关代码
主要调用链,可参考下面:
WriteData→WriteDataPadded→f.endWrite()→f.w.Write(f.wbuf)
最终进入go/1.15.5/libexec/src/crypto/tls/conn.go文件中Conn结构下的Write方法里:
1.func (c *Conn) Write(b []byte) (int, error) {
2. for {
3. //---省略不相关代码
4. if err := c.Handshake(); err != nil {
5. return 0, err
6. }
7. //---省略不相关代码
8. n, err := c.writeRecordLocked(recordTypeApplicationData, b)
9. return n + m, c.out.setErrorLocked(err)
10.}
主要流程说明:
2、数据帧dataFame的加密过程encrypt |
进入go/1.15.5/libexec/src/crypto/tls/conn.go文件中writeRecordLocked结构下的
1.func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) {
2. var n int
3. for len(data) > 0 {
4. m := len(data)
//---省略不相关代码
5. _, c.outBuf = sliceForAppend(c.outBuf[:0], recordHeaderLen)
6. c.outBuf[0] = byte(typ)
7. vers := c.vers
8. if vers == 0 {
9. vers = VersionTLS10
10. } else if vers == VersionTLS13 {
11. vers = VersionTLS12
12. }
13. c.outBuf[1] = byte(vers >> 8)
14. c.outBuf[2] = byte(vers)
15. c.outBuf[3] = byte(m >> 8)
16. c.outBuf[4] = byte(m)
17. var err error
18. log.Infof("-----mdata---------m----1:%d\tdata1:%v", m, string(data))
19. c.outBuf, err = c.out.encrypt(c.outBuf, data[:m], c.config.rand())
20. if err != nil {
21. return n, err
22. }
23. outbufs := hex.EncodeToString(c.outBuf)
24. log.Infof("-----mdata---------m---2:%d\tdata2:%v", m, outbufs)
25. if _, err := c.write(c.outBuf); err != nil {
26. return n, err
27. }
28. //---省略不相关代码
29.}
主要流程说明:
2.1、根据类型选择加密算法? |
我们只关心第19行encrypt方法:
进入go/1.15.5/libexec/src/crypto/tls/conn.go文件中halfConn结构体下的encrypt方法里:
1.func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) {
2. //---省略不重要代码
3. switch c := hc.cipher.(type) {
4. case cipher.Stream:
5. //---省略不相关代码
6. case aead:
7. nonce := explicitNonce
8. if len(nonce) == 0 {
9. nonce = hc.seq[:]
10. }
11. if hc.version == VersionTLS13 {
12. record = append(record, payload...)
13. record = append(record, record[0])
14. record[0] = byte(recordTypeApplicationData)
15. n := len(payload) + 1 + c.Overhead()
16. record[3] = byte(n >> 8)
17. record[4] = byte(n)
18. record = c.Seal(record[:recordHeaderLen],
19. nonce, record[recordHeaderLen:], record[:recordHeaderLen])
20. } else {
21. copy(hc.additionalData[:], hc.seq[:])
22. copy(hc.additionalData[8:], record)
23. record = c.Seal(record, nonce, payload, hc.additionalData[:])
24. }
25. case cbcMode:
26. //---省略不相关代码
27. default:
28. panic("unknown cipher type")
29. }
30.//---省略不相关代码
31. return record, nil
32.}
主要流程说明:
2.2、如何创建cipher?如何使用在tls链路建立阶段双方协商好的数据作为加密数据的? |
调用Seal其实,也就是调用的cipher的Seal方法
因此,需要知道cipher在什么地方进行的初始化?
进入go/1.15.5/libexec/src/crypto/tls/handshake_client_tls13.go文件中的clientHandshakeStateTLS13结构体下的handshake方法里:
(也就是进入到了客户端跟服务器端tcp链路建立后的握手阶段)
1.func (hs *clientHandshakeStateTLS13) handshake() error {
2. //---省略不相关代码
3. if err := hs.processServerHello(); err != nil {
4. return err
5. }
6. if err := hs.sendDummyChangeCipherSpec(); err != nil {
7. return err
8. }
9. if err := hs.establishHandshakeKeys(); err != nil {
10. return err
11. }
12. //---省略不相关代码
我们只关心第9行的establishHandshakeKeys方法,
进入go/1.15.5/libexec/src/crypto/tls/handshake_client_tls13.go文件中的establishHandshakeKeys方法里:
1.func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
2. c := hs.c
3. sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
4. //---省略不相关代码
5. handshakeSecret := hs.suite.extract(sharedKey,
6. hs.suite.deriveSecret(earlySecret, "derived", nil))
7. //---省略不相关代码
8. clientSecret := hs.suite.deriveSecret(handshakeSecret,
9. clientHandshakeTrafficLabel, hs.transcript)
10. c.out.setTrafficSecret(hs.suite, clientSecret)
主要流程说明:
进入setTrafficSecret方法里:
1.func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) {
2. hc.trafficSecret = secret
3. key, iv := suite.trafficKey(secret)
4. hc.cipher = suite.aead(key, iv)
5. for i := range hc.seq {
6. hc.seq[i] = 0
7. }
8.}
主要流程说明:
点击suite.aead函数,进入到go/1.15.5/libexec/src/crypto/tls/cipher_suites.go文件中的cipherSuiteTLS13结构体里:
type cipherSuiteTLS13 struct {
id uint16
keyLen int
aead func(key, fixedNonce []byte) aead
hash crypto.Hash
}
aead就是一个函数类型,返还的是aead;
进入到go/1.15.5/libexec/src/crypto/tls/cipher_suites.go文件中aeadAESGCMTLS13函数里:
1.func aeadAESGCMTLS13(key, nonceMask []byte) aead {
2. if len(nonceMask) != aeadNonceLength {
3. panic("tls: internal error: wrong nonce length")
4. }
5. aes, err := aes.NewCipher(key)
6. if err != nil {
7. panic(err)
8. }
9. aead, err := cipher.NewGCM(aes)
10. if err != nil {
11. panic(err)
12. }
13. ret := &xorNonceAEAD{aead: aead}
14. copy(ret.nonceMask[:], nonceMask)
15. return ret
16.}
其中参数key里暗含着clientSecret数据;
经过第5行,第9行,第13行的转换,
那么,第13行中的aead同样暗含着clientSecret数据。
最终将clientSecret数据封装到了xorNonceAEAD结构体里。
其实,setTrafficSecret方法中的hc.cipher = suite.aead(key, iv)语句的最终结果,hc.cipher就是xorNonceAEAD
此时,已经知道了使用哪个加密算法,以及协商好的加密数据,接下来,就是开始真正的对数据帧进行加密了。
2.3、使用加密算法以及协商好的加密数据开始对数据帧真正加密? |
此时,我们返回到go/1.15.5/libexec/src/crypto/tls/conn.go文件中的halfConn结构体下的encrypt方法里,
调用Seal方法,也就是调用的xorNonceAEAD结构体的Seal方法;
因此,我们进入go/1.15.5/libexec/src/crypto/tls/cipher_suites.go文件中xorNonceAEAD结构体下的Seal方法里:
1.func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
2. for i, b := range nonce {
3. f.nonceMask[4+i] ^= b
4. }
5. result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
6. for i, b := range nonce {
7. f.nonceMask[4+i] ^= b
8. }
9. return result
10.}
我们只关注第5行,其中f.aead里存储着客户端跟服务器端tls链路建立时协商好的数据,如在客户端一侧,协商好的最终数据是clientSecret,
此时第5行在调用Seal时,会用到clientSecret,具体就不再看了。
到目前为止,我们介绍完了数据帧在传输阶段确实加密了,加密时用到了tls链路建立阶段双方协商好的一段数据,用以加密。
下一篇文章
如何理解认证token?