package main
import ( "bytes" "crypto" "crypto/aes" "crypto/cipher" "crypto/ecdh" "crypto/ecdsa" "crypto/hmac" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/sha512" "crypto/x509" "encoding/asn1" "encoding/binary"
"fmt" "hash" "io" "math/big" "net" "strings" )
const ( TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F TLS_ECDHE_ECDSA_AES256_GCM_SHA384 = 0xC02C TLS_ECDHE_ECDSA_AES128_GCM_SHA256 = 0xC02B TLS_ECDHE_RSA_AES256_GCM_SHA384 = 0xC030 TLS_ECDHE_RSA_AES128_GCM_SHA256 = 0xC02F
EXT_SERVER_NAME = 0x0000 EXT_SIG_ALGORITHMS = 0x000D EXT_SUPPORTED_GROUPS = 0x000A EXT_EC_POINT_FORMATS = 0x000B EXT_EXTENDED_MASTER_SECRET = 0x0017 EXT_RENEGOTIATION_INFO = 0xFF01
CURVE_X25519 = 0x001D CURVE_SECP256R1 = 0x0017 CURVE_SECP384R1 = 0x0018
HASH_SHA256 = 0x04 HASH_SHA384 = 0x05 SIG_ECDSA = 0x03 SIG_RSA = 0x01 )
type tlsState struct { domain string clientRand []byte serverRand []byte cipherSuite uint16 certDER []byte serverPubKey interface{}
skxCurve uint16 skxServerPub []byte skxHashAlg byte skxSigAlg byte
clientECPriv *ecdh.PrivateKey clientECPub *ecdh.PublicKey sharedSecret []byte
masterSecret []byte clientWriteKey []byte serverWriteKey []byte clientWriteIV []byte serverWriteIV []byte
clientSeq uint64 serverSeq uint64 }
func alertMsg(desc byte) string { m := map[byte]string{ 0x00: "close_notify", 0x0A: "unexpected_message", 0x14: "bad_record_mac", 0x15: "decryption_failed", 0x16: "record_overflow", 0x17: "decompression_failure", 0x18: "handshake_failure", 0x19: "no_certificate", 0x1A: "bad_certificate", 0x1B: "unsupported_certificate", 0x1C: "certificate_revoked", 0x1D: "certificate_expired", 0x1E: "certificate_unknown", 0x1F: "illegal_parameter", 0x20: "unknown_ca", 0x21: "access_denied", 0x22: "decode_error", 0x23: "decrypt_error", 0x24: "export_restriction", 0x25: "protocol_version", 0x26: "insufficient_security", 0x27: "internal_error", 0x28: "user_canceled", 0x29: "no_renegotiation", 0x2A: "unsupported_extension", 0x2B: "certificate_unobtainable", 0x2C: "unrecognized_name", 0x2D: "no_application_protocol", } if v, ok := m[desc]; ok { return v } return fmt.Sprintf("unknown(0x%02x)", desc) }
func prf12(secret, label, seedA, seedB []byte, outLen int, useSHA384 bool) []byte { seed := append(seedA, seedB...) labelSeed := append(label, seed...) h := sha256.New if useSHA384 { h = sha512.New384 } return pHash(secret, labelSeed, outLen, h) }
func pHash(secret, seed []byte, outLen int, h func() hash.Hash) []byte { result := make([]byte, 0, outLen) A := seed for len(result) < outLen { A = hmacHash(secret, A, h) result = append(result, hmacHash(secret, append(A, seed...), h)...) } return result[:outLen] }
func hmacHash(secret, data []byte, h func() hash.Hash) []byte { mac := hmac.New(h, secret) mac.Write(data) return mac.Sum(nil) }
func buildClientHello(domain string) ([]byte, []byte) { random := make([]byte, 32) if _, err := rand.Read(random); err != nil { panic(err) }
cipherSuites := []byte{ 0xC0, 0x2B, } compression := []byte{0x00}
var exts []byte
sniDomain := []byte(domain) sniSN := bytes.NewBuffer(nil) sniSN.WriteByte(0x00) sniSN.Write([]byte{byte(len(sniDomain) >> 8), byte(len(sniDomain))}) sniSN.Write(sniDomain) sniED := bytes.NewBuffer(nil) sniED.Write([]byte{byte(sniSN.Len() >> 8), byte(sniSN.Len())}) sniED.Write(sniSN.Bytes()) sniE := bytes.NewBuffer(nil) sniE.Write([]byte{0x00, 0x00}) sniE.Write([]byte{byte(sniED.Len() >> 8), byte(sniED.Len())}) sniE.Write(sniED.Bytes()) exts = append(exts, sniE.Bytes()...)
sigAlgs := []byte{ 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x01, } sigED := bytes.NewBuffer(nil) sigED.Write([]byte{byte(len(sigAlgs) >> 8), byte(len(sigAlgs))}) sigED.Write(sigAlgs) sigE := bytes.NewBuffer(nil) sigE.Write([]byte{0x00, 0x0D}) sigE.Write([]byte{byte(sigED.Len() >> 8), byte(sigED.Len())}) sigE.Write(sigED.Bytes()) exts = append(exts, sigE.Bytes()...)
groups := []byte{0x00, 0x02, 0x00, 0x17} gE := bytes.NewBuffer(nil) gE.Write([]byte{0x00, 0x0A}) gE.Write([]byte{byte(len(groups) >> 8), byte(len(groups))}) gE.Write(groups) exts = append(exts, gE.Bytes()...)
ecFmt := []byte{0x01, 0x00} ecE := bytes.NewBuffer(nil) ecE.Write([]byte{0x00, 0x0B}) ecE.Write([]byte{byte(len(ecFmt) >> 8), byte(len(ecFmt))}) ecE.Write(ecFmt) exts = append(exts, ecE.Bytes()...)
riE := bytes.NewBuffer(nil) riE.Write([]byte{0xFF, 0x01}) riE.Write([]byte{0x00, 0x01, 0x00}) exts = append(exts, riE.Bytes()...)
extList := bytes.NewBuffer(nil) extList.Write([]byte{byte(len(exts) >> 8), byte(len(exts))}) extList.Write(exts)
body := bytes.NewBuffer(nil) body.Write([]byte{0x03, 0x03}) body.Write(random) body.WriteByte(0x00) body.Write([]byte{byte(len(cipherSuites) >> 8), byte(len(cipherSuites))}) body.Write(cipherSuites) body.WriteByte(0x01) body.Write(compression) body.Write(extList.Bytes())
handshakeBody := body.Bytes() hs := bytes.NewBuffer(nil) hs.WriteByte(0x01) hs.Write([]byte{byte(len(handshakeBody) >> 16), byte(len(handshakeBody) >> 8), byte(len(handshakeBody))}) hs.Write(handshakeBody) handshake := hs.Bytes()
record := bytes.NewBuffer(nil) record.WriteByte(0x16) record.Write([]byte{0x03, 0x03}) record.Write([]byte{byte(len(handshake) >> 8), byte(len(handshake))}) record.Write(handshake) return record.Bytes(), random }
func parseServerHandshake(data []byte, st *tlsState) error { r := bytes.NewReader(data) recIdx := 0 gotServerHelloDone := false for r.Len() > 0 { recIdx++ var typ [1]byte if _, err := r.Read(typ[:]); err != nil { return err } var ver [2]byte if _, err := r.Read(ver[:]); err != nil { return err } var length uint16 if err := binary.Read(r, binary.BigEndian, &length); err != nil { return err } payload := make([]byte, length) if _, err := io.ReadFull(r, payload); err != nil { return err } fmt.Printf(" Record #%d: type=0x%02x version=%d.%d length=%d\n", recIdx, typ[0], ver[0], ver[1], length)
if typ[0] == 0x16 { subR := bytes.NewReader(payload) msgIdx := 0 for subR.Len() > 0 { msgIdx++ var msgType byte if err := binary.Read(subR, binary.BigEndian, &msgType); err != nil { return err } var lenBuf [3]byte if _, err := io.ReadFull(subR, lenBuf[:]); err != nil { return err } msgLen := uint32(lenBuf[0])<<16 | uint32(lenBuf[1])<<8 | uint32(lenBuf[2]) msgData := make([]byte, msgLen) if _, err := io.ReadFull(subR, msgData); err != nil { return err } typeNames := map[byte]string{ 0x02: "ServerHello", 0x0B: "Certificate", 0x0C: "ServerKeyExchange", 0x0D: "CertificateRequest", 0x0E: "ServerHelloDone", } typeName := typeNames[msgType] if typeName == "" { typeName = fmt.Sprintf("Unknown(0x%02x)", msgType) } fmt.Printf(" Msg #%d: type=%s length=%d\n", msgIdx, typeName, msgLen)
switch msgType { case 0x02: if len(msgData) < 34 { return fmt.Errorf("server hello too short") } st.serverRand = make([]byte, 32) copy(st.serverRand, msgData[2:34]) sessionIDLen := int(msgData[34]) offset := 35 + sessionIDLen if offset+3 > len(msgData) { return fmt.Errorf("server hello too short for cipher") } st.cipherSuite = binary.BigEndian.Uint16(msgData[offset : offset+2]) compressionMethod := msgData[offset+2] fmt.Printf(" ServerHello: sessionIDLen=%d, cipherSuite=0x%04x, compressionMethod=%d\n", sessionIDLen, st.cipherSuite, compressionMethod) if offset+2 < len(msgData) { extTotalLen := int(binary.BigEndian.Uint16(msgData[offset+2 : offset+4])) extEnd := offset + 4 + extTotalLen fmt.Printf(" ServerHello extensions total length=%d\n", extTotalLen) off := offset + 4 for off < extEnd && off+4 <= len(msgData) { extType := binary.BigEndian.Uint16(msgData[off : off+2]) extLen := int(binary.BigEndian.Uint16(msgData[off+2 : off+4])) fmt.Printf(" Extension: type=0x%04x length=%d\n", extType, extLen) off += 4 + extLen } } case 0x0B: if len(msgData) < 3 { return fmt.Errorf("certificate too short") } certListLen := uint32(msgData[0])<<16 | uint32(msgData[1])<<8 | uint32(msgData[2]) certData := msgData[3 : 3+certListLen] certLen := uint32(certData[0])<<16 | uint32(certData[1])<<8 | uint32(certData[2]) fmt.Printf(" Certificate: certListLen=%d, first cert len=%d\n", certListLen, certLen) st.certDER = certData[3 : 3+certLen] case 0x0C: if err := parseServerKeyExchange(msgData, st); err != nil { return fmt.Errorf("ServerKeyExchange parse: %v", err) } skxPayload := bytes.NewBuffer(nil) skxPayload.WriteByte(0x0C) skxPayload.Write([]byte{byte(len(msgData) >> 16), byte(len(msgData) >> 8), byte(len(msgData))}) skxPayload.Write(msgData) fmt.Printf(" ServerKeyExchange: curve=0x%04x, serverPubKey len=%d\n", st.skxCurve, len(st.skxServerPub)) case 0x0E: gotServerHelloDone = true fmt.Println(" ServerHelloDone") } } } if gotServerHelloDone { break } if typ[0] == 0x15 { if len(payload) >= 2 { return fmt.Errorf("alert received: level=%d, desc=%s", payload[0], alertMsg(payload[1])) } return fmt.Errorf("alert received: %x", payload) } } return nil }
func parseServerKeyExchange(data []byte, st *tlsState) error { if len(data) < 4 { return fmt.Errorf("too short") } curveType := data[0] if curveType != 0x03 { return fmt.Errorf("unsupported curve type: %d", curveType) } st.skxCurve = binary.BigEndian.Uint16(data[1:3]) offset := 3
pubKeyLen := int(data[offset]) offset++ if offset+pubKeyLen > len(data) { return fmt.Errorf("server pub key exceeds data") } st.skxServerPub = data[offset : offset+pubKeyLen] offset += pubKeyLen
if offset+2 > len(data) { return fmt.Errorf("missing signature algorithm") } st.skxHashAlg = data[offset] st.skxSigAlg = data[offset+1] offset += 2 fmt.Printf(" ServerKeyExchange: hash=0x%02x sig=0x%02x\n", st.skxHashAlg, st.skxSigAlg)
sigLen := int(binary.BigEndian.Uint16(data[offset : offset+2])) offset += 2 if offset+sigLen > len(data) { return fmt.Errorf("signature exceeds data") } sig := data[offset : offset+sigLen]
if err := verifyServerKeyExchangeSignature(data, st, sig); err != nil { return fmt.Errorf("signature verify failed: %v", err) } fmt.Println(" ServerKeyExchange signature verified OK") return nil }
type ecdsaSigValue struct { R, S *big.Int }
func verifyServerKeyExchangeSignature(skxData []byte, st *tlsState, sigDER []byte) error { var sig ecdsaSigValue if rest, err := asn1.Unmarshal(sigDER, &sig); err != nil { return fmt.Errorf("DER parse: %v", err) } else if len(rest) > 0 { return fmt.Errorf("trailing data after signature") }
verifyData := bytes.NewBuffer(nil) verifyData.Write(st.clientRand) verifyData.Write(st.serverRand) verifyData.Write([]byte{0x03}) verifyData.Write([]byte{byte(st.skxCurve >> 8), byte(st.skxCurve)}) pubKeyLen := len(st.skxServerPub) verifyData.Write([]byte{byte(pubKeyLen)}) verifyData.Write(st.skxServerPub)
var hashBytes []byte switch st.skxHashAlg { case 0x04: h := sha256.Sum256(verifyData.Bytes()) hashBytes = h[:] case 0x05: h := sha512.Sum384(verifyData.Bytes()) hashBytes = h[:] case 0x06: h := sha512.Sum512(verifyData.Bytes()) hashBytes = h[:] default: return fmt.Errorf("unsupported hash algorithm: 0x%02x", st.skxHashAlg) }
cert, err := x509.ParseCertificate(st.certDER) if err != nil { return err } st.serverPubKey = cert.PublicKey
switch pub := cert.PublicKey.(type) { case *ecdsa.PublicKey: if !ecdsa.Verify(pub, hashBytes, sig.R, sig.S) { return fmt.Errorf("ECDSA signature verification failed") } fmt.Printf(" ECDSA signature verified (hash=0x%02x)\n", st.skxHashAlg) case *rsa.PublicKey: cryptoHash := crypto.SHA256 switch st.skxHashAlg { case 0x04: cryptoHash = crypto.SHA256 case 0x05: cryptoHash = crypto.SHA384 case 0x06: cryptoHash = crypto.SHA512 } if err := rsa.VerifyPKCS1v15(pub, cryptoHash, hashBytes, sigDER); err != nil { return fmt.Errorf("RSA signature verification failed: %v", err) } fmt.Printf(" RSA signature verified (hash=0x%02x)\n", st.skxHashAlg) default: return fmt.Errorf("unsupported public key type") } return nil }
func generateECDHEKeyPair(curve uint16) (*ecdh.PrivateKey, *ecdh.PublicKey, error) { switch curve { case CURVE_X25519: priv, err := ecdh.X25519().GenerateKey(rand.Reader) if err != nil { return nil, nil, err } return priv, priv.PublicKey(), nil case CURVE_SECP256R1: priv, err := ecdh.P256().GenerateKey(rand.Reader) if err != nil { return nil, nil, err } return priv, priv.PublicKey(), nil default: return nil, nil, fmt.Errorf("unsupported curve: 0x%04x", curve) } }
func buildClientKeyExchangeECDH(pubKey *ecdh.PublicKey) []byte { pubBytes := pubKey.Bytes() body := bytes.NewBuffer(nil) body.Write([]byte{byte(len(pubBytes))}) body.Write(pubBytes) handshakeBody := body.Bytes()
hs := bytes.NewBuffer(nil) hs.WriteByte(0x10) hs.Write([]byte{byte(len(handshakeBody) >> 16), byte(len(handshakeBody) >> 8), byte(len(handshakeBody))}) hs.Write(handshakeBody) handshake := hs.Bytes()
record := bytes.NewBuffer(nil) record.WriteByte(0x16) record.Write([]byte{0x03, 0x03}) record.Write([]byte{byte(len(handshake) >> 8), byte(len(handshake))}) record.Write(handshake) return record.Bytes() }
func computeSharedSecretECDH(curve uint16, priv *ecdh.PrivateKey, serverPubBytes []byte) ([]byte, error) { var serverPub *ecdh.PublicKey var err error
switch curve { case CURVE_X25519: serverPub, err = ecdh.X25519().NewPublicKey(serverPubBytes) if err != nil { return nil, fmt.Errorf("parse server X25519 pub: %v", err) } case CURVE_SECP256R1: serverPub, err = ecdh.P256().NewPublicKey(serverPubBytes) if err != nil { return nil, fmt.Errorf("parse server P-256 pub: %v", err) } default: return nil, fmt.Errorf("unsupported curve for shared secret: 0x%04x", curve) }
secret, err := priv.ECDH(serverPub) if err != nil { return nil, fmt.Errorf("ECDH compute: %v", err) } return secret, nil }
func deriveKeysAEAD(preMaster, clientRand, serverRand []byte, keyLen int, useSHA384 bool) ([]byte, []byte, []byte, []byte) { masterSecret := prf12(preMaster, []byte("master secret"), clientRand, serverRand, 48, useSHA384) fmt.Printf(" masterSecret=%x\n", masterSecret)
keyBlock := prf12(masterSecret, []byte("key expansion"), serverRand, clientRand, keyLen*2+8, useSHA384) clientWriteKey := keyBlock[0:keyLen] serverWriteKey := keyBlock[keyLen : keyLen*2] clientWriteIV := keyBlock[keyLen*2 : keyLen*2+4] serverWriteIV := keyBlock[keyLen*2+4 : keyLen*2+8] fmt.Printf(" clientWriteKey=%x\n", clientWriteKey) fmt.Printf(" serverWriteKey=%x\n", serverWriteKey) fmt.Printf(" clientWriteIV=%x\n", clientWriteIV) fmt.Printf(" serverWriteIV=%x\n", serverWriteIV)
return clientWriteKey, serverWriteKey, clientWriteIV, serverWriteIV }
func encryptGCM(data, writeKey, iv []byte, seqNum uint64, contentType byte) []byte { plainLen := len(data)
nonce := make([]byte, 12) copy(nonce[0:4], iv) binary.BigEndian.PutUint64(nonce[4:12], seqNum)
aad := make([]byte, 0, 15) seqBytes := make([]byte, 8) binary.BigEndian.PutUint64(seqBytes, seqNum) aad = append(aad, seqBytes...) aad = append(aad, contentType) aad = append(aad, 0x03, 0x03) aad = append(aad, byte(plainLen>>8), byte(plainLen))
block, err := aes.NewCipher(writeKey) if err != nil { panic(err) } aesgcm, err := cipher.NewGCM(block) if err != nil { panic(err) }
ciphertext := aesgcm.Seal(nil, nonce, data, aad)
nonceExplicit := make([]byte, 8) binary.BigEndian.PutUint64(nonceExplicit, seqNum) result := append(nonceExplicit, ciphertext...) return result }
func decryptGCM(ciphertext, writeKey, iv []byte, seqNum uint64, contentType byte) ([]byte, error) { if len(ciphertext) < 8+16 { return nil, fmt.Errorf("ciphertext too short for nonce+tag") }
explicitNonce := ciphertext[:8] actualCiphertext := ciphertext[8:] plainLen := len(actualCiphertext) - 16
nonce := make([]byte, 12) copy(nonce[0:4], iv) copy(nonce[4:12], explicitNonce)
aad := make([]byte, 0, 15) seqBytes := make([]byte, 8) binary.BigEndian.PutUint64(seqBytes, seqNum) aad = append(aad, seqBytes...) aad = append(aad, contentType) aad = append(aad, 0x03, 0x03) aad = append(aad, byte(plainLen>>8), byte(plainLen))
block, err := aes.NewCipher(writeKey) if err != nil { return nil, err } aesgcm, err := cipher.NewGCM(block) if err != nil { return nil, err }
plaintext, err := aesgcm.Open(nil, nonce, actualCiphertext, aad) if err != nil { return nil, fmt.Errorf("GCM decrypt/verify failed: %v", err) } return plaintext, nil }
func sendRecord(conn net.Conn, contentType byte, payload, writeKey, iv []byte, seqNum uint64) ([]byte, error) { encrypted := encryptGCM(payload, writeKey, iv, seqNum, contentType) record := bytes.NewBuffer(nil) record.WriteByte(contentType) record.Write([]byte{0x03, 0x03}) record.Write([]byte{byte(len(encrypted) >> 8), byte(len(encrypted))}) record.Write(encrypted)
_, err := conn.Write(record.Bytes()) return record.Bytes(), err }
func recvRecord(conn net.Conn, readKey, iv []byte, seqNum uint64) (contentType byte, plaintext []byte, rawRecord []byte, err error) { header := make([]byte, 5) _, err = io.ReadFull(conn, header) if err != nil { return 0, nil, nil, err } contentType = header[0] length := binary.BigEndian.Uint16(header[3:5])
if contentType == 0x14 { payload := make([]byte, length) io.ReadFull(conn, payload) rawRecord = append(header, payload...)
return contentType, payload, rawRecord, nil }
encrypted := make([]byte, length) if _, err := io.ReadFull(conn, encrypted); err != nil { return 0, nil, nil, err } rawRecord = append(header, encrypted...)
plaintext, err = decryptGCM(encrypted, readKey, iv, seqNum, contentType) if err != nil { return 0, nil, nil, err }
if contentType == 0x15 { fmt.Printf("<<< Alert (decrypted): level=%d desc=%s\n", plaintext[0], alertMsg(plaintext[1])) return contentType, plaintext, rawRecord, nil }
return contentType, plaintext, rawRecord, nil }
func containsServerHelloDone(payload []byte) bool { r := bytes.NewReader(payload) for r.Len() >= 4 { var msgType byte if err := binary.Read(r, binary.BigEndian, &msgType); err != nil { return false } var lenBuf [3]byte if _, err := io.ReadFull(r, lenBuf[:]); err != nil { return false } msgLen := uint32(lenBuf[0])<<16 | uint32(lenBuf[1])<<8 | uint32(lenBuf[2]) if msgType == 0x0E && msgLen == 0 { return true } skip := make([]byte, msgLen) if msgLen > 0 { if _, err := io.ReadFull(r, skip); err != nil { return false } } } return false }
func readFullHandshake(conn net.Conn) ([]byte, int, error) { var all []byte recIdx := 0 for { header := make([]byte, 5) n, err := io.ReadFull(conn, header) if err != nil { return all, recIdx, nil } recIdx++ typ := header[0] length := binary.BigEndian.Uint16(header[3:5]) fmt.Printf(" readFullHandshake #%d: type=0x%02x length=%d (header bytes: %d)\n", recIdx, typ, length, n) payload := make([]byte, length) if _, err := io.ReadFull(conn, payload); err != nil { return nil, recIdx, err } all = append(all, header...) all = append(all, payload...) if typ == 0x16 { if containsServerHelloDone(payload) { fmt.Printf(" Found ServerHelloDone, total read=%d\n", len(all)) break } } if typ == 0x14 { fmt.Printf(" Found ChangeCipherSpec, total read=%d\n", len(all)) break } } return all, recIdx, nil }
func extractHandshakeMessages(tlsRecords []byte) []byte { var handshakeMsgs []byte r := bytes.NewReader(tlsRecords) for r.Len() >= 5 { var typ [1]byte if _, err := r.Read(typ[:]); err != nil { break } var ver [2]byte if _, err := r.Read(ver[:]); err != nil { break } var length uint16 if err := binary.Read(r, binary.BigEndian, &length); err != nil { break } payload := make([]byte, length) if _, err := io.ReadFull(r, payload); err != nil { break } if typ[0] == 0x16 { handshakeMsgs = append(handshakeMsgs, payload...) } } return handshakeMsgs }
func main() { target := "yishuifengxiao.com:443" domain := "yishuifengxiao.com"
conn, err := net.Dial("tcp", target) if err != nil { panic(err) } defer conn.Close()
st := &tlsState{domain: domain}
clientHello, clientRand := buildClientHello(domain) st.clientRand = clientRand clientHelloHsMsg := clientHello[5:] fmt.Printf(">>> ClientHello (%d bytes)\n", len(clientHello))
if _, err := conn.Write(clientHello); err != nil { panic(err) } st.clientSeq++
serverData, serverRecCount, err := readFullHandshake(conn) if err != nil { panic(err) } fmt.Printf("<<< Received %d bytes of server handshake (%d records)\n", len(serverData), serverRecCount)
st.serverSeq = uint64(serverRecCount)
if err := parseServerHandshake(serverData, st); err != nil { panic(err) }
switch st.cipherSuite { case TLS_ECDHE_ECDSA_AES256_GCM_SHA384: fmt.Println(" Negotiated: ECDHE-ECDSA-AES256-GCM-SHA384") case TLS_ECDHE_ECDSA_AES128_GCM_SHA256: fmt.Println(" Negotiated: ECDHE-ECDSA-AES128-GCM-SHA256") case TLS_ECDHE_RSA_AES256_GCM_SHA384: fmt.Println(" Negotiated: ECDHE-RSA-AES256-GCM-SHA384") case TLS_ECDHE_RSA_AES128_GCM_SHA256: fmt.Println(" Negotiated: ECDHE-RSA-AES128-GCM-SHA256") default: panic(fmt.Sprintf("unsupported cipher suite: 0x%04x", st.cipherSuite)) }
priv, pub, err := generateECDHEKeyPair(st.skxCurve) if err != nil { panic(err) } st.clientECPriv = priv st.clientECPub = pub
curveName := "unknown" switch st.skxCurve { case CURVE_X25519: curveName = "X25519" case CURVE_SECP256R1: curveName = "secp256r1 (P-256)" } fmt.Printf(" Generated %s key pair\n", curveName)
privBytes := priv.Bytes() pubBytes := pub.Bytes() fmt.Printf(" clientECPriv=%x (len=%d)\n", privBytes, len(privBytes)) fmt.Printf(" clientECPub=%x (len=%d)\n", pubBytes, len(pubBytes))
sharedSecret, err := computeSharedSecretECDH(st.skxCurve, priv, st.skxServerPub) if err != nil { panic(err) } st.sharedSecret = sharedSecret fmt.Printf(" sharedSecret=%x\n", sharedSecret)
cke := buildClientKeyExchangeECDH(pub) ckeHsMsg := cke[5:] fmt.Printf(">>> ClientKeyExchange (%d bytes)\n", len(cke))
if _, err := conn.Write(cke); err != nil { panic(err) } st.clientSeq++
var keyLen int useSHA384 := false switch st.cipherSuite { case TLS_ECDHE_ECDSA_AES128_GCM_SHA256, TLS_ECDHE_RSA_AES128_GCM_SHA256: keyLen = 16 default: keyLen = 32 useSHA384 = true }
st.masterSecret = prf12(sharedSecret, []byte("master secret"), st.clientRand, st.serverRand, 48, useSHA384) keyBlock := prf12(st.masterSecret, []byte("key expansion"), st.serverRand, st.clientRand, keyLen*2+8, useSHA384) st.clientWriteKey = keyBlock[0:keyLen] st.serverWriteKey = keyBlock[keyLen : keyLen*2] st.clientWriteIV = keyBlock[keyLen*2 : keyLen*2+4] st.serverWriteIV = keyBlock[keyLen*2+4 : keyLen*2+8] fmt.Printf(" masterSecret=%x\n", st.masterSecret) fmt.Printf(" clientWriteKey=%x\n", st.clientWriteKey) fmt.Printf(" serverWriteKey=%x\n", st.serverWriteKey) fmt.Printf(" clientWriteIV=%x\n", st.clientWriteIV) fmt.Printf(" serverWriteIV=%x\n", st.serverWriteIV)
ccs := []byte{0x14, 0x03, 0x03, 0x00, 0x01, 0x01} if _, err := conn.Write(ccs); err != nil { panic(err) } fmt.Println(">>> ChangeCipherSpec sent")
st.clientSeq++ st.clientSeq = 0
serverHsMsgs := extractHandshakeMessages(serverData) allHsMsgs := append(clientHelloHsMsg, serverHsMsgs...) allHsMsgs = append(allHsMsgs, ckeHsMsg...) fmt.Printf(" Handshake messages: clientHello=%d + server=%d + cke=%d = total %d bytes\n", len(clientHelloHsMsg), len(serverHsMsgs), len(ckeHsMsg), len(allHsMsgs))
fmt.Printf(" clientHelloHsMsg=%x\n", clientHelloHsMsg) fmt.Printf(" serverHsMsgs=%x\n", serverHsMsgs) fmt.Printf(" ckeHsMsg=%x\n", ckeHsMsg)
var hsHash []byte if useSHA384 { h := sha512.Sum384(allHsMsgs) hsHash = h[:] fmt.Printf(" Handshake messages SHA-384 hash=%x\n", hsHash) } else { h := sha256.Sum256(allHsMsgs) hsHash = h[:] fmt.Printf(" Handshake messages SHA-256 hash=%x\n", hsHash) }
verifyData := prf12(st.masterSecret, []byte("client finished"), hsHash, nil, 12, useSHA384) fmt.Printf(" verify_data=%x\n", verifyData)
finishedMsg := bytes.NewBuffer(nil) finishedMsg.WriteByte(0x14) finishedMsg.Write([]byte{0x00, 0x00, 0x0C}) finishedMsg.Write(verifyData) fmt.Printf(" finishedMsg (%d bytes): %x\n", finishedMsg.Len(), finishedMsg.Bytes())
plainFinished := finishedMsg.Bytes() encryptedFinished := encryptGCM(plainFinished, st.clientWriteKey, st.clientWriteIV, st.clientSeq, 0x16)
finishedHeader := bytes.NewBuffer(nil) finishedHeader.WriteByte(0x16) finishedHeader.Write([]byte{0x03, 0x03}) finishedHeader.Write([]byte{byte(len(encryptedFinished) >> 8), byte(len(encryptedFinished))}) finishedHeader.Write(encryptedFinished) finishedRecord := finishedHeader.Bytes() fmt.Printf(" finishedRecord (%d bytes)\n", len(finishedRecord))
if _, err := conn.Write(finishedRecord); err != nil { panic(err) } fmt.Println(">>> Finished sent (encrypted)") st.clientSeq++
contentType, _, _, err := recvRecord(conn, st.serverWriteKey, st.serverWriteIV, st.serverSeq) if err != nil { panic(fmt.Sprintf("recv CCS error: %v", err)) } if contentType != 0x14 { panic(fmt.Sprintf("expected ChangeCipherSpec, got 0x%02x", contentType)) }
st.serverSeq++ st.serverSeq = 0
contentType, _, _, err = recvRecord(conn, st.serverWriteKey, st.serverWriteIV, st.serverSeq) if err != nil { panic(fmt.Sprintf("recv Finished error: %v", err)) } if contentType != 0x16 { panic(fmt.Sprintf("expected Finished, got 0x%02x", contentType)) }
st.serverSeq++
httpReq := "GET / HTTP/1.1\r\nHost: " + domain + "\r\nConnection: close\r\nUser-Agent: manual-tls\r\n\r\n" httpData := []byte(httpReq)
fmt.Println("========== 发送的 Application Data ==========")
sendEncrypted := encryptGCM(httpData, st.clientWriteKey, st.clientWriteIV, st.clientSeq, 0x17)
sendRecordBuf := bytes.NewBuffer(nil) sendRecordBuf.WriteByte(0x17) sendRecordBuf.Write([]byte{0x03, 0x03}) sendRecordBuf.Write([]byte{byte(len(sendEncrypted) >> 8), byte(len(sendEncrypted))}) sendRecordBuf.Write(sendEncrypted)
if _, err := conn.Write(sendRecordBuf.Bytes()); err != nil { panic(err) } st.clientSeq++ fmt.Printf(">>> Application Data 发送成功 (seq=%d)\n", st.clientSeq-1) fmt.Println("============================================")
fmt.Println("========== 接收的 Application Data ==========") for { contentType, _, _, err := recvRecord(conn, st.serverWriteKey, st.serverWriteIV, st.serverSeq) if err != nil { if err.Error() == "EOF" || strings.Contains(err.Error(), "EOF") { break } fmt.Printf(" Recv error: %v\n", err) break } if contentType == 0x15 { fmt.Println("<<< Alert received during HTTP") break } if contentType == 0x17 { st.serverSeq++ } } fmt.Println("============================================") }
|