BuildClientHelloPadded returns a ClientHello whose total size is OutputLen + extraPad bytes, where extraPad ∈ [0, 65000]. The extension is byte-identical to BuildClientHello when extraPad == 0. Extra bytes are appended to the padding extension. The record and handshake length fields are recomputed
(rnd, sessID, sni, keyShare []byte, extraPad int)
| 116 | // a DPI that looked for "exact length 517 during handshake-ACK window" |
| 117 | // cannot match a variable-length stream. |
| 118 | func BuildClientHelloPadded(rnd, sessID, sni, keyShare []byte, extraPad int) ([]byte, error) { |
| 119 | if extraPad < 0 || extraPad > 65000 { |
| 120 | return nil, ErrInvalidInput |
| 121 | } |
| 122 | base, err := BuildClientHello(rnd, sessID, sni, keyShare) |
| 123 | if err != nil { |
| 124 | return nil, err |
| 125 | } |
| 126 | if extraPad == 0 { |
| 127 | return base, nil |
| 128 | } |
| 129 | |
| 130 | out := make([]byte, 0, len(base)+extraPad) |
| 131 | out = append(out, base...) |
| 132 | out = append(out, make([]byte, extraPad)...) |
| 133 | |
| 134 | // Fix up the three length fields that covered the inner structures. |
| 135 | // Record header bytes 3-4: TLS record body length (handshake data only, |
| 136 | // excludes the 5-byte record header itself). |
| 137 | recBodyLen := uint16(len(out) - 5) |
| 138 | out[3] = byte(recBodyLen >> 8) |
| 139 | out[4] = byte(recBodyLen) |
| 140 | // Handshake header bytes 6-8: uint24 handshake body length (excludes the |
| 141 | // 4-byte handshake header itself). |
| 142 | hsBodyLen := len(out) - 9 |
| 143 | out[6] = byte(hsBodyLen >> 16) |
| 144 | out[7] = byte(hsBodyLen >> 8) |
| 145 | out[8] = byte(hsBodyLen) |
| 146 | // Padding extension is the last extension. Its 2-byte length field sits |
| 147 | // immediately after static5 (bytes [len(base)-2-padN : len(base)-padN]). |
| 148 | // Simpler: extension length = (old padding size) + extraPad. Old padding |
| 149 | // size is at bytes [len(base)-2-oldPad : len(base)-oldPad]. oldPad = |
| 150 | // (paddingBudget - len(sni)). |
| 151 | oldPad := paddingBudget - len(sni) |
| 152 | padLenOff := len(base) - 2 - oldPad |
| 153 | newPadBody := oldPad + extraPad |
| 154 | out[padLenOff] = byte(newPadBody >> 8) |
| 155 | out[padLenOff+1] = byte(newPadBody) |
| 156 | |
| 157 | return out, nil |
| 158 | } |
| 159 | |
| 160 | func mustHex(s string) []byte { |
| 161 | b, err := hex.DecodeString(s) |