mirror of
				https://github.com/ehang-io/nps
				synced 2025-10-26 21:27:10 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			254 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package crypt
 | |
| 
 | |
| import (
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| type CurveID uint16
 | |
| type SignatureScheme uint16
 | |
| 
 | |
| const (
 | |
| 	statusTypeOCSP               uint8  = 1
 | |
| 	extensionServerName          uint16 = 0
 | |
| 	extensionStatusRequest       uint16 = 5
 | |
| 	extensionSupportedCurves     uint16 = 10
 | |
| 	extensionSupportedPoints     uint16 = 11
 | |
| 	extensionSignatureAlgorithms uint16 = 13
 | |
| 	extensionALPN                uint16 = 16
 | |
| 	extensionSCT                 uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6
 | |
| 	extensionSessionTicket       uint16 = 35
 | |
| 	extensionNextProtoNeg        uint16 = 13172 // not IANA assigned
 | |
| 	extensionRenegotiationInfo   uint16 = 0xff01
 | |
| 	scsvRenegotiation            uint16 = 0x00ff
 | |
| )
 | |
| 
 | |
| type ClientHelloMsg struct {
 | |
| 	raw                          []byte
 | |
| 	vers                         uint16
 | |
| 	random                       []byte
 | |
| 	sessionId                    []byte
 | |
| 	cipherSuites                 []uint16
 | |
| 	compressionMethods           []uint8
 | |
| 	nextProtoNeg                 bool
 | |
| 	serverName                   string
 | |
| 	ocspStapling                 bool
 | |
| 	scts                         bool
 | |
| 	supportedCurves              []CurveID
 | |
| 	supportedPoints              []uint8
 | |
| 	ticketSupported              bool
 | |
| 	sessionTicket                []uint8
 | |
| 	supportedSignatureAlgorithms []SignatureScheme
 | |
| 	secureRenegotiation          []byte
 | |
| 	secureRenegotiationSupported bool
 | |
| 	alpnProtocols                []string
 | |
| }
 | |
| 
 | |
| func (m *ClientHelloMsg) GetServerName() string {
 | |
| 	return m.serverName
 | |
| }
 | |
| 
 | |
| func (m *ClientHelloMsg) Unmarshal(data []byte) bool {
 | |
| 	if len(data) < 42 {
 | |
| 		return false
 | |
| 	}
 | |
| 	m.raw = data
 | |
| 	m.vers = uint16(data[4])<<8 | uint16(data[5])
 | |
| 	m.random = data[6:38]
 | |
| 	sessionIdLen := int(data[38])
 | |
| 	if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
 | |
| 		return false
 | |
| 	}
 | |
| 	m.sessionId = data[39 : 39+sessionIdLen]
 | |
| 	data = data[39+sessionIdLen:]
 | |
| 	if len(data) < 2 {
 | |
| 		return false
 | |
| 	}
 | |
| 	// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
 | |
| 	// they are uint16s, the number must be even.
 | |
| 	cipherSuiteLen := int(data[0])<<8 | int(data[1])
 | |
| 	if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
 | |
| 		return false
 | |
| 	}
 | |
| 	numCipherSuites := cipherSuiteLen / 2
 | |
| 	m.cipherSuites = make([]uint16, numCipherSuites)
 | |
| 	for i := 0; i < numCipherSuites; i++ {
 | |
| 		m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
 | |
| 		if m.cipherSuites[i] == scsvRenegotiation {
 | |
| 			m.secureRenegotiationSupported = true
 | |
| 		}
 | |
| 	}
 | |
| 	data = data[2+cipherSuiteLen:]
 | |
| 	if len(data) < 1 {
 | |
| 		return false
 | |
| 	}
 | |
| 	compressionMethodsLen := int(data[0])
 | |
| 	if len(data) < 1+compressionMethodsLen {
 | |
| 		return false
 | |
| 	}
 | |
| 	m.compressionMethods = data[1 : 1+compressionMethodsLen]
 | |
| 	data = data[1+compressionMethodsLen:]
 | |
| 
 | |
| 	m.nextProtoNeg = false
 | |
| 	m.serverName = ""
 | |
| 	m.ocspStapling = false
 | |
| 	m.ticketSupported = false
 | |
| 	m.sessionTicket = nil
 | |
| 	m.supportedSignatureAlgorithms = nil
 | |
| 	m.alpnProtocols = nil
 | |
| 	m.scts = false
 | |
| 
 | |
| 	if len(data) == 0 {
 | |
| 		// ClientHello is optionally followed by extension data
 | |
| 		return true
 | |
| 	}
 | |
