cose 0.4.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 421ef632c61005a43efc9b8c9fff3560d7b22386bd3ab8ff0a556ac57a338eba
4
- data.tar.gz: '0912bd03e39ca678d6a46b6d1f7b046593a656b754235dd584b59242c9fe34d2'
3
+ metadata.gz: eb978af65e0e7ba43a06e776b45792856bec76e58a7ce6d1d3d88619c6fca09e
4
+ data.tar.gz: c8cdf73b87523e266c36076c542cf91bb5e252bda826523308b4adc355ce1c6d
5
5
  SHA512:
6
- metadata.gz: aaae536b461341f4b79960a66c261709510887ab8690083473b41ca45ec8952c19e1d298fc716abbbf8973790ca82d78ee967673a58332ea2998fbcfe771819f
7
- data.tar.gz: 026774ddd5ea2fa6f23a6d7866e22f87a880aa802af380733b8bfdf62efbb0a808a4c07d287935b31fcda31544e6855a7686f6aee1b4640180cf0591abd780bd
6
+ metadata.gz: bc8f10f840915de994963318cf3fb5f138eb836bd391280029cceb7b5b4c9a95577dd281be1d67f1dc36def4a5a809af6e4ff665dc26e0cf30990219248cf043
7
+ data.tar.gz: 87ec47c8278246a0fdbec217a1d13b6588aed62400bfd49010d4573dab75b70a436867fe8d4fb895b784bbe29783c28e2910f576f213aeda8d830662f1067f7f
data/.gitignore CHANGED
@@ -11,5 +11,6 @@
11
11
  .rspec_status
12
12
 
13
13
  /Gemfile.lock
