@nictool/dns-resource-record 1.6.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +356 -199
  3. package/dist/dns-rr.cjs +7050 -0
  4. package/dist/dns-rr.min.js +26 -0
  5. package/dist/dns-rr.min.js.map +7 -0
  6. package/index.js +12 -11
  7. package/lib/binary.js +108 -0
  8. package/lib/bind.js +96 -0
  9. package/lib/dns-query.js +67 -0
  10. package/lib/tinydns.js +128 -44
  11. package/lib/wire.js +629 -0
  12. package/package.json +28 -8
  13. package/rr/TEMPLATE.js +124 -0
  14. package/rr/a.js +8 -48
  15. package/rr/aaaa.js +13 -80
  16. package/rr/apl.js +123 -27
  17. package/rr/caa.js +27 -50
  18. package/rr/cert.js +60 -81
  19. package/rr/cname.js +8 -59
  20. package/rr/dhcid.js +20 -30
  21. package/rr/dname.js +10 -32
  22. package/rr/dnskey.js +38 -32
  23. package/rr/ds.js +59 -54
  24. package/rr/hinfo.js +21 -40
  25. package/rr/hip.js +88 -26
  26. package/rr/https.js +28 -40
  27. package/rr/ipseckey.js +97 -18
  28. package/rr/key.js +39 -29
  29. package/rr/kx.js +16 -27
  30. package/rr/loc.js +42 -12
  31. package/rr/mx.js +17 -48
  32. package/rr/naptr.js +58 -33
  33. package/rr/ns.js +8 -36
  34. package/rr/nsec.js +89 -18
  35. package/rr/nsec3.js +62 -26
  36. package/rr/nsec3param.js +56 -53
  37. package/rr/nxt.js +57 -18
  38. package/rr/openpgpkey.js +24 -30
  39. package/rr/ptr.js +7 -47
  40. package/rr/rp.js +17 -32
  41. package/rr/rrsig.js +108 -75
  42. package/rr/sig.js +70 -45
  43. package/rr/smimea.js +33 -33
  44. package/rr/soa.js +31 -49
  45. package/rr/spf.js +15 -18
  46. package/rr/srv.js +28 -51
  47. package/rr/sshfp.js +35 -48
  48. package/rr/svcb.js +27 -40
  49. package/rr/tlsa.js +34 -34
  50. package/rr/tsig.js +87 -24
  51. package/rr/txt.js +77 -68
  52. package/rr/uri.js +19 -29
  53. package/rr/wks.js +154 -22
  54. package/rr.js +290 -109
  55. package/lib/readme.js +0 -84
package/rr/cert.js CHANGED
@@ -1,8 +1,34 @@
1
1
  import RR from '../rr.js'
2
2
 
3
3
  import * as TINYDNS from '../lib/tinydns.js'
4
+ import * as BINARY from '../lib/binary.js'
4
5
 
