cose 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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