14
+ /gemfiles/*.gemfile.lock
14
15
 
15
16
  .byebug_history
data/.travis.yml CHANGED
@@ -1,10 +1,16 @@
1
1
  sudo: false
2
2
  language: ruby
3
+
3
4
  rvm:
4
5
  - ruby-head
5
- - 2.6.1
6
- - 2.5.3
6
+ - 2.6.2
7
+ - 2.5.4
7
8
  - 2.4.5
9
+
10
+ gemfile:
11
+ - gemfiles/openssl_2_0.gemfile
12
+ - gemfiles/openssl_2_1.gemfile
13
+
8
14
  before_install: gem install bundler -v 2.0.1
9
15
 
10
16
  matrix:
data/Appraisals ADDED
@@ -0,0 +1,7 @@
1
+ appraise "openssl_2_0" do
2
+ gem "openssl", "~> 2.0.0"
3
+ end
4
+
5
+ appraise "openssl_2_1" do
6
+ gem "openssl", "~> 2.1.0"
7
+ end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.5.0] - 2019-03-25
4
+
5
+ ### Added
6
+
7
+ - `COSE::Key.serialize(openssl_pkey)` serializes an `OpenSSL::PKey::PKey` object into CBOR data. Supports RSA keys plus
8
+ EC keys from curves prime256v1, secp384r1 and secp521r1.
9
+ - `COSE::Key::EC2#to_pkey` converts to an `OpenSSL::PKey::EC` object
10
+ - `COSE::Key::RSA#to_pkey` converts to an `OpenSSL::PKey::RSA` object
11
+
3
12
  ## [v0.4.1] - 2019-03-12
4
13
 
5
14
  ### Fixed
@@ -45,6 +54,7 @@
45
54
  - EC2 key object
46
55
  - Works with ruby 2.5
47
56
 
57
+ [v0.5.0]: https://github.com/cedarcode/cose-ruby/compare/v0.4.1...v0.5.0/
48
58
  [v0.4.1]: https://github.com/cedarcode/cose-ruby/compare/v0.4.0...v0.4.1/
49
59
  [v0.4.0]: https://github.com/cedarcode/cose-ruby/compare/v0.3.0...v0.4.0/
50
60
  [v0.3.0]: https://github.com/cedarcode/cose-ruby/compare/v0.2.0...v0.3.0/
data/README.md CHANGED
@@ -25,12 +25,23 @@ Or install it yourself as:
25
25
 
26
26
  ### Key Objects
27
27
 
28
+ #### Deserialization (from CBOR to Ruby objects)
29
+
28
30
  ```ruby
29
31
  cbor_data = "..."
30
32
 
31
- key = COSE::Key.deserialize(cbor_data)
33
+ cose_key = COSE::Key.deserialize(cbor_data)
34
+ ```
35
+
36
+ Once you have a `COSE::Key` instance you can either access key parameters directly and/or convert it to an
37
+ `OpenSSL::PKey::PKey` instance for operating with it (encrypting/decrypting, signing/verifying, etc).
38
+
39
+ ```ruby
40
+ # Convert to an OpenSSL::PKey::PKey
41
+ openssl_pkey = cose_key.to_pkey
32
42
 
33
- case key.class
43
+ # Access COSE key parameters
44
+ case key
34
45
  when COSE::Key::EC2
35
46
  key.curve
36
47
  key.x_coordinate
@@ -39,43 +50,73 @@ when COSE::Key::EC2
39
50
  when COSE::Key::RSA
40
51
  key.modulus_n
41
52
  key.public_exponent_e
53
+ key.private_exponent_d
54
+ key.prime_factor_p
55
+ key.prime_factor_q
56
+ key.d_p
57
+ key.d_q
58
+ key.q_inv
42
59
  when COSE::Key::Symmetric
43
60
  key.key_value
44
61
  end
45
62
  ```
46
63
 
47
- #### EC2
64
+ If you already know which COSE key type is encoded in the CBOR data, then:
48
65
 
49
66
  ```ruby
50
- cbor_data = "..."
67
+ ec2_key_cbor = "..."
68
+
69
+ cose_ec2_key = COSE::Key::EC2.deserialize(ec2_key_cbor)
70
+
71
+ cose_ec2_key.curve
72
+ cose_ec2_key.x_coordinate
73
+ cose_ec2_key.y_coordinate
74
+ cose_ec2_key.d_coordinate
51
75
 
52
- key = COSE::Key::EC2.deserialize(cbor_data)
76
+ # or
53
77
 
54
- key.curve
55
- key.x_coordinate
56
- key.y_coordinate
57
- key.d_coordinate
78
+ ec_pkey = cose_ec2_key.to_pkey # Instance of an OpenSSL::PKey::EC
58
79
  ```
59
80
 
60
- #### Symmetric
81
+ ```ruby
82
+ symmetric_key_cbor = "..."
83
+
84
+ cose_symmetric_key = COSE::Key::Symmetric.deserialize(symmetric_key_cbor)
85
+
86
+ cose_symmetric_key.key_value
87
+ ```
61
88
 
62
89
  ```ruby
63
- cbor_data = "..."
90
+ rsa_key_cbor = "..."
91
+
92
+ cose_rsa_key = COSE::Key::RSA.deserialize(rsa_key_cbor)
93
+
94
+ cose_rsa_key.modulus_n
95
+ cose_rsa_key.public_exponent_e
96
+ cose_rsa_key.private_exponent_d
97
+ cose_rsa_key.prime_factor_p
98
+ cose_rsa_key.prime_factor_q
99
+ cose_rsa_key.d_p
100
+ cose_rsa_key.d_q
101
+ cose_rsa_key.q_inv
64
102
 
65
- key = COSE::Key::Symmetric.deserialize(cbor_data)
103
+ # or
66
104
 
67
- key.key_value
105
+ rsa_pkey = cose_rsa_key.to_pkey # Instance of an OpenSSL::PKey::RSA
68
106
  ```
69
107
 
70
- #### RSA
108
+ #### Serialization (from Ruby objects to CBOR)
71
109
 
72
110
  ```ruby
73
- cbor_data = "..."
111
+ ec_pkey = OpenSSL::PKey::EC.new("prime256v1").generate_key
112
+
113
+ cose_ec2_key_cbor = COSE::Key.serialize(ec_pkey)
114
+ ```
74
115
 
75
- key = COSE::Key::RSA.deserialize(cbor_data)
116
+ ```ruby
117
+ rsa_pkey = OpenSSL::PKey::RSA.new(2048)
76
118
 
77
- key.modulus_n
78
- key.public_exponent_e
119
+ cose_rsa_key_cbor = COSE::Key.serialize(rsa_pkey)
79
120
  ```
80
121
 
81
122
  ### Signing Objects
data/cose.gemspec CHANGED
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
 
34
34
  spec.add_dependency "cbor", "~> 0.5.9.2"
35
35
 
36
+ spec.add_development_dependency "appraisal", "~> 2.2.0"
36
37
  spec.add_development_dependency "bundler", ">= 1.17", "< 3"
37
38
  spec.add_development_dependency "byebug", "~> 11.0"
38
39
  spec.add_development_dependency "rake", "~> 12.3"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "openssl", "~> 2.0.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "openssl", "~> 2.1.0"
6
+
7
+ gemspec path: "../"
data/lib/cose/key.rb CHANGED
@@ -2,11 +2,23 @@ require "cbor"
2
2
  require "cose/key/ec2"
3
3
  require "cose/key/rsa"
4
4
  require "cose/key/symmetric"
5
+ require "openssl"
5
6
 
6
7
  module COSE
7
8
  class UnknownKeyType < StandardError; end
8
9
 
9
10
  module Key
11
+ def self.serialize(pkey)
12
+ case pkey
13
+ when OpenSSL::PKey::EC, OpenSSL::PKey::EC::Point
14
+ COSE::Key::EC2.from_pkey(pkey).serialize
15
+ when OpenSSL::PKey::RSA
16
+ COSE::Key::RSA.from_pkey(pkey).serialize
17
+ else
18
+ raise "Unsupported serialization of #{pkey.class} object"
19
+ end
20
+ end
21
+
10
22
  def self.deserialize(data)
11
23
  map = CBOR.decode(data)
12
24
 
data/lib/cose/key/ec2.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "cose/key/base"
4
+ require "openssl"
4
5
 
5
6
  module COSE
6
7
  module Key
@@ -13,6 +14,44 @@ module COSE
13
14
  Y_LABEL = -3
14
15
 
15
16
  KTY_EC2 = 2
17
+ CRV_P256 = 1
18
+ CRV_P384 = 2
19
+ CRV_P521 = 3
20
+
21
+ PKEY_CURVES = {
22
+ CRV_P256 => "prime256v1",
23
+ CRV_P384 => "secp384r1",
24
+ CRV_P521 => "secp521r1"
25
+ }.freeze
26
+
27
+ def self.from_pkey(pkey)
28
+ curve = PKEY_CURVES.key(pkey.group.curve_name) || raise("Unsupported EC curve #{pkey.group.curve_name}")
29
+
30
+ case pkey
31
+ when OpenSSL::PKey::EC::Point
32
+ public_key = pkey
33
+ when OpenSSL::PKey::EC
34
+ public_key = pkey.public_key
35
+ private_key = pkey.private_key
36
+ else
37
+ raise "Unsupported"
38
+ end
39
+
40
+ if public_key
41
+ bytes = public_key.to_bn.to_s(2)[1..-1]
42
+
43
+ coordinate_length = bytes.size / 2
44
+
45
+ x_coordinate = bytes[0..(coordinate_length - 1)]
46
+ y_coordinate = bytes[coordinate_length..-1]
47
+ end
48
+
49
+ if private_key
50
+ d_coordinate = private_key.to_s(2)
51
+ end
52
+
53
+ new(curve: curve, x_coordinate: x_coordinate, y_coordinate: y_coordinate, d_coordinate: d_coordinate)
54
+ end
16
55
 
17
56
  attr_reader :algorithm, :curve, :d_coordinate, :x_coordinate, :y_coordinate
18
57
 
@@ -32,6 +71,34 @@ module COSE
32
71
  end
33
72
  end
34
73
 
74
+ def serialize
75
+ CBOR.encode(
76
+ Base::LABEL_KTY => KTY_EC2,
77
+ CRV_LABEL => curve,
78
+ X_LABEL => x_coordinate,
79
+ Y_LABEL => y_coordinate,
80
+ D_LABEL => d_coordinate
81
+ )
82
+ end
83
+
84
+ def to_pkey
85
+ if PKEY_CURVES[curve]
86
+ group = OpenSSL::PKey::EC::Group.new(PKEY_CURVES[curve])
87
+ pkey = OpenSSL::PKey::EC.new(group)
88
+ public_key_bn = OpenSSL::BN.new("\x04" + x_coordinate + y_coordinate, 2)
89
+ public_key_point = OpenSSL::PKey::EC::Point.new(group, public_key_bn)
90
+ pkey.public_key = public_key_point
91
+
92
+ if d_coordinate
93
+ pkey.private_key = OpenSSL::BN.new(d_coordinate, 2)
94
+ end
95
+
96
+ pkey
97
+ else
98
+ raise "Unsupported curve #{curve}"
99
+ end
100
+ end
101
+
35
102
  def self.from_map(map)
36
103
  enforce_type(map, KTY_EC2, "Not an EC2 key")
37
104
 
data/lib/cose/key/rsa.rb CHANGED
@@ -1,18 +1,65 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "cose/key/base"
4
+ require "openssl"
4
5
 
5
6
  module COSE
6
7
  module Key
7
8
  class RSA < Base
8
9
  LABEL_N = -1
9
10
  LABEL_E = -2
11
+ LABEL_D = -3
12
+ LABEL_P = -4
13
+ LABEL_Q = -5
14
+ LABEL_D_P = -6
15
+ LABEL_D_Q = -7
16
+ LABEL_Q_INV = -8
10
17
 
11
18
  KTY_RSA = 3
12
19
 
13
- attr_reader :modulus_n, :public_exponent_e
20
+ def self.from_pkey(pkey)
21
+ params = pkey.params
14
22
 
15
- def initialize(modulus_n:, public_exponent_e:)
23
+ attributes = {
24
+ modulus_n: params["n"].to_s(2),
25
+ public_exponent_e: params["e"].to_s(2)
26
+ }
27
+
28
+ if pkey.private?
29
+ attributes.merge!(
30
+ private_exponent_d: params["d"].to_s(2),
31
+ prime_factor_p: params["p"].to_s(2),
32
+ prime_factor_q: params["q"].to_s(2),
33
+ d_p: params["dmp1"].to_s(2),
34
+ d_q: params["dmq1"].to_s(2),
35
+ q_inv: params["iqmp"].to_s(2)
36
+ )
37
+ end
38
+
39
+ new(attributes)
40
+ end
41
+
42
+ attr_reader(
43
+ :modulus_n,
44
+ :public_exponent_e,
45
+ :private_exponent_d,
46
+ :prime_factor_p,
47
+ :prime_factor_q,
48
+ :d_p,
49
+ :d_q,
50
+ :q_inv
51
+ )
52
+
53
+ def initialize(
54
+ modulus_n:,
55
+ public_exponent_e:,
56
+ private_exponent_d: nil,
57
+ prime_factor_p: nil,
58
+ prime_factor_q: nil,
59
+ d_p: nil,
60
+ d_q: nil,
61
+ q_inv: nil
62
+ )
16
63
  if !modulus_n
17
64
  raise ArgumentError, "Required modulus_n is missing"
18
65
  elsif !public_exponent_e
@@ -20,14 +67,50 @@ module COSE
20
67
  else
21
68
  @modulus_n = modulus_n
22
69
  @public_exponent_e = public_exponent_e
70
+ @private_exponent_d = private_exponent_d
71
+ @prime_factor_p = prime_factor_p
72
+ @prime_factor_q = prime_factor_q
73
+ @d_p = d_p
74
+ @d_q = d_q
75
+ @q_inv = q_inv
23
76
  end
24
77
  end
25
78
 
79
+ def serialize
80
+ CBOR.encode(
81
+ Base::LABEL_KTY => KTY_RSA,
82
+ LABEL_N => modulus_n,
83
+ LABEL_E => public_exponent_e,
84
+ LABEL_D => private_exponent_d,
85
+ LABEL_P => prime_factor_p,
86
+ LABEL_Q => prime_factor_q,
87
+ LABEL_D_P => d_p,
88
+ LABEL_D_Q => d_q,
89
+ LABEL_Q_INV => q_inv
90
+ )
91
+ end
92
+
93
+ def to_pkey
94
+ pkey = OpenSSL::PKey::RSA.new
95
+
96
+ pkey.set_key(bn(modulus_n), bn(public_exponent_e), bn(private_exponent_d))
97
+ pkey.set_factors(bn(prime_factor_p), bn(prime_factor_q))
98
+ pkey.set_crt_params(bn(d_p), bn(d_q), bn(q_inv))
99
+
100
+ pkey
101
+ end
102
+
26
103
  def self.from_map(map)
27
104
  enforce_type(map, KTY_RSA, "Not an RSA key")
28
105
 
29
106
  new(modulus_n: map[LABEL_N], public_exponent_e: map[LABEL_E])
30
107
  end
108
+
109
+ private
110
+
111
+ def bn(data)
112
+ OpenSSL::BN.new(data, 2)
113
+ end
31
114
  end
32
115
  end
33
116
  end
data/lib/cose/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module COSE
4
- VERSION = "0.4.1"
4
+ VERSION = "0.5.0"
5
5
  end
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.1
4
+ version: 0.5.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-03-13 00:00:00.000000000 Z
12
+ date: 2019-03-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: cbor
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: 0.5.9.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: appraisal
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 2.2.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 2.2.0
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: bundler
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -113,6 +127,7 @@ files:
113
127
  - ".rspec"
114
128
  - ".rubocop.yml"
115
129
  - ".travis.yml"
130
+ - Appraisals
116
131
  - CHANGELOG.md
117
132
  - Gemfile
118
133
  - LICENSE.txt
@@ -121,6 +136,8 @@ files:
121
136
  - bin/console
122
137
  - bin/setup
123
138
  - cose.gemspec
139
+ - gemfiles/openssl_2_0.gemfile
140
+ - gemfiles/openssl_2_1.gemfile
124
141
  - lib/cose.rb
125
142
  - lib/cose/encrypt.rb
126
143
  - lib/cose/encrypt0.rb