5
6
  export default class CERT extends RR {
7
+ static typeName = 'CERT'
8
+ static typeId = 37
9
+ static RFCs = [2538, 4398]
10
+ static rdataFields = [
11
+ ['cert type', 'certtype'],
12
+ ['key tag', 'u16'],
13
+ ['algorithm', 'u8'],
14
+ ['certificate', 'base64'],
15
+ ]
16
+
17
+ static CERT_TYPES = {
18
+ PKIX: 1,
19
+ SPKI: 2,
20
+ PGP: 3,
21
+ IPKIX: 4,
22
+ ISPKI: 5,
23
+ IPGP: 6,
24
+ ACPKIX: 7,
25
+ IACPKIX: 8,
26
+ URI: 253,
27
+ OID: 254,
28
+ }
29
+
30
+ static CERT_TYPES_REVERSE = Object.fromEntries(Object.entries(CERT.CERT_TYPES).map(([k, v]) => [v, k]))
31
+
6
32
  constructor(opts) {
7
33
  super(opts)
8
34
  }
@@ -15,20 +41,8 @@ export default class CERT extends RR {
15
41
  this.throwHelp('cert type is required')
16
42
  }
17
43
  // Accept both mnemonic and numeric, but validate mnemonic
18
- if (typeof val === 'string') {
19
- const types = {
20
- PKIX: 1,
21
- SPKI: 2,
22
- PGP: 3,
23
- IPKIX: 4,
24
- ISPKI: 5,
25
- IPGP: 6,
26
- ACPKIX: 7,
27
- IACPKIX: 8,
28
- URI: 253,
29
- OID: 254,
30
- }
31
- if (!Object.hasOwn(types, val)) {
44
+ if (typeof val === 'string' && !/^[0-9]+$/.test(val)) {
45
+ if (!Object.hasOwn(CERT.CERT_TYPES, val)) {
32
46
  this.throwHelp(`CERT: unknown cert type mnemonic: ${val}`)
33
47
  }
34
48
  } else {
@@ -39,37 +53,17 @@ export default class CERT extends RR {
39
53
 
40
54
  getCertTypeValue(val) {
41
55
  if (typeof val === 'number') return val
42
- const types = {
43
- PKIX: 1,
44
- SPKI: 2,
45
- PGP: 3,
46
- IPKIX: 4,
47
- ISPKI: 5,
48
- IPGP: 6,
49
- ACPKIX: 7,
50
- IACPKIX: 8,
51
- URI: 253,
52
- OID: 254,
53
- }
54
- if (Object.hasOwn(types, val)) return types[val]
56
+ if (/^[0-9]+$/.test(val)) return parseInt(val, 10)
57
+ if (Object.hasOwn(CERT.CERT_TYPES, val)) return CERT.CERT_TYPES[val]
55
58
  this.throwHelp(`CERT: unknown cert type mnemonic: ${val}`)
56
59
  }
57
60
 
58
61
  setKeyTag(val) {
59
- // The key tag field is the 16-bit value
60
- // The key tag field is represented as an unsigned decimal integer.
61
-
62
- this.is16bitInt('CERT', 'key tag', val)
63
-
64
- this.set('key tag', val)
62
+ this.setTypedValue('u16', 'key tag', val)
65
63
  }
66
64
 
67
65
  setAlgorithm(val) {
68
- // The algorithm field has the same meaning as the algorithm field in DNSKEY
69
- // The algorithm field is represented as an unsigned decimal integer
70
- this.is8bitInt('CERT', 'algorithm', val)
71
-
72
- this.set('algorithm', val)
66
+ this.setTypedValue('u8', 'algorithm', val)
73
67
  }
74
68
 
75
69
  setCertificate(val) {
@@ -78,6 +72,7 @@ export default class CERT extends RR {
78
72
  if (val === undefined || val === null || val === '') {
79
73
  this.throwHelp('certificate is required and cannot be empty')
80
74
  }
75
+ this.isBase64('CERT', 'certificate', val.replace(/[\s()]/g, ''))
81
76
  this.set('certificate', val)
82
77
  }
83
78
 
@@ -85,18 +80,6 @@ export default class CERT extends RR {
85
80
  return 'Certificate'
86
81
  }
87
82
 
88
- getRdataFields() {
89
- return ['cert type', 'key tag', 'algorithm', 'certificate']
90
- }
91
-
92
- getRFCs() {
93
- return [2538, 4398]
94
- }
95
-
96
- getTypeId() {
97
- return 37
98
- }
99
-
100
83
  getCanonical() {
101
84
  return {
102
85
  owner: 'mail.example.com.',
@@ -106,44 +89,31 @@ export default class CERT extends RR {
106
89
  'cert type': 'PGP',
107
90
  'key tag': 0,
108
91
  algorithm: 0,
109
- certificate: 'hexidecimalkeystring1',
92
+ certificate: 'AQIDBA==',
110
93
  }
111
94
  }
112
95
 
113
96
  /****** IMPORTERS *******/
114
97
 
115
98
  fromTinydns({ tinyline }) {
116
- const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
117
- if (n != 37) this.throwHelp('CERT fromTinydns, invalid n')
118
-
119
- const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
120
- const typeNum = bytes.readUInt16BE(0)
121
- let certType = typeNum
122
-
123
- const types = {
124
- 1: 'PKIX',
125
- 2: 'SPKI',
126
- 3: 'PGP',
127
- 4: 'IPKIX',
128
- 5: 'ISPKI',
129
- 6: 'IPGP',
130
- 7: 'ACPKIX',
131
- 8: 'IACPKIX',
132
- 253: 'URI',
133
- 254: 'OID',
134
- }
135
- if (types[typeNum]) certType = types[typeNum]
99
+ const { owner, typeId, rdata, ttl, timestamp, location } = this.parseTinydnsLine(tinyline)
100
+ if (typeId != this.getTypeId()) this.throwHelp('CERT fromTinydns, invalid n')
101
+
102
+ const bytes = TINYDNS.octalRdataToBytes(rdata)
103
+ const typeNum = (bytes[0] << 8) | bytes[1]
104
+
105
+ const certType = CERT.CERT_TYPES_REVERSE[typeNum] ?? typeNum
136
106
 
137
107
  return new CERT({
138
- owner: this.fullyQualify(owner),
139
- ttl: parseInt(ttl, 10),
108
+ owner,
109
+ ttl,
140
110
  type: 'CERT',
141
111
  'cert type': certType,
142
- 'key tag': bytes.readUInt16BE(2),
143
- algorithm: bytes.readUInt8(4),
144
- certificate: bytes.slice(5).toString(),
145
- timestamp: ts,
146
- location: loc?.trim() ?? '',
112
+ 'key tag': (bytes[2] << 8) | bytes[3],
113
+ algorithm: bytes[4],
114
+ certificate: BINARY.bytesToBase64(bytes.subarray(5)),
115
+ timestamp,
116
+ location,
147
117
  })
148
118
  }
149
119
 
@@ -165,13 +135,22 @@ export default class CERT extends RR {
165
135
  /****** EXPORTERS *******/
166
136
 
167
137
  toTinydns() {
168
- const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
169
-
170
138
  return this.getTinydnsGeneric(
171
139
  TINYDNS.UInt16toOctal(this.getCertTypeValue(this.get('cert type'))) +
172
140
  TINYDNS.UInt16toOctal(this.get('key tag')) +
173
141
  TINYDNS.UInt8toOctal(this.get('algorithm')) +
174
- TINYDNS.escapeOctal(dataRe, this.get('certificate')),
142
+ TINYDNS.base64toOctal(this.get('certificate').replace(/[\s()]/g, '')),
175
143
  )
176
144
  }
145
+
146
+ getWireRdata() {
147
+ const certBytes = BINARY.base64ToBytes(this.get('certificate').replace(/[\s()]/g, ''))
148
+ const bytes = new Uint8Array(5 + certBytes.length)
149
+ const dv = new DataView(bytes.buffer, bytes.byteOffset)
150
+ dv.setUint16(0, this.getCertTypeValue(this.get('cert type')))
151
+ dv.setUint16(2, this.get('key tag'))
152
+ bytes[4] = this.get('algorithm')
153
+ bytes.set(certBytes, 5)
154
+ return bytes
155
+ }
177
156
  }
package/rr/cname.js CHANGED
@@ -1,46 +1,26 @@
1
1
  import RR from '../rr.js'
2
2
 
3
3
  export default class CNAME extends RR {
4
+ static typeName = 'CNAME'
5
+ static typeId = 5
6
+ static RFCs = [1035, 2181]
7
+ static tinydnsType = 'C'
8
+ static rdataFields = [['cname', 'fqdn']]
9
+ static tags = ['common']
10
+
4
11
  constructor(opts) {
5
12
  super(opts)
6
13
  }
7
14
 
8
15
  /****** Resource record specific setters *******/
9
16
  setCname(val) {
10
- // A <domain-name> which specifies the canonical or primary
11
- // name for the owner. The owner name is an alias.
12
-
13
- if (!val) this.throwHelp('CNAME: cname is required')
14
-
15
- if (this.isIPv4(val) || this.isIPv6(val)) this.throwHelp(`CNAME: cname must be a FQDN: RFC 2181`)
16
-
17
- if (!this.isFullyQualified('CNAME', 'cname', val)) return
18
- if (!this.isValidHostname('CNAME', 'cname', val)) return
19
-
20
- // RFC 4034: letters in the DNS names are lower cased
21
- this.set('cname', val.toLowerCase())
17
+ this.setTypedValue('fqdn', 'cname', val)
22
18
  }
23
19
 
24
20
  getDescription() {
25
21
  return 'Canonical Name'
26
22
  }
27
23
 
28
- getTags() {
29
- return ['common']
30
- }
31
-
32
- getRdataFields(arg) {
33
- return ['cname']
34
- }
35
-
36
- getRFCs() {
37
- return [1035, 2181]
38
- }
39
-
40
- getTypeId() {
41
- return 5
42
- }
43
-
44
24
  getCanonical() {
45
25
  return {
46
26
  owner: 'www.example.com.',
@@ -51,39 +31,8 @@ export default class CNAME extends RR {
51
31
  }
52
32
  }
53
33
 
54
- /****** IMPORTERS *******/
55
- fromTinydns({ tinyline }) {
56
- // Cfqdn:p:ttl:timestamp:lo
57
- const [fqdn, p, ttl, ts, loc] = tinyline.slice(1).split(':')
58
-
59
- return new CNAME({
60
- owner: this.fullyQualify(fqdn),
61
- ttl: parseInt(ttl, 10),
62
- type: 'CNAME',
63
- cname: this.fullyQualify(p),
64
- timestamp: ts,
65
- location: loc?.trim() ?? '',
66
- })
67
- }
68
-
69
- fromBind({ bindline }) {
70
- // test.example.com 3600 IN CNAME ...
71
- const [owner, ttl, c, type, cname] = bindline.split(/\s+/)
72
- return new CNAME({
73
- owner,
74
- ttl: parseInt(ttl, 10),
75
- class: c,
76
- type,
77
- cname,
78
- })
79
- }
80
-
81
34
  /****** EXPORTERS *******/
82
35
  getWireRdata() {
83
36
  return this.wirePackDomain(this.get('cname'))
84
37
  }
85
-
86
- toTinydns() {
87
- return `C${this.getTinyFQDN('owner')}:${this.get('cname')}:${this.getTinydnsPostamble()}\n`
88
- }
89
38
  }
package/rr/dhcid.js CHANGED
@@ -3,6 +3,11 @@ import RR from '../rr.js'
3
3
  import * as TINYDNS from '../lib/tinydns.js'
4
4
 
5
5
  export default class DHCID extends RR {
6
+ static typeName = 'DHCID'
7
+ static typeId = 49
8
+ static RFCs = [4701]
9
+ static rdataFields = [['data', 'base64']]
10
+
6
11
  constructor(opts) {
7
12
  super(opts)
8
13
  }
@@ -10,6 +15,7 @@ export default class DHCID extends RR {
10
15
  /****** Resource record specific setters *******/
11
16
  setData(val) {
12
17
  if (!val) this.throwHelp('DHCID: data is required')
18
+ this.isBase64('DHCID', 'data', val)
13
19
  this.set('data', val)
14
20
  }
15
21
 
@@ -17,18 +23,6 @@ export default class DHCID extends RR {
17
23
  return 'DHCP Identifier'
18
24
  }
19
25
 
20
- getRdataFields(arg) {
21
- return ['data']
22
- }
23
-
24
- getRFCs() {
25
- return [4701]
26
- }
27
-
28
- getTypeId() {
29
- return 49
30
- }
31
-
32
26
  getCanonical() {
33
27
  return {
34
28
  owner: 'host.example.com.',
@@ -42,33 +36,29 @@ export default class DHCID extends RR {
42
36
  /****** IMPORTERS *******/
43
37
  fromTinydns({ tinyline }) {
44
38
  // DHCID via generic, :fqdn:49:rdata:ttl:timestamp:lo
45
- const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
46
- if (n != 49) this.throwHelp('DHCID fromTinydns, invalid n')
39
+ const { owner, typeId, rdata, ttl, timestamp, location } = this.parseTinydnsLine(tinyline)
40
+ if (typeId != this.getTypeId()) this.throwHelp('DHCID fromTinydns, invalid n')
47
41
 
48
42
  return new DHCID({
49
- owner: this.fullyQualify(fqdn),
50
- ttl: parseInt(ttl, 10),
43
+ owner,
44
+ ttl,
51
45
  type: 'DHCID',
52
46
  data: TINYDNS.octalToBase64(rdata),
53
- timestamp: ts,
54
- location: loc?.trim() ?? '',
47
+ timestamp,
48
+ location,
55
49
  })
56
50
  }
57
51
 
58
- fromBind({ bindline }) {
59
- // host.example.com 3600 IN DHCID <base64data>
60
- const parts = bindline.split(/\s+/)
61
- const [owner, ttl, c, type] = parts
62
- return new DHCID({
63
- owner,
64
- ttl: parseInt(ttl, 10),
65
- class: c,
66
- type,
67
- data: parts.slice(4).join(''),
68
- })
52
+ /****** EXPORTERS *******/
53
+
54
+ getWireRdata() {
55
+ return new Uint8Array(
56
+ atob(this.get('data'))
57
+ .split('')
58
+ .map((c) => c.charCodeAt(0)),
59
+ )
69
60
  }
70
61
 
71
- /****** EXPORTERS *******/
72
62
  toTinydns() {
73
63
  return this.getTinydnsGeneric(TINYDNS.base64toOctal(this.get('data')))
74
64
  }
package/rr/dname.js CHANGED
@@ -2,39 +2,24 @@ import RR from '../rr.js'
2
2
  import * as TINYDNS from '../lib/tinydns.js'
3
3
 
4
4
  export default class DNAME extends RR {
5
+ static typeName = 'DNAME'
6
+ static typeId = 39
7
+ static RFCs = [2672, 6672]
8
+ static rdataFields = [['target', 'fqdn']]
9
+
5
10
  constructor(opts) {
6
11
  super(opts)
7
12
  }
8
13
 
9
14
  /****** Resource record specific setters *******/
10
15
  setTarget(val) {
11
- if (!val) this.throwHelp('DNAME: target is required')
12
-
13
- if (this.isIPv4(val) || this.isIPv6(val)) this.throwHelp(`DNAME: target must be a domain name`)
14
-
15
- this.isFullyQualified('DNAME', 'target', val)
16
- this.isValidHostname('DNAME', 'target', val)
17
-
18
- // RFC 4034: letters in the DNS names are lower cased
19
- this.set('target', val.toLowerCase())
16
+ this.setTypedValue('fqdn', 'target', val)
20
17
  }
21
18
 
22
19
  getDescription() {
23
20
  return 'Delegation Name'
24
21
  }
25
22
 
26
- getRdataFields(arg) {
27
- return ['target']
28
- }
29
-
30
- getRFCs() {
31
- return [2672, 6672]
32
- }
33
-
34
- getTypeId() {
35
- return 39
36
- }
37
-
38
23
  getCanonical() {
39
24
  return {
40
25
  owner: 'example.com.',
@@ -61,19 +46,12 @@ export default class DNAME extends RR {
61
46
  })
62
47
  }
63
48
 
64
- fromBind({ bindline }) {
65
- // test.example.com 3600 IN DNAME ...
66
- const [owner, ttl, c, type, target] = bindline.split(/\s+/)
67
- return new DNAME({
68
- owner,
69
- ttl: parseInt(ttl, 10),
70
- class: c,
71
- type,
72
- target,
73
- })
49
+ /****** EXPORTERS *******/
50
+
51
+ getWireRdata() {
52
+ return this.wirePackDomain(this.get('target'))
74
53
  }
75
54
 
76
- /****** EXPORTERS *******/
77
55
  toTinydns() {
78
56
  const rdata = TINYDNS.packDomainName(this.get('target'))
79
57
  return this.getTinydnsGeneric(rdata)
package/rr/dnskey.js CHANGED
@@ -1,8 +1,20 @@
1
1
  import RR from '../rr.js'
2
2
 
3
3
  import * as TINYDNS from '../lib/tinydns.js'
4
+ import * as BINARY from '../lib/binary.js'
4
5
 
5
6
  export default class DNSKEY extends RR {
7
+ static typeName = 'DNSKEY'
8
+ static typeId = 48
9
+ static RFCs = [4034, 6014, 8624, 9619, 9905]
10
+ static rdataFields = [
11
+ ['flags', 'u16'],
12
+ ['protocol', 'u8'],
13
+ ['algorithm', 'u8'],
14
+ ['publickey', 'base64'],
15
+ ]
16
+ static tags = ['dnssec']
17
+
6
18
  constructor(opts) {
7
19
  super(opts)
8
20
  }
@@ -71,7 +83,7 @@ export default class DNSKEY extends RR {
71
83
 
72
84
  setPublickey(val) {
73
85
  if (!val) this.throwHelp(`DNSKEY: publickey is required`)
74
-
86
+ this.isBase64('DNSKEY', 'publickey', val.replace(/[\s()]/g, ''))
75
87
  this.set('publickey', val)
76
88
  }
77
89
 
@@ -79,22 +91,6 @@ export default class DNSKEY extends RR {
79
91
  return 'DNS Public Key'
80
92
  }
81
93
 
82
- getTags() {
83
- return ['dnssec']
84
- }
85
-
86
- getRdataFields(arg) {
87
- return ['flags', 'protocol', 'algorithm', 'publickey']
88
- }
89
-
90
- getRFCs() {
91
- return [4034, 6014, 8624]
92
- }
93
-
94
- getTypeId() {
95
- return 48
96
- }
97
-
98
94
  getCanonical() {
99
95
  return {
100
96
  owner: 'example.com.',
@@ -104,7 +100,8 @@ export default class DNSKEY extends RR {
104
100
  flags: 256,
105
101
  protocol: 3,
106
102
  algorithm: 5,
107
- publickey: 'AQPSKAsj8...',
103
+ publickey:
104
+ 'AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwIXAqcOTiW7iHnQt5hwVAAAAA==',
108
105
  }
109
106
  }
110
107
 
@@ -136,34 +133,43 @@ export default class DNSKEY extends RR {
136
133
  }
137
134
 
138
135
  fromTinydns({ tinyline }) {
139
- const [fqdn, n, rdata, ttl, ts, loc] = tinyline.substring(1).split(':')
140
- if (n != 48) this.throwHelp('DNSKEY fromTinydns, invalid n')
136
+ const { owner, typeId, rdata, ttl, timestamp, location } = this.parseTinydnsLine(tinyline)
137
+ if (typeId != this.getTypeId()) this.throwHelp('DNSKEY fromTinydns, invalid n')
141
138
 
142
- const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
139
+ const bytes = TINYDNS.octalRdataToBytes(rdata)
143
140
 
144
141
  return new DNSKEY({
145
- owner: this.fullyQualify(fqdn),
146
- ttl: parseInt(ttl, 10),
142
+ owner,
143
+ ttl,
147
144
  type: 'DNSKEY',
148
- flags: bytes.readUInt16BE(0),
149
- protocol: bytes.readUInt8(2),
150
- algorithm: bytes.readUInt8(3),
151
- publickey: bytes.slice(4).toString(),
152
- timestamp: ts,
153
- location: loc?.trim() ?? '',
145
+ flags: (bytes[0] << 8) | bytes[1],
146
+ protocol: bytes[2],
147
+ algorithm: bytes[3],
148
+ publickey: BINARY.bytesToBase64(bytes.subarray(4)),
149
+ timestamp,
150
+ location,
154
151
  })
155
152
  }
156
153
 
157
154
  /****** EXPORTERS *******/
158
155
 
159
156
  toTinydns() {
160
- const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
161
-
162
157
  return this.getTinydnsGeneric(
163
158
  TINYDNS.UInt16toOctal(this.get('flags')) +
164
159
  TINYDNS.UInt8toOctal(this.get('protocol')) +
165
160
  TINYDNS.UInt8toOctal(this.get('algorithm')) +
166
- TINYDNS.escapeOctal(dataRe, this.get('publickey')),
161
+ TINYDNS.base64toOctal(this.get('publickey').replace(/[\s()]/g, '')),
167
162
  )
168
163
  }
164
+
165
+ getWireRdata() {
166
+ const keyBytes = BINARY.base64ToBytes(this.get('publickey').replace(/[\s()]/g, ''))
167
+ const bytes = new Uint8Array(4 + keyBytes.length)
168
+ const dv = new DataView(bytes.buffer, bytes.byteOffset)
169
+ dv.setUint16(0, this.get('flags'))
170
+ bytes[2] = this.get('protocol')
171
+ bytes[3] = this.get('algorithm')
172
+ bytes.set(keyBytes, 4)
173
+ return bytes
174
+ }
169
175
  }