| 	if len(data) < 2 {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	extensionsLength := int(data[0])<<8 | int(data[1])
 | |
| 	data = data[2:]
 | |
| 	if extensionsLength != len(data) {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	for len(data) != 0 {
 | |
| 		if len(data) < 4 {
 | |
| 			return false
 | |
| 		}
 | |
| 		extension := uint16(data[0])<<8 | uint16(data[1])
 | |
| 		length := int(data[2])<<8 | int(data[3])
 | |
| 		data = data[4:]
 | |
| 		if len(data) < length {
 | |
| 			return false
 | |
| 		}
 | |
| 
 | |
| 		switch extension {
 | |
| 		case extensionServerName:
 | |
| 			d := data[:length]
 | |
| 			if len(d) < 2 {
 | |
| 				return false
 | |
| 			}
 | |
| 			namesLen := int(d[0])<<8 | int(d[1])
 | |
| 			d = d[2:]
 | |
| 			if len(d) != namesLen {
 | |
| 				return false
 | |
| 			}
 | |
| 			for len(d) > 0 {
 | |
| 				if len(d) < 3 {
 | |
| 					return false
 | |
| 				}
 | |
| 				nameType := d[0]
 | |
| 				nameLen := int(d[1])<<8 | int(d[2])
 | |
| 				d = d[3:]
 | |
| 				if len(d) < nameLen {
 | |
| 					return false
 | |
| 				}
 | |
| 				if nameType == 0 {
 | |
| 					m.serverName = string(d[:nameLen])
 | |
| 					// An SNI value may not include a
 | |
| 					// trailing dot. See
 | |
| 					// https://tools.ietf.org/html/rfc6066#section-3.
 | |
| 					if strings.HasSuffix(m.serverName, ".") {
 | |
| 						return false
 | |
| 					}
 | |
| 					break
 | |
| 				}
 | |
| 				d = d[nameLen:]
 | |
| 			}
 | |
| 		case extensionNextProtoNeg:
 | |
| 			if length > 0 {
 | |
| 				return false
 | |
| 			}
 | |
| 			m.nextProtoNeg = true
 | |
| 		case extensionStatusRequest:
 | |
| 			m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
 | |
| 		case extensionSupportedCurves:
 | |
| 			// https://tools.ietf.org/html/rfc4492#section-5.5.1
 | |
| 			if length < 2 {
 | |
| 				return false
 | |
| 			}
 | |
| 			l := int(data[0])<<8 | int(data[1])
 | |
| 			if l%2 == 1 || length != l+2 {
 | |
| 				return false
 | |
| 			}
 | |
| 			numCurves := l / 2
 | |
| 			m.supportedCurves = make([]CurveID, numCurves)
 | |
| 			d := data[2:]
 | |
| 			for i := 0; i < numCurves; i++ {
 | |
| 				m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
 | |
| 				d = d[2:]
 | |
| 			}
 | |
| 		case extensionSupportedPoints:
 | |
| 			// https://tools.ietf.org/html/rfc4492#section-5.5.2
 | |
| 			if length < 1 {
 | |
| 				return false
 | |
| 			}
 | |
| 			l := int(data[0])
 | |
| 			if length != l+1 {
 | |
| 				return false
 | |
| 			}
 | |
| 			m.supportedPoints = make([]uint8, l)
 | |
| 			copy(m.supportedPoints, data[1:])
 | |
| 		case extensionSessionTicket:
 | |
| 			// https://tools.ietf.org/html/rfc5077#section-3.2
 | |
| 			m.ticketSupported = true
 | |
| 			m.sessionTicket = data[:length]
 | |
| 		case extensionSignatureAlgorithms:
 | |
| 			// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
 | |
| 			if length < 2 || length&1 != 0 {
 | |
| 				return false
 | |
| 			}
 | |
| 			l := int(data[0])<<8 | int(data[1])
 | |
| 			if l != length-2 {
 | |
| 				return false
 | |
| 			}
 | |
| 			n := l / 2
 | |
| 			d := data[2:]
 | |
| 			m.supportedSignatureAlgorithms = make([]SignatureScheme, n)
 | |
| 			for i := range m.supportedSignatureAlgorithms {
 | |
| 				m.supportedSignatureAlgorithms[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1])
 | |
| 				d = d[2:]
 | |
| 			}
 | |
| 		case extensionRenegotiationInfo:
 | |
| 			if length == 0 {
 | |
| 				return false
 | |
| 			}
 | |
| 			d := data[:length]
 | |
| 			l := int(d[0])
 | |
| 			d = d[1:]
 | |
| 			if l != len(d) {
 | |
| 				return false
 | |
| 			}
 | |
| 
 | |
| 			m.secureRenegotiation = d
 | |
| 			m.secureRenegotiationSupported = true
 | |
| 		case extensionALPN:
 | |
| 			if length < 2 {
 | |
| 				return false
 | |
| 			}
 | |
| 			l := int(data[0])<<8 | int(data[1])
 | |
| 			if l != length-2 {
 | |
| 				return false
 | |
| 			}
 | |
| 			d := data[2:length]
 | |
| 			for len(d) != 0 {
 | |
| 				stringLen := int(d[0])
 | |
| 				d = d[1:]
 | |
| 				if stringLen == 0 || stringLen > len(d) {
 | |
| 					return false
 | |
| 				}
 | |
| 				m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen]))
 | |
| 				d = d[stringLen:]
 | |
| 			}
 | |
| 		case extensionSCT:
 | |
| 			m.scts = true
 | |
| 			if length != 0 {
 | |
| 				return false
 | |
| 			}
 | |
| 		}
 | |
| 		data = data[length:]
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | 
