packed.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. /*
  2. * Copyright (c) 2023, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package protocol
  20. import (
  21. "encoding"
  22. "encoding/base64"
  23. "encoding/hex"
  24. "encoding/json"
  25. "fmt"
  26. "strconv"
  27. "strings"
  28. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/accesscontrol"
  30. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  31. "github.com/fxamacker/cbor/v2"
  32. )
  33. // PackedAPIParameters is a compacted representation of common.APIParameters
  34. // using integer keys in place of string keys, and with some values
  35. // represented in compacted form, such as byte slices in place of hex or
  36. // base64 strings.
  37. //
  38. // The PackedAPIParameters representation is intended to be used to create
  39. // compacted, CBOR encodings of API parameters.
  40. type PackedAPIParameters map[int]interface{}
  41. // EncodePackedAPIParameters converts common.APIParameters to
  42. // PackedAPIParameters.
  43. func EncodePackedAPIParameters(params common.APIParameters) (PackedAPIParameters, error) {
  44. packedParams := PackedAPIParameters{}
  45. for name, value := range params {
  46. spec, ok := packedAPIParametersNameToSpec[name]
  47. if !ok {
  48. // The API parameter to be packed is not in
  49. // packedAPIParametersNameToSpec. This will occur if
  50. // packedAPIParametersNameToSpec is not updated when new API
  51. // parameters are added. Fail the operation and, ultimately, the
  52. // dial rather than proceeding without the parameter.
  53. return nil, errors.Tracef("unknown parameter name: %s", name)
  54. }
  55. if spec.converter != nil {
  56. var err error
  57. value, err = spec.converter.pack(value)
  58. if err != nil {
  59. return nil, errors.Tracef(
  60. "pack %s (%T) failed: %v", name, params[name], err)
  61. }
  62. }
  63. if _, ok := packedParams[spec.key]; ok {
  64. // This is a sanity check and shouldn't happen unless
  65. // packedAPIParametersNameToSpec is misconfigured.
  66. return nil, errors.TraceNew("duplicate parameter")
  67. }
  68. packedParams[spec.key] = value
  69. }
  70. return packedParams, nil
  71. }
  72. // DecodePackedAPIParameters converts PackedAPIParameters to
  73. // common.APIParameters
  74. func DecodePackedAPIParameters(packedParams PackedAPIParameters) (common.APIParameters, error) {
  75. params := common.APIParameters{}
  76. for key, value := range packedParams {
  77. spec, ok := packedAPIParametersKeyToSpec[key]
  78. if !ok {
  79. // The API parameter received is not in
  80. // packedAPIParametersNameToInt. Skip logging it and proceed.
  81. // This allows for production psiphond/broker instances to handle
  82. // experimental clients which ship new parameters, and matches
  83. // the legacy JSON-encoded API parameters behavior.
  84. continue
  85. }
  86. if spec.converter != nil {
  87. var err error
  88. value, err = spec.converter.unpack(value)
  89. if err != nil {
  90. return nil, errors.Tracef(
  91. "unpack %s (%T) failed: %v", spec.name, packedParams[key], err)
  92. }
  93. }
  94. if _, ok := params[spec.name]; ok {
  95. // This is a sanity check and shouldn't happen unless
  96. // packedAPIParametersKeyToSpec is misconfigured.
  97. return nil, errors.TraceNew("duplicate parameter")
  98. }
  99. params[spec.name] = value
  100. }
  101. return params, nil
  102. }
  103. // GetNetworkType returns the "network_type" API parameter value, if present.
  104. func (p PackedAPIParameters) GetNetworkType() (string, bool) {
  105. spec, ok := packedAPIParametersNameToSpec["network_type"]
  106. if !ok {
  107. return "", false
  108. }
  109. value, ok := p[spec.key]
  110. if !ok {
  111. return "", false
  112. }
  113. networkType, ok := value.(string)
  114. if !ok {
  115. return "", false
  116. }
  117. return networkType, true
  118. }
  119. // MakePackedAPIParametersRequestPayload converts common.APIParameters to
  120. // PackedAPIParameters and encodes the packed parameters as CBOR data.
  121. func MakePackedAPIParametersRequestPayload(
  122. params common.APIParameters) ([]byte, error) {
  123. packedParams, err := EncodePackedAPIParameters(params)
  124. if err != nil {
  125. return nil, errors.Trace(err)
  126. }
  127. payload, err := CBOREncoding.Marshal(packedParams)
  128. if err != nil {
  129. return nil, errors.Trace(err)
  130. }
  131. payload = addPackedAPIParametersPreamble(payload)
  132. return payload, nil
  133. }
  134. // GetPackedAPIParametersRequestPayload decodes the CBOR payload and converts
  135. // the PackedAPIParameters to common.APIParameters.
  136. //
  137. // GetPackedAPIParametersRequestPayload returns false and a nil error if the
  138. // input payload is not CBOR data, which is the case for legacy JSON
  139. // payloads.
  140. func GetPackedAPIParametersRequestPayload(
  141. payload []byte) (common.APIParameters, bool, error) {
  142. payload, ok := isPackedAPIParameters(payload)
  143. if !ok {
  144. return nil, false, nil
  145. }
  146. var packedParams PackedAPIParameters
  147. err := cbor.Unmarshal(payload, &packedParams)
  148. if err != nil {
  149. return nil, false, errors.Trace(err)
  150. }
  151. params, err := DecodePackedAPIParameters(packedParams)
  152. if err != nil {
  153. return nil, false, errors.Trace(err)
  154. }
  155. return params, true, nil
  156. }
  157. const (
  158. packedAPIParametersDistinguisher = byte(0)
  159. packedAPIParametersVersion = byte(1)
  160. )
  161. func addPackedAPIParametersPreamble(payload []byte) []byte {
  162. var preamble [2]byte
  163. // Use a simple 0 byte to distinguish payloads from JSON.
  164. preamble[0] = packedAPIParametersDistinguisher
  165. // Add a version tag, for future protocol changes.
  166. preamble[1] = packedAPIParametersVersion
  167. // Attempt to use the input buffer, which will avoid an allocation if it
  168. // has sufficient capacity.
  169. payload = append(payload, preamble[:]...)
  170. copy(payload[2:], payload[:len(payload)-2])
  171. copy(payload[0:2], preamble[:])
  172. return payload
  173. }
  174. func isPackedAPIParameters(payload []byte) ([]byte, bool) {
  175. if len(payload) < 2 {
  176. return nil, false
  177. }
  178. if payload[0] != packedAPIParametersDistinguisher {
  179. return nil, false
  180. }
  181. if payload[1] != packedAPIParametersVersion {
  182. return nil, false
  183. }
  184. return payload[2:], true
  185. }
  186. // PackedServerEntryFields is a compacted representation of ServerEntryFields
  187. // using integer keys in place of string keys, and with some values
  188. // represented in compacted form, such as byte slices in place of hex or
  189. // base64 strings.
  190. //
  191. // The PackedServerEntryFields representation is intended to be used in
  192. // CBOR-encoded messages, including in-proxy broker requests.
  193. //
  194. // To support older clients encoding signed server entries with new,
  195. // unrecognized fields, the encoded structure includes a list of packed
  196. // fields, Fields, and a list of raw, unpacked fields, UnrecognizedFields.
  197. type PackedServerEntryFields struct {
  198. Fields map[int]interface{} `cbor:"1,keyasint,omitempty"`
  199. UnrecognizedFields map[string]interface{} `cbor:"2,keyasint,omitempty"`
  200. }
  201. // EncodePackedServerEntryFields converts serverEntryFields to
  202. // PackedServerEntryFields.
  203. func EncodePackedServerEntryFields(
  204. serverEntryFields ServerEntryFields) (PackedServerEntryFields, error) {
  205. // An allocated but empty UnrecognizedFields should be omitted from any
  206. // CBOR encoding, taking no space.
  207. packedServerEntry := PackedServerEntryFields{
  208. Fields: make(map[int]interface{}),
  209. UnrecognizedFields: make(map[string]interface{}),
  210. }
  211. for name, value := range serverEntryFields {
  212. spec, ok := packedServerEntryFieldsNameToSpec[name]
  213. if !ok {
  214. // Add unrecognized fields to the unpacked UnrecognizedFields set.
  215. if _, ok := packedServerEntry.UnrecognizedFields[name]; ok {
  216. // This is a sanity check and shouldn't happen.
  217. return PackedServerEntryFields{}, errors.TraceNew("duplicate field")
  218. }
  219. packedServerEntry.UnrecognizedFields[name] = value
  220. continue
  221. }
  222. if spec.converter != nil {
  223. var err error
  224. value, err = spec.converter.pack(value)
  225. if err != nil {
  226. return PackedServerEntryFields{}, errors.Tracef(
  227. "pack %s (%T) failed: %v", name, serverEntryFields[name], err)
  228. }
  229. }
  230. if _, ok := packedServerEntry.Fields[spec.key]; ok {
  231. // This is a sanity check and shouldn't happen unless
  232. // packedServerEntryFieldsNameToSpec is misconfigured.
  233. return PackedServerEntryFields{}, errors.TraceNew("duplicate field")
  234. }
  235. packedServerEntry.Fields[spec.key] = value
  236. }
  237. return packedServerEntry, nil
  238. }
  239. // DecodePackedServerEntryFields converts PackedServerEntryFields to
  240. // ServerEntryFields.
  241. func DecodePackedServerEntryFields(
  242. packedServerEntryFields PackedServerEntryFields) (ServerEntryFields, error) {
  243. serverEntryFields := ServerEntryFields{}
  244. for key, value := range packedServerEntryFields.Fields {
  245. spec, ok := packedServerEntryFieldsKeyToSpec[key]
  246. if !ok {
  247. // Unlike DecodePackedAPIParameters, unknown fields cannot be
  248. // ignored as they may be part of the server entry digital
  249. // signature. Production psiphond/broker instances must be
  250. // updated to handle new server entry fields.
  251. return nil, errors.Tracef("unknown field key: %d", key)
  252. }
  253. if spec.converter != nil {
  254. var err error
  255. value, err = spec.converter.unpack(value)
  256. if err != nil {
  257. return nil, errors.Tracef(
  258. "unpack %s (%T) failed: %v",
  259. spec.name, packedServerEntryFields.Fields[key], err)
  260. }
  261. }
  262. if _, ok := serverEntryFields[spec.name]; ok {
  263. // This is a sanity check and shouldn't happen unless
  264. // packedServerEntryFieldsKeyToSpec is misconfigured.
  265. return nil, errors.TraceNew("duplicate field")
  266. }
  267. serverEntryFields[spec.name] = value
  268. }
  269. for name, value := range packedServerEntryFields.UnrecognizedFields {
  270. if _, ok := serverEntryFields[name]; ok {
  271. // This is a sanity check and shouldn't happen.
  272. return nil, errors.TraceNew("duplicate field")
  273. }
  274. serverEntryFields[name] = value
  275. }
  276. return serverEntryFields, nil
  277. }
  278. type packSpec struct {
  279. key int
  280. name string
  281. converter *packConverter
  282. }
  283. // packConverter defines an optional pack/unpack transformation to further
  284. // reduce encoding overhead. For example, fields that are expected to be hex
  285. // strings may be converted to byte slices, and then back again; integer
  286. // strings are converted to actual integers; etc..
  287. type packConverter struct {
  288. pack func(interface{}) (interface{}, error)
  289. unpack func(interface{}) (interface{}, error)
  290. }
  291. func packInt(v interface{}) (interface{}, error) {
  292. switch value := v.(type) {
  293. case string:
  294. i, err := strconv.Atoi(value)
  295. if err != nil {
  296. return nil, errors.Trace(err)
  297. }
  298. return i, nil
  299. case float64:
  300. // Decoding server entry JSON from the local datastore may produce
  301. // float64 field types.
  302. return int(value), nil
  303. default:
  304. return nil, errors.TraceNew(
  305. "expected string or float type")
  306. }
  307. }
  308. func unpackInt(v interface{}) (interface{}, error) {
  309. switch i := v.(type) {
  310. case int:
  311. return strconv.FormatInt(int64(i), 10), nil
  312. case int64:
  313. return strconv.FormatInt(i, 10), nil
  314. case uint64:
  315. return strconv.FormatUint(i, 10), nil
  316. default:
  317. return "", errors.TraceNew(
  318. "expected int, int64, or uint64 type")
  319. }
  320. }
  321. func packFloat(v interface{}) (interface{}, error) {
  322. switch value := v.(type) {
  323. case string:
  324. i, err := strconv.ParseFloat(value, 64)
  325. if err != nil {
  326. return nil, errors.Trace(err)
  327. }
  328. return i, nil
  329. case float64:
  330. return value, nil
  331. default:
  332. return nil, errors.TraceNew(
  333. "expected string or float type")
  334. }
  335. }
  336. func unpackFloat(v interface{}) (interface{}, error) {
  337. f, ok := v.(float64)
  338. if !ok {
  339. return "", errors.TraceNew("expected int type")
  340. }
  341. return fmt.Sprintf("%f", f), nil
  342. }
  343. func packHex(v interface{}) (interface{}, error) {
  344. // Accept a type that is either a string, or implements MarshalText
  345. // returning a string. The resulting string must be hex encoded.
  346. s, err := stringOrTextMarshal(v)
  347. if err != nil {
  348. return nil, errors.Trace(err)
  349. }
  350. b, err := hex.DecodeString(s)
  351. if err != nil {
  352. return nil, errors.Trace(err)
  353. }
  354. return b, nil
  355. }
  356. func unpackHexLower(v interface{}) (interface{}, error) {
  357. b, ok := v.([]byte)
  358. if !ok {
  359. return "", errors.TraceNew("expected []byte type")
  360. }
  361. return hex.EncodeToString(b), nil
  362. }
  363. func unpackHexUpper(v interface{}) (interface{}, error) {
  364. s, err := unpackHexLower(v)
  365. if err != nil {
  366. return "", errors.Trace(err)
  367. }
  368. return strings.ToUpper(s.(string)), nil
  369. }
  370. func packBase64(v interface{}) (interface{}, error) {
  371. // Accept a type that is either a string, or implements MarshalText
  372. // returning a string. The resulting string must be base64 encoded.
  373. s, err := stringOrTextMarshal(v)
  374. if err != nil {
  375. return nil, errors.Trace(err)
  376. }
  377. b, err := base64.StdEncoding.DecodeString(s)
  378. if err != nil {
  379. return nil, errors.Trace(err)
  380. }
  381. return b, nil
  382. }
  383. func unpackBase64(v interface{}) (interface{}, error) {
  384. b, ok := v.([]byte)
  385. if !ok {
  386. return "", errors.TraceNew("expected []byte type")
  387. }
  388. return base64.StdEncoding.EncodeToString(b), nil
  389. }
  390. func packUnpaddedBase64(v interface{}) (interface{}, error) {
  391. // Accept a type that is either a string, or implements MarshalText
  392. // returning a string. The resulting string must be base64 encoded
  393. // (unpadded).
  394. s, err := stringOrTextMarshal(v)
  395. if err != nil {
  396. return nil, errors.Trace(err)
  397. }
  398. b, err := base64.RawStdEncoding.DecodeString(s)
  399. if err != nil {
  400. return nil, errors.Trace(err)
  401. }
  402. return b, nil
  403. }
  404. func unpackUnpaddedBase64(v interface{}) (interface{}, error) {
  405. b, ok := v.([]byte)
  406. if !ok {
  407. return "", errors.TraceNew("expected []byte type")
  408. }
  409. return base64.RawStdEncoding.EncodeToString(b), nil
  410. }
  411. func packAuthorizations(v interface{}) (interface{}, error) {
  412. auths, ok := v.([]string)
  413. if !ok {
  414. return "", errors.TraceNew("expected []string type")
  415. }
  416. packedAuths, err := accesscontrol.PackAuthorizations(auths, CBOREncoding)
  417. if err != nil {
  418. return nil, errors.Trace(err)
  419. }
  420. return packedAuths, nil
  421. }
  422. func unpackAuthorizations(v interface{}) (interface{}, error) {
  423. packedAuths, ok := v.([]byte)
  424. if !ok {
  425. return nil, errors.TraceNew("expected []byte type")
  426. }
  427. auths, err := accesscontrol.UnpackAuthorizations(packedAuths)
  428. if err != nil {
  429. return nil, errors.Trace(err)
  430. }
  431. return auths, nil
  432. }
  433. func packNoop(v interface{}) (interface{}, error) {
  434. return v, nil
  435. }
  436. func unpackRawJSON(v interface{}) (interface{}, error) {
  437. // For compatibility with the legacy JSON encoding as used in the status
  438. // API request payload, where the input is pre-JSON-marshaling
  439. // json.RawMessage (so use packNoop) and the output is expected to be an
  440. // unmarshaled JSON decoded object; e.g., map[string]interface{}.
  441. packedRawJSON, ok := v.([]byte)
  442. if !ok {
  443. return nil, errors.TraceNew("expected []byte type")
  444. }
  445. var unmarshaledJSON map[string]interface{}
  446. err := json.Unmarshal(packedRawJSON, &unmarshaledJSON)
  447. if err != nil {
  448. return nil, errors.Trace(err)
  449. }
  450. return unmarshaledJSON, nil
  451. }
  452. func stringOrTextMarshal(v interface{}) (string, error) {
  453. switch value := v.(type) {
  454. case string:
  455. return value, nil
  456. case encoding.TextMarshaler:
  457. bytes, err := value.MarshalText()
  458. if err != nil {
  459. return "", errors.Trace(err)
  460. }
  461. return string(bytes), nil
  462. default:
  463. return "", errors.TraceNew(
  464. "expected string or TextMarshaler type")
  465. }
  466. }
  467. var (
  468. // All of the following variables should be read-only after
  469. // initialization, due to concurrent access.
  470. packedAPIParametersNameToSpec = make(map[string]packSpec)
  471. packedAPIParametersKeyToSpec = make(map[int]packSpec)
  472. packedServerEntryFieldsNameToSpec = make(map[string]packSpec)
  473. packedServerEntryFieldsKeyToSpec = make(map[int]packSpec)
  474. intConverter = &packConverter{packInt, unpackInt}
  475. floatConverter = &packConverter{packFloat, unpackFloat}
  476. lowerHexConverter = &packConverter{packHex, unpackHexLower}
  477. upperHexConverter = &packConverter{packHex, unpackHexUpper}
  478. base64Converter = &packConverter{packBase64, unpackBase64}
  479. unpaddedBase64Converter = &packConverter{packUnpaddedBase64, unpackUnpaddedBase64}
  480. authorizationsConverter = &packConverter{packAuthorizations, unpackAuthorizations}
  481. rawJSONConverter = &packConverter{packNoop, unpackRawJSON}
  482. )
  483. func init() {
  484. // Packed API parameters
  485. //
  486. // - must be appended to when server entry fields are added; existing key
  487. // values cannot be reordered or reused.
  488. //
  489. // - limitation: use of converters means secrets/passwords/IDs are locked
  490. // in as upper or lower hex with even digits, etc.
  491. //
  492. // - while not currently the case, if different API requests have the same
  493. // input field name with different types, the nil converter must be used.
  494. packedAPIParameterSpecs := []packSpec{
  495. // Specs: protocol.PSIPHON_API_HANDSHAKE_AUTHORIZATIONS
  496. {1, "authorizations", authorizationsConverter},
  497. // Specs:
  498. // tactics.SPEED_TEST_SAMPLES_PARAMETER_NAME
  499. // tactics.APPLIED_TACTICS_TAG_PARAMETER_NAME
  500. // tactics.STORED_TACTICS_TAG_PARAMETER_NAME
  501. {2, "stored_tactics_tag", lowerHexConverter},
  502. {3, "speed_test_samples", nil},
  503. {4, "applied_tactics_tag", lowerHexConverter},
  504. // Specs: server.baseParams
  505. //
  506. // - client_build_rev does not use a hex converter since some values
  507. // are a non-even length prefix of a commit hash hex.
  508. {5, "client_session_id", lowerHexConverter},
  509. {6, "propagation_channel_id", upperHexConverter},
  510. {7, "sponsor_id", upperHexConverter},
  511. {8, "client_version", intConverter},
  512. {9, "client_platform", nil},
  513. {10, "client_features", nil},
  514. {11, "client_build_rev", nil},
  515. {12, "device_region", nil},
  516. {13, "device_location", nil},
  517. // Specs: server.baseSessionParams
  518. {14, "session_id", lowerHexConverter},
  519. // Specs: server.baseDialParams
  520. //
  521. // - intConverter is used for boolean fields as those are "0"/"1"
  522. // string values by legacy convention.
  523. //
  524. // - the `padding` field is not packed since it is intended to pad the
  525. // encoded message to its existing size.
  526. {15, "relay_protocol", nil},
  527. {16, "ssh_client_version", nil},
  528. {17, "upstream_proxy_type", nil},
  529. {18, "upstream_proxy_custom_header_names", nil},
  530. {19, "fronting_provider_id", upperHexConverter},
  531. {20, "meek_dial_address", nil},
  532. {21, "meek_resolved_ip_address", nil},
  533. {22, "meek_sni_server_name", nil},
  534. {23, "meek_host_header", nil},
  535. {24, "meek_transformed_host_name", intConverter},
  536. {25, "user_agent", nil},
  537. {26, "tls_profile", nil},
  538. {27, "tls_version", nil},
  539. {28, "server_entry_region", nil},
  540. {29, "server_entry_source", nil},
  541. {30, "server_entry_timestamp", nil},
  542. {31, "dial_port_number", intConverter},
  543. {32, "quic_version", nil},
  544. {33, "quic_dial_sni_address", nil},
  545. {34, "quic_disable_client_path_mtu_discovery", intConverter},
  546. {35, "upstream_bytes_fragmented", intConverter},
  547. {36, "upstream_min_bytes_written", intConverter},
  548. {37, "upstream_max_bytes_written", intConverter},
  549. {38, "upstream_min_delayed", intConverter},
  550. {39, "upstream_max_delayed", intConverter},
  551. {40, "padding", nil},
  552. {41, "pad_response", intConverter},
  553. {42, "is_replay", intConverter},
  554. {43, "egress_region", nil},
  555. {44, "dial_duration", intConverter},
  556. {45, "candidate_number", intConverter},
  557. {46, "established_tunnels_count", intConverter},
  558. {47, "upstream_ossh_padding", intConverter},
  559. {48, "meek_cookie_size", intConverter},
  560. {49, "meek_limit_request", intConverter},
  561. {50, "meek_redial_probability", floatConverter},
  562. {51, "meek_tls_padding", intConverter},
  563. {52, "network_latency_multiplier", floatConverter},
  564. {53, "client_bpf", nil},
  565. {54, "network_type", nil},
  566. {55, "conjure_cached", nil},
  567. {56, "conjure_delay", nil},
  568. {57, "conjure_transport", nil},
  569. {58, "conjure_prefix", nil},
  570. {59, "conjure_stun", nil},
  571. {60, "conjure_empty_packet", intConverter},
  572. {61, "conjure_network", nil},
  573. {62, "conjure_port_number", intConverter},
  574. {63, "split_tunnel", nil},
  575. {64, "split_tunnel_regions", nil},
  576. {65, "dns_preresolved", nil},
  577. {66, "dns_preferred", nil},
  578. {67, "dns_transform", nil},
  579. {68, "dns_attempt", intConverter},
  580. {69, "http_transform", nil},
  581. {70, "seed_transform", nil},
  582. {71, "ossh_prefix", nil},
  583. {72, "tls_fragmented", intConverter},
  584. {73, "tls_padding", intConverter},
  585. {74, "tls_ossh_sni_server_name", nil},
  586. {75, "tls_ossh_transformed_host_name", intConverter},
  587. // Specs: server.inproxyDialParams
  588. {76, "inproxy_connection_id", unpaddedBase64Converter},
  589. {77, "inproxy_relay_packet", unpaddedBase64Converter},
  590. {78, "inproxy_broker_is_replay", intConverter},
  591. {79, "inproxy_broker_transport", nil},
  592. {80, "inproxy_broker_fronting_provider_id", upperHexConverter},
  593. {81, "inproxy_broker_dial_address", nil},
  594. {82, "inproxy_broker_resolved_ip_address", nil},
  595. {83, "inproxy_broker_sni_server_name", nil},
  596. {84, "inproxy_broker_host_header", nil},
  597. {85, "inproxy_broker_transformed_host_name", intConverter},
  598. {86, "inproxy_broker_user_agent", nil},
  599. {87, "inproxy_broker_tls_profile", nil},
  600. {88, "inproxy_broker_tls_version", nil},
  601. {89, "inproxy_broker_tls_fragmented", intConverter},
  602. {90, "inproxy_broker_tls_padding", intConverter},
  603. {91, "inproxy_broker_client_bpf", nil},
  604. {92, "inproxy_broker_upstream_bytes_fragmented", intConverter},
  605. {93, "inproxy_broker_upstream_min_bytes_written", intConverter},
  606. {94, "inproxy_broker_upstream_max_bytes_written", intConverter},
  607. {95, "inproxy_broker_upstream_min_delayed", intConverter},
  608. {96, "inproxy_broker_upstream_max_delayed", intConverter},
  609. {97, "inproxy_broker_http_transform", nil},
  610. {98, "inproxy_broker_dns_preresolved", nil},
  611. {99, "inproxy_broker_dns_preferred", nil},
  612. {100, "inproxy_broker_dns_transform", nil},
  613. {101, "inproxy_broker_dns_attempt", intConverter},
  614. {102, "inproxy_webrtc_dns_preresolved", nil},
  615. {103, "inproxy_webrtc_dns_preferred", nil},
  616. {104, "inproxy_webrtc_dns_transform", nil},
  617. {105, "inproxy_webrtc_dns_attempt", intConverter},
  618. {106, "inproxy_webrtc_stun_server", nil},
  619. {107, "inproxy_webrtc_stun_server_resolved_ip_address", nil},
  620. {108, "inproxy_webrtc_stun_server_RFC5780", nil},
  621. {109, "inproxy_webrtc_stun_server_RFC5780_resolved_ip_address", nil},
  622. {110, "inproxy_webrtc_randomize_dtls", intConverter},
  623. {111, "inproxy_webrtc_padded_messages_sent", intConverter},
  624. {112, "inproxy_webrtc_padded_messages_received", intConverter},
  625. {113, "inproxy_webrtc_decoy_messages_sent", intConverter},
  626. {114, "inproxy_webrtc_decoy_messages_received", intConverter},
  627. // Specs: server.handshakeRequestParams
  628. {115, "missing_server_entry_signature", base64Converter},
  629. {116, "missing_server_entry_provider_id", base64Converter},
  630. // Specs: server.uniqueUserParams
  631. //
  632. // - future enhancement: add a timestamp converter from RFC3339 to and
  633. // from 64-bit Unix time?
  634. {117, "last_connected", nil},
  635. // Specs: server.connectedRequestParams
  636. {118, "establishment_duration", intConverter},
  637. // Specs: server.remoteServerListStatParams
  638. {119, "client_download_timestamp", nil},
  639. {120, "tunneled", intConverter},
  640. {121, "url", nil},
  641. {122, "etag", nil},
  642. {123, "bytes", intConverter},
  643. {124, "duration", intConverter},
  644. // Specs: server.failedTunnelStatParams
  645. //
  646. // - given CBOR integer encoding, int key values greater than 128 may
  647. // be a byte longer; this means some failed_tunnel required field
  648. // key encodings may be longer than some optional handshake field
  649. // key encodings; however, we prioritize reducing the handshake
  650. // size, since it comes earlier in the tunnel flow.
  651. {125, "server_entry_tag", base64Converter},
  652. {126, "client_failed_timestamp", nil},
  653. {127, "record_probability", floatConverter},
  654. {128, "liveness_test_upstream_bytes", intConverter},
  655. {129, "liveness_test_sent_upstream_bytes", intConverter},
  656. {130, "liveness_test_downstream_bytes", intConverter},
  657. {131, "liveness_test_received_downstream_bytes", intConverter},
  658. {132, "bytes_up", intConverter},
  659. {133, "bytes_down", intConverter},
  660. {134, "tunnel_error", nil},
  661. // Specs: status request payload
  662. //
  663. // - future enhancement: pack the statusData payload, which is
  664. // currently sent as unpacked JSON.
  665. {135, "statusData", rawJSONConverter},
  666. }
  667. for _, spec := range packedAPIParameterSpecs {
  668. if _, ok := packedAPIParametersNameToSpec[spec.name]; ok {
  669. panic("duplicate parameter name")
  670. }
  671. packedAPIParametersNameToSpec[spec.name] = spec
  672. if _, ok := packedAPIParametersKeyToSpec[spec.key]; ok {
  673. panic("duplicate parameter key")
  674. }
  675. packedAPIParametersKeyToSpec[spec.key] = spec
  676. }
  677. // Packed server entry fields
  678. //
  679. // - must be appended to when server entry fields are added; existing key
  680. // values cannot be reordered or reused.
  681. //
  682. // - limitation: use of converters means secrets/passwords/IDs are locked
  683. // in as upper or lower hex with even digits, etc.
  684. //
  685. // - since webServerCertificate is omitted in non-legacy server entries,
  686. // no PEM-encoding packer is implemented.
  687. //
  688. // - unlike API integer parameters and certain server entry fields, most
  689. // port values are already int types and so not converted.
  690. //
  691. // - local-only fields are also packed, to allow for future use of packed
  692. // encodings in the local datastore.
  693. packedServerEntryFieldSpecs := []packSpec{
  694. {1, "tag", base64Converter},
  695. {2, "ipAddress", nil},
  696. {3, "webServerPort", intConverter},
  697. {4, "webServerSecret", lowerHexConverter},
  698. {5, "webServerCertificate", nil},
  699. {6, "sshPort", nil},
  700. {7, "sshUsername", nil},
  701. {8, "sshPassword", lowerHexConverter},
  702. {9, "sshHostKey", unpaddedBase64Converter},
  703. {10, "sshObfuscatedPort", nil},
  704. {11, "sshObfuscatedQUICPort", nil},
  705. {12, "limitQUICVersions", nil},
  706. {13, "sshObfuscatedTapdancePort", nil},
  707. {14, "sshObfuscatedConjurePort", nil},
  708. {15, "sshObfuscatedKey", lowerHexConverter},
  709. {16, "capabilities", nil},
  710. {17, "region", nil},
  711. {18, "providerID", upperHexConverter},
  712. {19, "frontingProviderID", upperHexConverter},
  713. {20, "tlsOSSHPort", nil},
  714. {21, "meekServerPort", nil},
  715. {22, "meekCookieEncryptionPublicKey", base64Converter},
  716. {23, "meekObfuscatedKey", lowerHexConverter},
  717. {24, "meekFrontingHost", nil},
  718. {25, "meekFrontingHosts", nil},
  719. {26, "meekFrontingDomain", nil},
  720. {27, "meekFrontingAddresses", nil},
  721. {28, "meekFrontingAddressesRegex", nil},
  722. {29, "meekFrontingDisableSNI", intConverter},
  723. {30, "tacticsRequestPublicKey", base64Converter},
  724. {31, "tacticsRequestObfuscatedKey", base64Converter},
  725. {32, "configurationVersion", nil},
  726. {33, "signature", base64Converter},
  727. {34, "disableHTTPTransforms", intConverter},
  728. {35, "disableObfuscatedQUICTransforms", intConverter},
  729. {36, "disableOSSHTransforms", intConverter},
  730. {37, "disableOSSHPrefix", intConverter},
  731. {38, "inproxySessionPublicKey", unpaddedBase64Converter},
  732. {39, "inproxySessionRootObfuscationSecret", unpaddedBase64Converter},
  733. {40, "inproxySSHPort", nil},
  734. {41, "inproxyOSSHPort", nil},
  735. {42, "inproxyQUICPort", nil},
  736. {43, "inproxyMeekPort", nil},
  737. {44, "inproxyTlsOSSHPort", nil},
  738. {45, "localSource", nil},
  739. {46, "localTimestamp", nil},
  740. {47, "isLocalDerivedTag", intConverter},
  741. }
  742. for _, spec := range packedServerEntryFieldSpecs {
  743. if _, ok := packedServerEntryFieldsNameToSpec[spec.name]; ok {
  744. panic("duplicate field name")
  745. }
  746. packedServerEntryFieldsNameToSpec[spec.name] = spec
  747. if _, ok := packedServerEntryFieldsKeyToSpec[spec.key]; ok {
  748. panic("duplicate field key")
  749. }
  750. packedServerEntryFieldsKeyToSpec[spec.key] = spec
  751. }
  752. }