cose 0.8.0 → 0.9.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.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/.rubocop.yml +1 -1
- data/.travis.yml +4 -13
- data/Appraisals +2 -0
- data/CHANGELOG.md +10 -0
- data/README.md +27 -30
- data/SECURITY.md +2 -2
- data/cose.gemspec +3 -3
- data/lib/cose.rb +1 -0
- data/lib/cose/algorithm.rb +40 -0
- data/lib/cose/algorithm/base.rb +16 -0
- data/lib/cose/algorithm/ecdsa.rb +62 -0
- data/lib/cose/algorithm/hmac.rb +38 -0
- data/lib/cose/algorithm/rsa_pss.rb +50 -0
- data/lib/cose/algorithm/signature_algorithm.rb +14 -0
- data/lib/cose/encrypt.rb +2 -0
- data/lib/cose/encrypt0.rb +2 -0
- data/lib/cose/error.rb +5 -0
- data/lib/cose/key.rb +2 -0
- data/lib/cose/key/curve.rb +2 -0
- data/lib/cose/mac.rb +29 -12
- data/lib/cose/mac0.rb +24 -1
- data/lib/cose/recipient.rb +2 -0
- data/lib/cose/security_message.rb +49 -4
- data/lib/cose/security_message/headers.rb +31 -0
- data/lib/cose/sign.rb +35 -1
- data/lib/cose/sign1.rb +24 -1
- data/lib/cose/signature.rb +2 -0
- data/lib/cose/version.rb +1 -1
- metadata +16 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad1f050dae008442fcd66013cf0b0dcd6c192b66c8fb308b7aeed1fdd53687f7
|
4
|
+
data.tar.gz: 2917bb5f18b73c9651ca33946aea646f72fcfb3d3ee84dc26bf653b7e2573a71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da3842878fc2de2d47006aeea49fdc644197226362de585f4b1692929676d0fa048567b29091682ede9f31aeda0a704db88a79f0773548342720c4bcb5a98969
|
7
|
+
data.tar.gz: edf9e3bef367c32a46c8a554a422df690c2156fcabc61c55c658cfa1f0b67f70471a9566052574c92bb640002196dc33244a19c86bb7368d00c35447142a4063
|
data/.gitmodules
ADDED
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -5,11 +5,10 @@ cache: bundler
|
|
5
5
|
rvm:
|
6
6
|
- ruby-head
|
7
7
|
- 2.7.0-preview1
|
8
|
-
- 2.6.
|
9
|
-
- 2.5.
|
10
|
-
- 2.4.
|
8
|
+
- 2.6.4
|
9
|
+
- 2.5.6
|
10
|
+
- 2.4.7
|
11
11
|
- 2.3.8
|
12
|
-
- 2.2.10
|
13
12
|
|
14
13
|
gemfile:
|
15
14
|
- gemfiles/openssl_head.gemfile
|
@@ -17,19 +16,11 @@ gemfile:
|
|
17
16
|
- gemfiles/openssl_2_0.gemfile
|
18
17
|
- gemfiles/openssl_default.gemfile
|
19
18
|
|
20
|
-
before_install: gem install bundler -v '~>
|
19
|
+
before_install: gem install bundler -v '~> 2.0'
|
21
20
|
|
22
21
|
matrix:
|
23
22
|
fast_finish: true
|
24
23
|
allow_failures:
|
25
24
|
- rvm: ruby-head
|
26
25
|
- rvm: 2.7.0-preview1
|
27
|
-
- rvm: 2.2.10
|
28
26
|
- gemfile: gemfiles/openssl_head.gemfile
|
29
|
-
exclude:
|
30
|
-
- rvm: 2.2.10
|
31
|
-
gemfile: gemfiles/openssl_head.gemfile
|
32
|
-
- rvm: 2.2.10
|
33
|
-
gemfile: gemfiles/openssl_2_1.gemfile
|
34
|
-
- rvm: 2.2.10
|
35
|
-
gemfile: gemfiles/openssl_2_0.gemfile
|
data/Appraisals
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.9.0] - 2019-08-31
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- `COSE::Sign1#verify`
|
8
|
+
- `COSE::Sign#verify`
|
9
|
+
- `COSE::Mac0#verify`
|
10
|
+
- `COSE::Mac#verify`
|
11
|
+
|
3
12
|
## [v0.8.0] - 2019-08-17
|
4
13
|
|
5
14
|
### Added
|
@@ -86,6 +95,7 @@
|
|
86
95
|
- EC2 key object
|
87
96
|
- Works with ruby 2.5
|
88
97
|
|
98
|
+
[v0.8.0]: https://github.com/cedarcode/cose-ruby/compare/v0.8.0...v0.9.0/
|
89
99
|
[v0.8.0]: https://github.com/cedarcode/cose-ruby/compare/v0.7.0...v0.8.0/
|
90
100
|
[v0.7.0]: https://github.com/cedarcode/cose-ruby/compare/v0.6.1...v0.7.0/
|
91
101
|
[v0.6.1]: https://github.com/cedarcode/cose-ruby/compare/v0.6.0...v0.6.1/
|
data/README.md
CHANGED
@@ -145,15 +145,14 @@ cbor_data = "..."
|
|
145
145
|
|
146
146
|
sign = COSE::Sign.deserialize(cbor_data)
|
147
147
|
|
148
|
-
|
149
|
-
sign.
|
150
|
-
sign.payload
|
148
|
+
# Verify by doing (key should be a COSE::Key):
|
149
|
+
sign.verify(key)
|
151
150
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
151
|
+
# or, if externally supplied authenticated data exists:
|
152
|
+
sign.verify(key, external_aad)
|
153
|
+
|
154
|
+
# Then access payload
|
155
|
+
sign.payload
|
157
156
|
```
|
158
157
|
|
159
158
|
#### COSE_Sign1
|
@@ -163,10 +162,14 @@ cbor_data = "..."
|
|
163
162
|
|
164
163
|
sign1 = COSE::Sign1.deserialize(cbor_data)
|
165
164
|
|
166
|
-
|
167
|
-
sign1.
|
165
|
+
# Verify by doing (key should be a COSE::Key):
|
166
|
+
sign1.verify(key)
|
167
|
+
|
168
|
+
# or, if externally supplied authenticated data exists:
|
169
|
+
sign1.verify(key, external_aad)
|
170
|
+
|
171
|
+
# Then access payload
|
168
172
|
sign1.payload
|
169
|
-
sign1.signature
|
170
173
|
```
|
171
174
|
|
172
175
|
### MAC Objects
|
@@ -178,24 +181,14 @@ cbor_data = "..."
|
|
178
181
|
|
179
182
|
mac = COSE::Mac.deserialize(cbor_data)
|
180
183
|
|
181
|
-
|
182
|
-
mac.
|
183
|
-
mac.payload
|
184
|
-
mac.tag
|
184
|
+
# Verify by doing (key should be a COSE::Key::Symmetric):
|
185
|
+
mac.verify(key)
|
185
186
|
|
186
|
-
|
187
|
-
|
188
|
-
recipient.unprotected_headers
|
189
|
-
recipient.ciphertext
|
187
|
+
# or, if externally supplied authenticated data exists:
|
188
|
+
mac.verify(key, external_aad)
|
190
189
|
|
191
|
-
|
192
|
-
|
193
|
-
recipient.protected_headers
|
194
|
-
recipient.unprotected_headers
|
195
|
-
recipient.ciphertext
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
190
|
+
# Then access payload
|
191
|
+
mac.payload
|
199
192
|
```
|
200
193
|
|
201
194
|
#### COSE_Mac0
|
@@ -205,10 +198,14 @@ cbor_data = "..."
|
|
205
198
|
|
206
199
|
mac0 = COSE::Mac0.deserialize(cbor_data)
|
207
200
|
|
208
|
-
|
209
|
-
mac0.
|
201
|
+
# Verify by doing (key should be a COSE::Key::Symmetric):
|
202
|
+
mac0.verify(key)
|
203
|
+
|
204
|
+
# or, if externally supplied authenticated data exists:
|
205
|
+
mac0.verify(key, external_aad)
|
206
|
+
|
207
|
+
# Then access payload
|
210
208
|
mac0.payload
|
211
|
-
mac0.tag
|
212
209
|
```
|
213
210
|
|
214
211
|
### Encryption Objects
|
data/SECURITY.md
CHANGED
data/cose.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
30
|
spec.require_paths = ["lib"]
|
31
31
|
|
32
|
-
spec.required_ruby_version = ">= 2.
|
32
|
+
spec.required_ruby_version = ">= 2.3"
|
33
33
|
|
34
34
|
spec.add_dependency "cbor", "~> 0.5.9"
|
35
35
|
|
@@ -38,6 +38,6 @@ Gem::Specification.new do |spec|
|
|
38
38
|
spec.add_development_dependency "byebug", ">= 10.0"
|
39
39
|
spec.add_development_dependency "rake", "~> 12.3"
|
40
40
|
spec.add_development_dependency "rspec", "~> 3.8"
|
41
|
-
spec.add_development_dependency "rubocop", "0.
|
42
|
-
spec.add_development_dependency "rubocop-performance", "~> 1.
|
41
|
+
spec.add_development_dependency "rubocop", "0.74.0"
|
42
|
+
spec.add_development_dependency "rubocop-performance", "~> 1.4"
|
43
43
|
end
|
data/lib/cose.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cose/algorithm/ecdsa"
|
4
|
+
require "cose/algorithm/hmac"
|
5
|
+
require "cose/algorithm/rsa_pss"
|
6
|
+
|
7
|
+
module COSE
|
8
|
+
module Algorithm
|
9
|
+
@registered_by_id = {}
|
10
|
+
@registered_by_name = {}
|
11
|
+
|
12
|
+
def self.register(algorithm)
|
13
|
+
@registered_by_id[algorithm.id] = algorithm
|
14
|
+
@registered_by_name[algorithm.name] = algorithm
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find(id_or_name)
|
18
|
+
by_id(id_or_name) || by_name(id_or_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.by_id(id)
|
22
|
+
@registered_by_id[id]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.by_name(name)
|
26
|
+
@registered_by_name[name]
|
27
|
+
end
|
28
|
+
|
29
|
+
register(ECDSA.new(-7, "ES256", hash_function: "SHA256"))
|
30
|
+
register(ECDSA.new(-35, "ES384", hash_function: "SHA384"))
|
31
|
+
register(ECDSA.new(-36, "ES512", hash_function: "SHA512"))
|
32
|
+
register(RSAPSS.new(-37, "PS256", hash_function: "SHA256", salt_length: 32))
|
33
|
+
register(RSAPSS.new(-38, "PS384", hash_function: "SHA384", salt_length: 48))
|
34
|
+
register(RSAPSS.new(-39, "PS512", hash_function: "SHA512", salt_length: 64))
|
35
|
+
register(HMAC.new(4, "HMAC 256/64", hash_function: "SHA256", tag_length: 64))
|
36
|
+
register(HMAC.new(5, "HMAC 256/256", hash_function: "SHA256", tag_length: 256))
|
37
|
+
register(HMAC.new(6, "HMAC 384/384", hash_function: "SHA384", tag_length: 384))
|
38
|
+
register(HMAC.new(7, "HMAC 512/512", hash_function: "SHA512", tag_length: 512))
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cose/algorithm/signature_algorithm"
|
4
|
+
require "cose/error"
|
5
|
+
require "cose/key/ec2"
|
6
|
+
require "openssl"
|
7
|
+
|
8
|
+
module COSE
|
9
|
+
module Algorithm
|
10
|
+
class ECDSA < SignatureAlgorithm
|
11
|
+
attr_reader :hash_function
|
12
|
+
|
13
|
+
def initialize(*args, hash_function:)
|
14
|
+
super(*args)
|
15
|
+
|
16
|
+
@hash_function = hash_function
|
17
|
+
end
|
18
|
+
|
19
|
+
def compatible_key?(key)
|
20
|
+
to_pkey(key)
|
21
|
+
rescue COSE::Error
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def valid_signature?(key, signature, verification_data)
|
28
|
+
pkey = to_pkey(key)
|
29
|
+
|
30
|
+
pkey.verify(hash_function, in_der(signature, pkey.group.degree), verification_data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_pkey(key)
|
34
|
+
case key
|
35
|
+
when COSE::Key::EC2
|
36
|
+
key.to_pkey
|
37
|
+
when OpenSSL::PKey::EC
|
38
|
+
key
|
39
|
+
else
|
40
|
+
raise(COSE::Error, "Incompatible key for algorithm")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Borrowed from jwt rubygem.
|
45
|
+
# https://github.com/jwt/ruby-jwt/blob/7a6a3f1dbaff806993156d1dff9c217bb2523ff8/lib/jwt/security_utils.rb#L34-L39
|
46
|
+
#
|
47
|
+
# Hopefully this will be provided by openssl rubygem in the future.
|
48
|
+
def in_der(signature, key_length)
|
49
|
+
n = (key_length.to_f / BYTE_LENGTH).ceil
|
50
|
+
|
51
|
+
if signature.size == n * 2
|
52
|
+
r = signature[0..(n - 1)]
|
53
|
+
s = signature[n..-1]
|
54
|
+
|
55
|
+
OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
|
56
|
+
else
|
57
|
+
signature
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cose/algorithm/base"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
module COSE
|
7
|
+
module Algorithm
|
8
|
+
class HMAC < Base
|
9
|
+
attr_reader :hash_function, :tag_length
|
10
|
+
|
11
|
+
def initialize(*args, hash_function:, tag_length:)
|
12
|
+
super(*args)
|
13
|
+
|
14
|
+
@hash_function = hash_function
|
15
|
+
@tag_length = tag_length
|
16
|
+
end
|
17
|
+
|
18
|
+
def mac(key, to_be_signed)
|
19
|
+
mac = OpenSSL::HMAC.digest(hash_function, key, to_be_signed)
|
20
|
+
|
21
|
+
if tag_bytesize && tag_bytesize < mac.bytesize
|
22
|
+
mac.byteslice(0, tag_bytesize)
|
23
|
+
else
|
24
|
+
mac
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def tag_bytesize
|
31
|
+
@tag_bytesize ||=
|
32
|
+
if tag_length
|
33
|
+
tag_length / BYTE_LENGTH
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cose/algorithm/signature_algorithm"
|
4
|
+
require "cose/key/rsa"
|
5
|
+
require "cose/error"
|
6
|
+
require "openssl"
|
7
|
+
|
8
|
+
module COSE
|
9
|
+
module Algorithm
|
10
|
+
class RSAPSS < SignatureAlgorithm
|
11
|
+
attr_reader :hash_function, :salt_length
|
12
|
+
|
13
|
+
def initialize(*args, hash_function:, salt_length:)
|
14
|
+
super(*args)
|
15
|
+
|
16
|
+
@hash_function = hash_function
|
17
|
+
@salt_length = salt_length
|
18
|
+
end
|
19
|
+
|
20
|
+
def compatible_key?(key)
|
21
|
+
to_pkey(key)
|
22
|
+
rescue COSE::Error
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def valid_signature?(key, signature, verification_data)
|
29
|
+
pkey = to_pkey(key)
|
30
|
+
|
31
|
+
if pkey.respond_to?(:verify_pss)
|
32
|
+
pkey.verify_pss(hash_function, signature, verification_data, salt_length: :digest, mgf1_hash: hash_function)
|
33
|
+
else
|
34
|
+
raise(COSE::Error, "Update to openssl gem >= v2.1 to have RSA-PSS support")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_pkey(key)
|
39
|
+
case key
|
40
|
+
when COSE::Key::RSA
|
41
|
+
key.to_pkey
|
42
|
+
when OpenSSL::PKey::RSA
|
43
|
+
key
|
44
|
+
else
|
45
|
+
raise(COSE::Error, "Incompatible key for algorithm")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cose/algorithm/base"
|
4
|
+
require "cose/error"
|
5
|
+
|
6
|
+
module COSE
|
7
|
+
module Algorithm
|
8
|
+
class SignatureAlgorithm < Base
|
9
|
+
def verify(key, signature, verification_data)
|
10
|
+
valid_signature?(key, signature, verification_data) || raise(COSE::Error, "Signature verification failed")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/cose/encrypt.rb
CHANGED
data/lib/cose/encrypt0.rb
CHANGED
data/lib/cose/error.rb
ADDED
data/lib/cose/key.rb
CHANGED
data/lib/cose/key/curve.rb
CHANGED
data/lib/cose/mac.rb
CHANGED
@@ -1,25 +1,42 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "cose/recipient"
|
3
|
-
require "cose/
|
4
|
+
require "cose/mac0"
|
4
5
|
|
5
6
|
module COSE
|
6
|
-
class Mac <
|
7
|
-
|
7
|
+
class Mac < Mac0
|
8
|
+
CONTEXT = "MAC"
|
9
|
+
|
10
|
+
attr_reader :recipients
|
8
11
|
|
9
12
|
def self.keyword_arguments_for_initialize(decoded)
|
10
|
-
{
|
11
|
-
payload: CBOR.decode(decoded[0]),
|
12
|
-
tag: decoded[1],
|
13
|
-
recipients: decoded[2].map { |s| COSE::Recipient.deserialize(s) }
|
14
|
-
}
|
13
|
+
super.merge(recipients: decoded.last.map { |r| COSE::Recipient.from_array(r) })
|
15
14
|
end
|
16
15
|
|
17
|
-
def
|
16
|
+
def self.tag
|
17
|
+
97
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(recipients:, **keyword_arguments)
|
18
21
|
super(**keyword_arguments)
|
19
22
|
|
20
|
-
@payload = payload
|
21
|
-
@tag = tag
|
22
23
|
@recipients = recipients
|
23
24
|
end
|
25
|
+
|
26
|
+
def verify(key, external_aad = nil)
|
27
|
+
recipient = recipients.detect { |r| r.headers.kid == key.kid }
|
28
|
+
|
29
|
+
if recipient
|
30
|
+
super
|
31
|
+
else
|
32
|
+
raise(COSE::Error, "No recipient match the key")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def context
|
39
|
+
CONTEXT
|
40
|
+
end
|
24
41
|
end
|
25
42
|
end
|
data/lib/cose/mac0.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "cbor"
|
2
4
|
require "cose/security_message"
|
5
|
+
require "openssl"
|
3
6
|
|
4
7
|
module COSE
|
5
8
|
class Mac0 < SecurityMessage
|
9
|
+
CONTEXT = "MAC0"
|
10
|
+
|
6
11
|
attr_reader :payload, :tag
|
7
12
|
|
8
13
|
def self.keyword_arguments_for_initialize(decoded)
|
9
|
-
{ payload:
|
14
|
+
{ payload: decoded[0], tag: decoded[1] }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.tag
|
18
|
+
17
|
10
19
|
end
|
11
20
|
|
12
21
|
def initialize(payload:, tag:, **keyword_arguments)
|
@@ -15,5 +24,19 @@ module COSE
|
|
15
24
|
@payload = payload
|
16
25
|
@tag = tag
|
17
26
|
end
|
27
|
+
|
28
|
+
def verify(key, external_aad = nil)
|
29
|
+
tag == algorithm.mac(key.k, data(external_aad)) || raise(COSE::Error, "Mac0 verification failed")
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def data(external_aad = nil)
|
35
|
+
CBOR.encode([context, serialized_map(protected_headers), external_aad || zero_length_bin_string, payload])
|
36
|
+
end
|
37
|
+
|
38
|
+
def context
|
39
|
+
CONTEXT
|
40
|
+
end
|
18
41
|
end
|
19
42
|
end
|
data/lib/cose/recipient.rb
CHANGED
@@ -1,26 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "cbor"
|
4
|
+
require "cose/algorithm"
|
5
|
+
require "cose/error"
|
6
|
+
require "cose/security_message/headers"
|
2
7
|
|
3
8
|
module COSE
|
4
9
|
class SecurityMessage
|
10
|
+
ZERO_LENGTH_BIN_STRING = "".b
|
11
|
+
|
5
12
|
attr_reader :protected_headers, :unprotected_headers
|
6
13
|
|
7
14
|
def self.deserialize(cbor)
|
8
15
|
decoded = CBOR.decode(cbor)
|
9
16
|
|
10
|
-
if decoded.
|
17
|
+
if decoded.is_a?(CBOR::Tagged)
|
18
|
+
if respond_to?(:tag) && tag != decoded.tag
|
19
|
+
raise(COSE::Error, "Invalid CBOR tag")
|
20
|
+
end
|
21
|
+
|
11
22
|
decoded = decoded.value
|
12
23
|
end
|
13
24
|
|
25
|
+
from_array(decoded)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.from_array(array)
|
14
29
|
new(
|
15
|
-
protected_headers:
|
16
|
-
unprotected_headers:
|
17
|
-
**keyword_arguments_for_initialize(
|
30
|
+
protected_headers: deserialize_headers(array[0]),
|
31
|
+
unprotected_headers: array[1],
|
32
|
+
**keyword_arguments_for_initialize(array[2..-1])
|
18
33
|
)
|
19
34
|
end
|
20
35
|
|
36
|
+
def self.deserialize_headers(data)
|
37
|
+
if data == ZERO_LENGTH_BIN_STRING
|
38
|
+
{}
|
39
|
+
else
|
40
|
+
CBOR.decode(data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
21
44
|
def initialize(protected_headers:, unprotected_headers:)
|
22
45
|
@protected_headers = protected_headers
|
23
46
|
@unprotected_headers = unprotected_headers
|
24
47
|
end
|
48
|
+
|
49
|
+
def algorithm
|
50
|
+
@algorithm ||= COSE::Algorithm.find(headers.alg) || raise(COSE::Error, "Unsupported algorithm '#{headers.alg}'")
|
51
|
+
end
|
52
|
+
|
53
|
+
def headers
|
54
|
+
@headers ||= Headers.new(protected_headers, unprotected_headers)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def serialized_map(map)
|
60
|
+
if map && !map.empty?
|
61
|
+
map.to_cbor
|
62
|
+
else
|
63
|
+
zero_length_bin_string
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def zero_length_bin_string
|
68
|
+
ZERO_LENGTH_BIN_STRING
|
69
|
+
end
|
25
70
|
end
|
26
71
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module COSE
|
4
|
+
class SecurityMessage
|
5
|
+
class Headers
|
6
|
+
HEADER_LABEL_ALG = 1
|
7
|
+
HEADER_LABEL_KID = 4
|
8
|
+
|
9
|
+
attr_reader :protected_bucket, :unprotected_bucket
|
10
|
+
|
11
|
+
def initialize(protected_bucket, unprotected_bucket)
|
12
|
+
@protected_bucket = protected_bucket
|
13
|
+
@unprotected_bucket = unprotected_bucket
|
14
|
+
end
|
15
|
+
|
16
|
+
def alg
|
17
|
+
header(HEADER_LABEL_ALG)
|
18
|
+
end
|
19
|
+
|
20
|
+
def kid
|
21
|
+
header(HEADER_LABEL_KID)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def header(label)
|
27
|
+
protected_bucket[label] || unprotected_bucket[label]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/cose/sign.rb
CHANGED
@@ -1,13 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "cbor"
|
4
|
+
require "cose/error"
|
2
5
|
require "cose/security_message"
|
3
6
|
require "cose/signature"
|
4
7
|
|
5
8
|
module COSE
|
6
9
|
class Sign < SecurityMessage
|
10
|
+
CONTEXT = "Signature"
|
11
|
+
|
7
12
|
attr_reader :payload, :signatures
|
8
13
|
|
9
14
|
def self.keyword_arguments_for_initialize(decoded)
|
10
|
-
{ payload:
|
15
|
+
{ payload: decoded[0], signatures: decoded[1].map { |s| COSE::Signature.from_array(s) } }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.tag
|
19
|
+
98
|
11
20
|
end
|
12
21
|
|
13
22
|
def initialize(payload:, signatures:, **keyword_arguments)
|
@@ -16,5 +25,30 @@ module COSE
|
|
16
25
|
@payload = payload
|
17
26
|
@signatures = signatures
|
18
27
|
end
|
28
|
+
|
29
|
+
def verify(key, external_aad = nil)
|
30
|
+
signature = signatures.detect { |s| s.headers.kid == key.kid }
|
31
|
+
|
32
|
+
if signature
|
33
|
+
signature.algorithm.verify(key, signature.signature, verification_data(signature, external_aad))
|
34
|
+
else
|
35
|
+
raise(COSE::Error, "No signature matches key kid")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def verification_data(signature, external_aad = nil)
|
42
|
+
@verification_data ||=
|
43
|
+
CBOR.encode(
|
44
|
+
[
|
45
|
+
CONTEXT,
|
46
|
+
serialized_map(protected_headers),
|
47
|
+
serialized_map(signature.protected_headers),
|
48
|
+
external_aad || ZERO_LENGTH_BIN_STRING,
|
49
|
+
payload
|
50
|
+
]
|
51
|
+
)
|
52
|
+
end
|
19
53
|
end
|
20
54
|
end
|
data/lib/cose/sign1.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "cbor"
|
4
|
+
require "cose/error"
|
2
5
|
require "cose/security_message"
|
3
6
|
|
4
7
|
module COSE
|
5
8
|
class Sign1 < SecurityMessage
|
9
|
+
CONTEXT = "Signature1"
|
10
|
+
|
6
11
|
attr_reader :payload, :signature
|
7
12
|
|
8
13
|
def self.keyword_arguments_for_initialize(decoded)
|
9
|
-
{ payload:
|
14
|
+
{ payload: decoded[0], signature: decoded[1] }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.tag
|
18
|
+
18
|
10
19
|
end
|
11
20
|
|
12
21
|
def initialize(payload:, signature:, **keyword_arguments)
|
@@ -15,5 +24,19 @@ module COSE
|
|
15
24
|
@payload = payload
|
16
25
|
@signature = signature
|
17
26
|
end
|
27
|
+
|
28
|
+
def verify(key, external_aad = nil)
|
29
|
+
if key.kid == headers.kid
|
30
|
+
algorithm.verify(key, signature, verification_data(external_aad))
|
31
|
+
else
|
32
|
+
raise(COSE::Error, "Non matching kid")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def verification_data(external_aad = nil)
|
39
|
+
CBOR.encode([CONTEXT, serialized_map(protected_headers), external_aad || ZERO_LENGTH_BIN_STRING, payload])
|
40
|
+
end
|
18
41
|
end
|
19
42
|
end
|
data/lib/cose/signature.rb
CHANGED
data/lib/cose/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gonzalo Rodriguez
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-08-
|
12
|
+
date: 2019-08-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cbor
|
@@ -107,28 +107,28 @@ dependencies:
|
|
107
107
|
requirements:
|
108
108
|
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
110
|
+
version: 0.74.0
|
111
111
|
type: :development
|
112
112
|
prerelease: false
|
113
113
|
version_requirements: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - '='
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
117
|
+
version: 0.74.0
|
118
118
|
- !ruby/object:Gem::Dependency
|
119
119
|
name: rubocop-performance
|
120
120
|
requirement: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '1.
|
124
|
+
version: '1.4'
|
125
125
|
type: :development
|
126
126
|
prerelease: false
|
127
127
|
version_requirements: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '1.
|
131
|
+
version: '1.4'
|
132
132
|
description:
|
133
133
|
email:
|
134
134
|
- gonzalo@cedarcode.com
|
@@ -138,6 +138,7 @@ extensions: []
|
|
138
138
|
extra_rdoc_files: []
|
139
139
|
files:
|
140
140
|
- ".gitignore"
|
141
|
+
- ".gitmodules"
|
141
142
|
- ".rspec"
|
142
143
|
- ".rubocop.yml"
|
143
144
|
- ".travis.yml"
|
@@ -156,8 +157,15 @@ files:
|
|
156
157
|
- gemfiles/openssl_default.gemfile
|
157
158
|
- gemfiles/openssl_head.gemfile
|
158
159
|
- lib/cose.rb
|
160
|
+
- lib/cose/algorithm.rb
|
161
|
+
- lib/cose/algorithm/base.rb
|
162
|
+
- lib/cose/algorithm/ecdsa.rb
|
163
|
+
- lib/cose/algorithm/hmac.rb
|
164
|
+
- lib/cose/algorithm/rsa_pss.rb
|
165
|
+
- lib/cose/algorithm/signature_algorithm.rb
|
159
166
|
- lib/cose/encrypt.rb
|
160
167
|
- lib/cose/encrypt0.rb
|
168
|
+
- lib/cose/error.rb
|
161
169
|
- lib/cose/key.rb
|
162
170
|
- lib/cose/key/base.rb
|
163
171
|
- lib/cose/key/curve.rb
|
@@ -170,6 +178,7 @@ files:
|
|
170
178
|
- lib/cose/mac0.rb
|
171
179
|
- lib/cose/recipient.rb
|
172
180
|
- lib/cose/security_message.rb
|
181
|
+
- lib/cose/security_message/headers.rb
|
173
182
|
- lib/cose/sign.rb
|
174
183
|
- lib/cose/sign1.rb
|
175
184
|
- lib/cose/signature.rb
|
@@ -189,7 +198,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
189
198
|
requirements:
|
190
199
|
- - ">="
|
191
200
|
- !ruby/object:Gem::Version
|
192
|
-
version: '2.
|
201
|
+
version: '2.3'
|
193
202
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
203
|
requirements:
|
195
204
|
- - ">="
|