web_package 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 21d491102ea52a0f47bd981ddf9d04e0be400e805b75340a58df13fc5a2304d0
4
+ data.tar.gz: 9763deee5944d7dc29d5f47ab490d1ea447d14e7d204f7fa3e68a69eb5ff92a3
5
+ SHA512:
6
+ metadata.gz: 3b8864f0082ff7e8ccfcff2066c9cace5c877b8ef4e77f80ef9b6fc1be5f441a03436437f565eb430f169e2f673f42f2667dc19c47e3ae92d91d95a54b64ced4
7
+ data.tar.gz: aab6bf17a00c12d82ba3e509e75765e9d92344c11409c876e1aa7800fa3b400b25cc9f209f2678a21a5f6d7e01404d931448ca26d6e8a9930d76d88437e6a590
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/.rubocop.yml ADDED
@@ -0,0 +1,42 @@
1
+ Metrics/LineLength:
2
+ Max: 100
3
+ IgnoreCopDirectives: true
4
+
5
+ Naming/UncommunicativeMethodParamName:
6
+ AllowedNames:
7
+ - s
8
+
9
+ Style/CharacterLiteral:
10
+ Enabled: false
11
+
12
+ Metrics/MethodLength:
13
+ Exclude:
14
+ - lib/web_package/cbor.rb
15
+ - lib/web_package/mice.rb
16
+ - lib/web_package/signed_http_exchange.rb
17
+
18
+ Metrics/AbcSize:
19
+ Max: 20
20
+ Exclude:
21
+ - lib/web_package/cbor.rb
22
+ - lib/web_package/mice.rb
23
+ - lib/web_package/signed_http_exchange.rb
24
+
25
+ Layout/EmptyLineAfterGuardClause:
26
+ Exclude:
27
+ - lib/web_package/signed_http_exchange.rb
28
+
29
+ Metrics/ClassLength:
30
+ Exclude:
31
+ - lib/web_package/signed_http_exchange.rb
32
+
33
+ Style/RedundantReturn:
34
+ Enabled: false
35
+
36
+ Layout/SpaceInsideRangeLiteral:
37
+ Exclude:
38
+ - lib/web_package/cbor.rb
39
+
40
+ Layout/ExtraSpacing:
41
+ Exclude:
42
+ - lib/web_package/cbor.rb
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Oleg Afanasyev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # web_package
2
+ Ruby implementation of Signed HTTP Exchange format, allowing a browser to trust that a HTTP request-response pair was generated by the origin it claims.
@@ -0,0 +1,89 @@
1
+ module WebPackage
2
+ # Concise Binary Object Representation
3
+ # https://tools.ietf.org/html/rfc7049
4
+ class CBOR
5
+ include Helpers
6
+
7
+ # Major type 0: an unsigned integer.
8
+ # Major type 1: a negative integer.
9
+ # Major type 2: a byte string.
10
+ # Major type 3: a text string, Unicode characters that is encoded as UTF-8 [RFC3629].
11
+ # Major type 4: an array of data items.
12
+ # Major type 5: a map of pairs of data items.
13
+ # Major type 6: optional semantic tagging of other major types.
14
+ # Major type 7: floating-point numbers and simple data types that need no content.
15
+ MAJOR_TYPES_RANGE = 0..7
16
+
17
+ def generate(input)
18
+ generate_bytes(input).pack('C*')
19
+ end
20
+
21
+ private
22
+
23
+ # https://tools.ietf.org/html/rfc7049#section-2.1
24
+ def generate_bytes(input)
25
+ case input
26
+ when Hash
27
+ input = input.transform_keys { |key| bin(key) }
28
+
29
+ bytes = hsh_size(input)
30
+ bytes[0] |= major_type(5)
31
+
32
+ input.keys.sort_by(&:bytesize).each do |key|
33
+ bytes.concat generate_bytes(key)
34
+ bytes.concat generate_bytes(input[key])
35
+ end
36
+ when String, Symbol
37
+ input = input.to_s
38
+ bytes = str_size(input)
39
+ # rubocop: disable Style/IdenticalConditionalBranches
40
+ # TODO: Use major_type(3) for non-binary strings.
41
+ # Right now all strings are encoded as byte strings because Chrome eats only such.
42
+ # So, we need to either proove wrong, or submit an issue to chromium dev.
43
+ bytes[0] |= input.encoding == Encoding::BINARY ? major_type(2) : major_type(2)
44
+ # rubocop: enable Style/IdenticalConditionalBranches
45
+ bytes.concat input.bytes
46
+ when Integer
47
+ raise '[CBOR] Not implemented for negative integers' if input.negative?
48
+
49
+ bytes = int_size(input)
50
+ bytes[0] |= major_type(0) # a positive integer
51
+ else
52
+ raise "[CBOR] Not implemented for #{input.class} class"
53
+ end
54
+
55
+ bytes
56
+ end
57
+
58
+ def major_type(num)
59
+ unless MAJOR_TYPES_RANGE.include? num
60
+ raise "[CBOR] Cannot infer Major Type from int #{num}, which is outside of allowed range."
61
+ end
62
+
63
+ # the type takes up 3 most significant bits of first byte
64
+ num << 5
65
+ end
66
+
67
+ def hsh_size(hsh)
68
+ size = hsh.size
69
+ raise '[CBOR] Not implemented for the hash of size more than 23 pairs' unless size < 24
70
+
71
+ [size]
72
+ end
73
+
74
+ def str_size(s)
75
+ int_size s.bytesize
76
+ end
77
+
78
+ def int_size(num)
79
+ case num
80
+ when 0... 24 then [num]
81
+ when 24... 2**8 then [24, *[num].pack('C').bytes]
82
+ when 2**8...2**16 then [25, *[num].pack('S>').bytes]
83
+ when 2**16...2**32 then [26, *[num].pack('L>').bytes]
84
+ when 2**32...2**64 then [27, *[num].pack('Q>').bytes]
85
+ else raise '[CBOR] Not implemented for integers greater than (2**64 - 1) bits'
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ module WebPackage
2
+ module Errors
3
+ class BodyEncodingError < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ module WebPackage
2
+ # Helper methods used in the library.
3
+ module Helpers
4
+ private
5
+
6
+ def bin(s)
7
+ force_bin(s.is_a?(String) ? s.dup : s.to_s)
8
+ end
9
+
10
+ def force_bin(s)
11
+ s.force_encoding Encoding::ASCII_8BIT
12
+ end
13
+
14
+ def digest(s)
15
+ Digest::SHA256.digest s
16
+ end
17
+
18
+ def base64(s)
19
+ Base64.strict_encode64 s
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,58 @@
1
+ module WebPackage
2
+ # Merkle Integrity Content Encoding
3
+ # https://tools.ietf.org/id/draft-thomson-http-mice-03.html
4
+ class MICE
5
+ include Helpers
6
+
7
+ CHUNK_SIZE = 2**14 # bytes
8
+
9
+ attr_reader :headers, :body
10
+
11
+ def initialize(headers, body)
12
+ @body = body.dup
13
+ @headers = headers.transform_keys { |key| key.to_s.downcase } # only lowercase keys allowed
14
+
15
+ @encoded = false
16
+ end
17
+
18
+ def encode!
19
+ return if encoded?
20
+
21
+ @root_digest, @body = interlace_body_with_digests
22
+ @headers.merge! 'content-encoding' => 'mi-sha256-03',
23
+ 'digest' => "mi-sha256-03=#{base64(@root_digest)}",
24
+ 'x-content-type-options' => 'nosniff'
25
+ # TODO: find out why we need (or do not need) Link header for the purpose of
26
+ # serving Signed Http Exchange
27
+ # linkHeader, err := formatLinkHeader(metadata.Preloads)
28
+ # fetchResp.Header.Set("Link", linkHeader)
29
+
30
+ @encoded = true
31
+ end
32
+
33
+ def encoded?
34
+ @encoded
35
+ end
36
+
37
+ private
38
+
39
+ def interlace_body_with_digests
40
+ num_parts = @body.bytesize.fdiv(CHUNK_SIZE).ceil
41
+
42
+ chunks = []
43
+ proofs = []
44
+
45
+ num_parts.times do |i|
46
+ delimeter = i.zero? && "\x00" || "\x01"
47
+ ri = num_parts - i - 1
48
+
49
+ chunks << force_bin(@body.byteslice(ri * CHUNK_SIZE, CHUNK_SIZE))
50
+ proofs << digest("#{chunks.last}#{proofs.last}#{delimeter}")
51
+ end
52
+
53
+ chunks << [CHUNK_SIZE].pack('Q>')
54
+
55
+ return proofs.pop, chunks.zip(proofs).flatten.reverse!.join
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,238 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ module WebPackage
4
+ # Builds headers and body of SXG format for a given pair of HTTP request-response.
5
+ # SXG format allows a browser to trust that a single HTTP request/response pair was
6
+ # generated by the origin it claims.
7
+ #
8
+ # Accetps two arguments: response url and response object (with body, headers and status).
9
+ # Provides two public methods to build HTTP response in SXG format: headers and body.
10
+ # Current implementation is lazy, meaning that signing is performed upon the
11
+ # invocation of the `body` method.
12
+ class SignedHttpExchange
13
+ SIGNATURE_MAX_SIZE = 2**14
14
+ HEADERS_MAX_SIZE = 2**19
15
+ SXG_HEADERS = {
16
+ 'Content-Type' => 'application/signed-exchange;v=b3',
17
+ 'Cache-Control' => 'no-transform',
18
+ 'X-Content-Type-Options' => 'nosniff'
19
+ }.freeze
20
+ CERT_URL = ENV['SXG_CERT_URL']
21
+ CERT_PATH = ENV['SXG_CERT_PATH']
22
+ PRIV_PATH = ENV['SXG_PRIV_PATH']
23
+ INTEGRITY = 'digest/mi-sha256-03'.freeze
24
+
25
+ # Mock request-response pair just in case:
26
+ MOCK_URL = 'https://example.com/wow-fake-path'.freeze
27
+ Response = Struct.new :body, :headers, :status
28
+ MOCK_RESP = Response['<h1>Hello!</h1>', { 'Content-Type' => 'text/html; charset=utf-8' }, 200]
29
+
30
+ attr_reader :url, :response, :mice
31
+
32
+ # accepts two args representing a request-response pair to be packed into SXG format
33
+ def initialize(url = MOCK_URL, response = MOCK_RESP)
34
+ @uri = build_uri_from url
35
+ @response = response
36
+
37
+ @cbor = CBOR.new
38
+ @mice = MICE.new(@response.headers, @response.body).tap(&:encode!)
39
+ @signer = Signer.new CERT_PATH, PRIV_PATH
40
+ end
41
+
42
+ def headers
43
+ SXG_HEADERS
44
+ end
45
+
46
+ # https://tools.ietf.org/html/draft-yasskin-http-origin-signed-responses-05#section-5.3
47
+ def body
48
+ return @body if @body
49
+ @body = ''
50
+
51
+ # 1. 8 bytes consisting of the ASCII characters "sxg1" followed by 4
52
+ # 0x00 bytes, to serve as a file signature. This is redundant with
53
+ # the MIME type, and recipients that receive both MUST check that
54
+ # they match and stop parsing if they don't.
55
+ # TODO: The implementation of the final RFC MUST use the following line:
56
+ # @body << "sxg1\x00\x00\x00\x00"
57
+ @body << "sxg1-b3\x00"
58
+
59
+ # 2. 2 bytes storing a big-endian integer "fallbackUrlLength".
60
+ @body << [fallback_url.bytesize].pack('S>')
61
+
62
+ # 3. "fallbackUrlLength" bytes holding a "fallbackUrl", which MUST be
63
+ # an absolute URL with a scheme of "https".
64
+ @body << fallback_url
65
+
66
+ # 4. 3 bytes storing a big-endian integer "sigLength". If this is
67
+ # larger than 16384 (16*1024), parsing MUST fail.
68
+ if signature.bytesize > SIGNATURE_MAX_SIZE
69
+ raise Errors::BodyEncodingError, 'Structured Signature Header length is too large: '\
70
+ "#{signature.bytesize} bytes, max: #{SIGNATURE_MAX_SIZE} bytes."
71
+ end
72
+ @body << [signature.bytesize].pack('L>').byteslice(-3, 3)
73
+
74
+ # 5. 3 bytes storing a big-endian integer "headerLength". If this is
75
+ # larger than 524288 (512*1024), parsing MUST fail.
76
+ if encoded_mice_headers.bytesize > HEADERS_MAX_SIZE
77
+ raise Errors::BodyEncodingError, 'Response Headers length is too large: '\
78
+ "#{encoded_mice_headers.bytesize} bytes, max: #{HEADERS_MAX_SIZE} bytes."
79
+ end
80
+ @body << [encoded_mice_headers.bytesize].pack('L>').byteslice(-3, 3)
81
+
82
+ # 6. "sigLength" bytes holding the "Signature" header field's value
83
+ # (Section 3.1).
84
+ @body << signature
85
+
86
+ # 7. "headerLength" bytes holding "signedHeaders", the canonical
87
+ # serialization (Section 3.4) of the CBOR representation of the
88
+ # response headers of the exchange represented by the "application/
89
+ # signed-exchange" resource (Section 3.2), excluding the
90
+ # "Signature" header field.
91
+ @body << encoded_mice_headers
92
+
93
+ # 8. The payload body (Section 3.3 of [RFC7230]) of the exchange
94
+ # represented by the "application/signed-exchange" resource.
95
+ # Note that the use of the payload body here means that a
96
+ # "Transfer-Encoding" header field inside the "application/signed-
97
+ # exchange" header block has no effect. A "Transfer-Encoding"
98
+ # header field on the outer HTTP response that transfers this
99
+ # resource still has its normal effect.
100
+ @body << @mice.body
101
+ end
102
+
103
+ private
104
+
105
+ def message
106
+ return @message if @message
107
+ @message = ''
108
+
109
+ # Help in debugging "VerifyFinal failed." error, source code:
110
+ # https://github.com/chromium/chromium/blob/8f0bd6c8be04f0dd556d42820f1eec0963dfe10b/
111
+ # content/browser/web_package/signed_exchange_signature_verifier.cc#L120
112
+ # It may look as if something is wrong with the certificate or signing algorithm, but in fact
113
+ # the error is caused by the message being composed incorrectly.
114
+ # So, if you get such an error - please check the `message` first.
115
+
116
+ # From specs:
117
+ # https://tools.ietf.org/html/draft-yasskin-http-origin-signed-responses-05#section-3.5
118
+ #
119
+ # Let "message" be the concatenation of the following byte
120
+ # strings. This matches the [RFC8446] format to avoid cross-
121
+ # protocol attacks if anyone uses the same key in a TLS
122
+ # certificate and an exchange-signing certificate.
123
+
124
+ # 1. A string that consists of octet 32 (0x20) repeated 64 times.
125
+ @message << "\x20" * 64
126
+
127
+ # 2. A context string: the ASCII encoding of "HTTP Exchange 1".
128
+ # ... but implementations of drafts MUST NOT use it and MUST use another
129
+ # draft-specific string beginning with "HTTP Exchange 1 " instead.
130
+ # TODO: The implementation of the final RFC MUST use the following line:
131
+ # @message << "HTTP Exchange 1"
132
+ @message << 'HTTP Exchange 1 b3'
133
+
134
+ # 3. A single 0 byte which serves as a separator.
135
+ @message << "\x00"
136
+
137
+ # 4. If "cert-sha256" is set, a byte holding the value 32
138
+ # followed by the 32 bytes of the value of "cert-sha256".
139
+ # Otherwise a 0 byte.
140
+ @message << (@signer.cert_sha256 ? "\x20#{@signer.cert_sha256}" : "\x00")
141
+
142
+ # 5. The 8-byte big-endian encoding of the length in bytes of
143
+ # "validity-url", followed by the bytes of "validity-url".
144
+ @message << [validity_url.bytesize].pack('Q>')
145
+ @message << validity_url
146
+
147
+ # 6. The 8-byte big-endian encoding of "date".
148
+ @message << [@signer.signed_at.to_i].pack('Q>')
149
+
150
+ # 7. The 8-byte big-endian encoding of "expires".
151
+ @message << [@signer.expires_at.to_i].pack('Q>')
152
+
153
+ # 8. The 8-byte big-endian encoding of the length in bytes of
154
+ # "requestUrl", followed by the bytes of "requestUrl".
155
+ @message << [url.bytesize].pack('Q>')
156
+ @message << url
157
+
158
+ # 9. The 8-byte big-endian encoding of the length in bytes of
159
+ # "responseHeaders", followed by the bytes of
160
+ # "responseHeaders".
161
+ @message << [encoded_mice_headers.bytesize].pack('Q>')
162
+ @message << encoded_mice_headers
163
+ end
164
+
165
+ def encoded_mice_headers
166
+ @encoded_mice_headers ||=
167
+ @cbor.generate @mice.headers.merge(':status' => bin(response.status))
168
+ end
169
+
170
+ # returns a string representing serialized label + params
171
+ def structured_header_for(label, params)
172
+ if params['cert-url'].blank?
173
+ raise '[SignedHttpExchange] No certificate url provided - please use `SXG_CERT_URL` '\
174
+ 'env var. Endpoint should respond with `application/cert-chain+cbor` content type.'
175
+ end
176
+
177
+ res = [label]
178
+
179
+ params.sort.each do |key, value|
180
+ # https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09#section-4.1.10
181
+ res << "#{key}=" + case value
182
+ when Integer then value.to_s
183
+ when String then %("#{value}") # a text string
184
+ when Array then "*#{base64(value.pack('C*'))}*" # a byte string
185
+ end
186
+ end
187
+
188
+ res.join(?;)
189
+ end
190
+
191
+ # https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09
192
+ def signature
193
+ # Example of a signature:
194
+ # label;cert-sha256=*+DoXYlCX+bFRyW65R3bFA2ICIz8Tyu54MLFUFo5tziA=*;cert-url="https://exampl
195
+ # e.com/cert.cbor";date=1555925114;expires=1555928714;integrity="digest/mi-sha256-03";sig=*
196
+ # MEQCIBgsnVxmRqzjeFczuXnQClf2bwtHdGeGGSMOz6y5EH7HAiAu1lt2ERsWIRcOmszB3XneSWoGKrMD7wvalVfPp
197
+ # 4tb9Q==*;validity-url="https://example.com/resource.validity.msg"
198
+ @signature ||=
199
+ structured_header_for 'label', 'cert-sha256': @signer.cert_sha256.bytes,
200
+ 'cert-url': CERT_URL,
201
+ 'date': @signer.signed_at.to_i,
202
+ 'expires': @signer.expires_at.to_i,
203
+ 'integrity': INTEGRITY,
204
+ 'sig': @signer.sign(message).bytes,
205
+ 'validity-url': validity_url
206
+ end
207
+
208
+ def build_uri_from(url)
209
+ u = url.is_a?(URI) ? url : URI(url)
210
+ raise "[SignedHttpExchange] Unsupported URI scheme: #{u.scheme}" unless u.is_a? URI::HTTPS
211
+ raise '[SignedHttpExchange] Request host is required' if u.host.blank?
212
+
213
+ u
214
+ end
215
+
216
+ def fallback_url
217
+ @fallback_url ||= @uri.to_s
218
+ end
219
+
220
+ def validity_url
221
+ @validity_url ||= begin
222
+ path = @uri.path
223
+ fi = path.index(?.)
224
+ no_format_path = fi ? path[0...fi] : path # path without format, i.e. default :html
225
+
226
+ URI::HTTPS.build(host: @uri.host, path: no_format_path).to_s
227
+ end
228
+ end
229
+
230
+ def bin(s)
231
+ s.to_s.force_encoding Encoding::ASCII_8BIT
232
+ end
233
+
234
+ def base64(s)
235
+ Base64.strict_encode64 s
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,23 @@
1
+ module WebPackage
2
+ # Performs signing of a message with ECDSA.
3
+ class Signer
4
+ include Helpers
5
+ attr_reader :signed_at, :expires_at, :cert, :integrity, :cert_url
6
+
7
+ def initialize(path_to_cert, path_to_key)
8
+ @alg = OpenSSL::PKey::EC.new(File.read(path_to_key))
9
+ @cert = OpenSSL::X509::Certificate.new(File.read(path_to_cert))
10
+
11
+ @signed_at = Time.zone.now
12
+ @expires_at = @signed_at + 7.days
13
+ end
14
+
15
+ def sign(message)
16
+ @alg.dsa_sign_asn1 digest(message)
17
+ end
18
+
19
+ def cert_sha256
20
+ @cert_sha256 ||= digest(@cert.to_der)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module WebPackage
2
+ VERSION = '0.0.0'.freeze
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'web_package/errors/body_format_error'
2
+ require 'web_package/errors/certificate_url_format_error'
3
+ require 'web_package/version'
4
+ require 'web_package/helpers'
5
+ require 'web_package/mice'
6
+ require 'web_package/cbor'
7
+ require 'web_package/signed_http_exchange'
8
+ require 'web_package/signer'
@@ -0,0 +1,22 @@
1
+ require File.expand_path('lib/web_package/version', __dir__)
2
+ require 'date'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'web_package'
6
+ s.version = WebPackage::VERSION
7
+ s.date = Date.today.to_s
8
+ s.summary = 'Packaging Websites with Ruby.'
9
+ s.description = 'Ruby implementation of Signed HTTP Exchange format, allowing a browser to '\
10
+ 'trust that a HTTP request-response pair was generated by the origin it claims.'
11
+ s.authors = ['Oleg Afanasyev', 'Alexey Martynyuk']
12
+ s.email = ['gafrom@gmail.com']
13
+ s.homepage = 'https://github.com/gafrom/web_package'
14
+ s.license = 'MIT'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.require_paths = ['lib']
18
+
19
+ s.required_ruby_version = '>=2.0.0'
20
+
21
+ s.add_development_dependency 'rubocop', '~> 0.67'
22
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: web_package
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Oleg Afanasyev
8
+ - Alexey Martynyuk
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-04-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubocop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '0.67'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '0.67'
28
+ description: Ruby implementation of Signed HTTP Exchange format, allowing a browser
29
+ to trust that a HTTP request-response pair was generated by the origin it claims.
30
+ email:
31
+ - gafrom@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - ".rubocop.yml"
38
+ - LICENSE
39
+ - README.md
40
+ - lib/web_package.rb
41
+ - lib/web_package/cbor.rb
42
+ - lib/web_package/errors/body_encoding_error.rb
43
+ - lib/web_package/helpers.rb
44
+ - lib/web_package/mice.rb
45
+ - lib/web_package/signed_http_exchange.rb
46
+ - lib/web_package/signer.rb
47
+ - lib/web_package/version.rb
48
+ - web_package.gemspec
49
+ homepage: https://github.com/gafrom/web_package
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.0
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.7.6
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: Packaging Websites with Ruby.
73
+ test_files: []