akero 1.0.4 → 1.1.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 (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +3 -0
  3. data/.gitignore +0 -2
  4. data/.rubocop.yml +43 -0
  5. data/.travis.yml +3 -7
  6. data/Gemfile +1 -0
  7. data/Guardfile +4 -3
  8. data/Makefile +26 -0
  9. data/README.md +28 -10
  10. data/Rakefile +8 -17
  11. data/akero.gemspec +17 -10
  12. data/bin/akero +6 -0
  13. data/coverage/.last_run.json +5 -0
  14. data/coverage/.resultset.json +367 -0
  15. data/coverage/.resultset.json.lock +0 -0
  16. data/coverage/assets/0.10.0/application.css +799 -0
  17. data/coverage/assets/0.10.0/application.js +1707 -0
  18. data/coverage/assets/0.10.0/colorbox/border.png +0 -0
  19. data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
  20. data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
  21. data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
  22. data/coverage/assets/0.10.0/favicon_green.png +0 -0
  23. data/coverage/assets/0.10.0/favicon_red.png +0 -0
  24. data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
  25. data/coverage/assets/0.10.0/loading.gif +0 -0
  26. data/coverage/assets/0.10.0/magnify.png +0 -0
  27. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  28. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  29. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  30. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  31. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  32. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  33. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  34. data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  35. data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  36. data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  37. data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  38. data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  39. data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  40. data/coverage/assets/0.7.1/application.css +1110 -0
  41. data/coverage/assets/0.7.1/application.js +626 -0
  42. data/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
  43. data/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
  44. data/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
  45. data/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
  46. data/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
  47. data/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
  48. data/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
  49. data/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
  50. data/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
  51. data/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
  52. data/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
  53. data/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
  54. data/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
  55. data/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
  56. data/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
  57. data/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
  58. data/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
  59. data/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
  60. data/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
  61. data/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
  62. data/coverage/assets/0.7.1/favicon_green.png +0 -0
  63. data/coverage/assets/0.7.1/favicon_red.png +0 -0
  64. data/coverage/assets/0.7.1/favicon_yellow.png +0 -0
  65. data/coverage/assets/0.7.1/loading.gif +0 -0
  66. data/coverage/assets/0.7.1/magnify.png +0 -0
  67. data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  68. data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  69. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  70. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  71. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  72. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  73. data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  74. data/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  75. data/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
  76. data/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  77. data/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
  78. data/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
  79. data/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  80. data/coverage/index.html +2248 -0
  81. data/doc/Akero/Message.html +475 -0
  82. data/doc/Akero.html +1148 -0
  83. data/doc/_index.html +125 -0
  84. data/doc/class_list.html +53 -0
  85. data/doc/css/common.css +1 -0
  86. data/doc/css/full_list.css +57 -0
  87. data/doc/css/style.css +338 -0
  88. data/doc/file.README.html +186 -0
  89. data/doc/file_list.html +55 -0
  90. data/doc/frames.html +28 -0
  91. data/doc/index.html +186 -0
  92. data/doc/js/app.js +214 -0
  93. data/doc/js/full_list.js +173 -0
  94. data/doc/js/jquery.js +4 -0
  95. data/doc/method_list.html +148 -0
  96. data/doc/top-level-namespace.html +112 -0
  97. data/lib/akero/benchmark.rb +21 -20
  98. data/lib/akero/cli.rb +74 -0
  99. data/lib/akero/version.rb +2 -1
  100. data/lib/akero.rb +92 -90
  101. data/spec/akero_spec.rb +66 -65
  102. data/spec/spec_helper.rb +1 -0
  103. metadata +164 -52
data/lib/akero.rb CHANGED
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (c) 2012 moe@busyloop.net
2
- #
3
+ #
3
4
  # MIT License
4
- #
5
+ #
5
6
  # Permission is hereby granted, free of charge, to any person obtaining
6
7
  # a copy of this software and associated documentation files (the
7
8
  # "Software"), to deal in the Software without restriction, including
@@ -9,10 +10,10 @@
9
10
  # distribute, sublicense, and/or sell copies of the Software, and to
10
11
  # permit persons to whom the Software is furnished to do so, subject to
11
12
  # the following conditions:
12
- #
13
+ #
13
14
  # The above copyright notice and this permission notice shall be
14
15
  # included in all copies or substantial portions of the Software.
15
- #
16
+ #
16
17
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
18
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
19
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -21,15 +22,16 @@
21
22
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
23
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24
 
24
- require "akero/version"
25
+ require 'akero/version'
25
26
 
26
27
  require 'openssl'
27
28
  require 'base64'
28
29
 
29
30
  # Akero is an easy-to-use library for peer-to-peer public key cryptography.
30
31
  #
31
- # Tested on: MRI 1.8.7, MRI 1.9.2, MRI 1.9.3, RBX 1.8, RBX 1.9
32
- # (JRuby may be added in the future but is currently NOT supported)
32
+ # The only officially supported ruby runtime is MRI (latest version).
33
+ # Regardless, Akero is known to work on any recent ruby version except JRuby.
34
+ # Tested on: MRI 1.8.7, MRI 1.9.2, MRI 1.9.3, RBX 1.8, RBX 1.9, MRI 2.3.*, and more.
33
35
  class Akero
34
36
  # Akero::Message wraps a received message.
35
37
  class Message
@@ -40,7 +42,9 @@ class Akero
40
42
 
41
43
  # @private
42
44
  def initialize(body, signer_cert, type)
43
- @body, @signer_cert, @type = body, signer_cert, type
45
+ @body = body
46
+ @signer_cert = signer_cert
47
+ @type = type
44
48
  end
45
49
 
46
50
  # @private
@@ -62,25 +66,26 @@ class Akero
62
66
  end
63
67
  end
64
68
 
69
+ # Akero
65
70
  class Akero
66
- ERR_MSG_MALFORMED_ENV = "Malformed message: Could not parse envelope" # @private
67
- ERR_MSG_MALFORMED_BODY = "Malformed message: Could not parse body; POSSIBLE SPOOF ATTEMPT" # @private
68
- ERR_PKEY_CORRUPT = "Invalid private key (checksum mismatch)" # @private
69
- ERR_CERT_CORRUPT = "Invalid certificate" # @private
70
- ERR_INVALID_RECIPIENT = "Invalid recipient (must be a String)" # @private
71
- ERR_INVALID_RECIPIENT_CERT = "Invalid recipient (corrupt public key?)" # @private
72
- ERR_DECRYPT = "Could not decrypt message" # @private
73
- ERR_MSG_NOT_STRING_NOR_PKCS7 = "Message must be of type String or OpenSSL::PKCS7" # @private
74
- ERR_MSG_CORRUPT_CERT = "Malformed message: Embedded certificate could not be verified; POSSIBLE SPOOF ATTEMPT!" # @private
75
- ERR_MSG_TOO_MANY_SIGNERS = "Corrupt message: Zero or multiple signers, expected exactly 1; POSSIBLE SPOOF ATTEMPT" # @private
71
+ ERR_MSG_MALFORMED_ENV = 'Malformed message: Could not parse envelope' # @private
72
+ ERR_MSG_MALFORMED_BODY = 'Malformed message: Could not parse body; POSSIBLE SPOOF ATTEMPT' # @private
73
+ ERR_PKEY_CORRUPT = 'Invalid private key (checksum mismatch)' # @private
74
+ ERR_CERT_CORRUPT = 'Invalid certificate' # @private
75
+ ERR_INVALID_RECIPIENT = 'Invalid recipient (must be a String)' # @private
76
+ ERR_INVALID_RECIPIENT_CERT = 'Invalid recipient (corrupt public key?)' # @private
77
+ ERR_DECRYPT = 'Could not decrypt message' # @private
78
+ ERR_MSG_NOT_STRING_NOR_PKCS7 = 'Message must be of type String or OpenSSL::PKCS7' # @private
79
+ ERR_MSG_CORRUPT_CERT = 'Malformed message: Embedded certificate could not be verified; POSSIBLE SPOOF ATTEMPT!' # @private
80
+ ERR_MSG_TOO_MANY_SIGNERS = 'Corrupt message: Zero or multiple signers, expected exactly 1; POSSIBLE SPOOF ATTEMPT' # @private
76
81
 
77
82
  PKEY_HEADER = "-----BEGIN AKERO PRIVATE KEY-----\n" # @private
78
83
  PKEY_FOOTER = "-----END AKERO PRIVATE KEY-----\n" # @private
79
- PLATE_CERT = ['CERTIFICATE','AKERO PUBLIC KEY'] # @private
80
- PLATE_SIGNED = ['PKCS7', 'AKERO SIGNED MESSAGE'] # @private
81
- PLATE_CRYPTED = ['PKCS7', 'AKERO SECRET MESSAGE'] # @private
84
+ PLATE_CERT = ['CERTIFICATE', 'AKERO PUBLIC KEY'].freeze # @private
85
+ PLATE_SIGNED = ['PKCS7', 'AKERO SIGNED MESSAGE'].freeze # @private
86
+ PLATE_CRYPTED = ['PKCS7', 'AKERO SECRET MESSAGE'].freeze # @private
82
87
 
83
- DEFAULT_RSA_BITS = 2048
88
+ DEFAULT_RSA_BITS = 4096
84
89
  DEFAULT_DIGEST = OpenSSL::Digest::SHA512
85
90
 
86
91
  # Unique fingerprint of this Akero keypair.
@@ -104,26 +109,26 @@ class Akero
104
109
  # @param [Integer] rsa_bits RSA key length
105
110
  # @param [OpenSSL::Digest] digest Signature digest
106
111
  # @return [Akero] New Akero instance
107
- def initialize(rsa_bits=DEFAULT_RSA_BITS, digest=DEFAULT_DIGEST)
112
+ def initialize(rsa_bits = DEFAULT_RSA_BITS, digest = DEFAULT_DIGEST)
108
113
  @key, @cert = generate_keypair(rsa_bits, digest) unless rsa_bits.nil?
109
114
  end
110
115
 
111
116
  # Load an Akero identity.
112
- #
117
+ #
113
118
  # @example Load previously stored private key
114
119
  # Akero.load(File.read('/tmp/alice.akr'))
115
120
  #
116
121
  # @param [String] private_key Akero private key
117
122
  # @return [Akero] New Akero instance
118
123
  def self.load(private_key)
119
- inner = Base64.decode64(private_key[PKEY_HEADER.length..private_key.length-PKEY_FOOTER.length])
124
+ inner = Base64.decode64(private_key[PKEY_HEADER.length..private_key.length - PKEY_FOOTER.length])
120
125
  if inner[0..63] != OpenSSL::Digest::SHA512.new(inner[64..-1]).digest
121
- raise RuntimeError, ERR_PKEY_CORRUPT
126
+ raise ERR_PKEY_CORRUPT
122
127
  end
123
128
  cert_len = inner[64..65].unpack('S')[0]
124
129
  akero = Akero.new(nil)
125
- akero.instance_variable_set(:@cert, OpenSSL::X509::Certificate.new(inner[66..66+cert_len]))
126
- akero.instance_variable_set(:@key, OpenSSL::PKey::RSA.new(inner[66+cert_len..-1]))
130
+ akero.instance_variable_set(:@cert, OpenSSL::X509::Certificate.new(inner[66..66 + cert_len]))
131
+ akero.instance_variable_set(:@key, OpenSSL::PKey::RSA.new(inner[66 + cert_len..-1]))
127
132
  akero
128
133
  end
129
134
 
@@ -134,18 +139,18 @@ class Akero
134
139
  #
135
140
  # @return [String] Public key (ascii armored)
136
141
  def public_key
137
- Akero::replate(@cert.to_s, Akero::PLATE_CERT)
142
+ Akero.replate(@cert.to_s, Akero::PLATE_CERT)
138
143
  end
139
144
 
140
145
  # Private key (do not share this with anyone!)
141
- #
146
+ #
142
147
  # @example Save and load an Akero identity
143
148
  # alice = Akero.new
144
149
  # # Save
145
150
  # File.open('/tmp/alice.akr', 'w') { |f| f.write(alice.private_key) }
146
151
  # # Load
147
152
  # new_alice = Akero.load(File.read('/tmp/alice.akr'))
148
- #
153
+ #
149
154
  # @return [String] Private key (ascii armored)
150
155
  # @see Akero#load
151
156
  def private_key
@@ -156,7 +161,7 @@ class Akero
156
161
  out << cert_der
157
162
  out << @key.to_der
158
163
  out.insert(0, OpenSSL::Digest::SHA512.new(out).digest)
159
- PKEY_HEADER+Base64.encode64(out)+PKEY_FOOTER
164
+ PKEY_HEADER + Base64.encode64(out) + PKEY_FOOTER
160
165
  end
161
166
 
162
167
  # Sign a message.
@@ -164,7 +169,7 @@ class Akero
164
169
  # @param [String] plaintext The message to sign (binary safe)
165
170
  # @param [Boolean] ascii_armor Convert the output in base64?
166
171
  # @return [String] Akero signed message
167
- def sign(plaintext, ascii_armor=true)
172
+ def sign(plaintext, ascii_armor = true)
168
173
  out = _sign(plaintext)
169
174
  ascii_armor ? Akero.replate(out.to_s, Akero::PLATE_SIGNED) : out.to_der
170
175
  end
@@ -189,20 +194,20 @@ class Akero
189
194
  # @param [String] plaintext The message to encrypt (binary safe)
190
195
  # @param [Boolean] ascii_armor Convert the output to base64?
191
196
  # @return [String] Akero secret message
192
- def encrypt(to, plaintext, ascii_armor=true)
197
+ def encrypt(to, plaintext, ascii_armor = true)
193
198
  to = [to] unless to.is_a? Array
194
- to = to.map { |e|
199
+ to = to.map do |e|
195
200
  case e
196
- when String
197
- begin
198
- OpenSSL::X509::Certificate.new(Akero.replate(e, Akero::PLATE_CERT, true))
199
- rescue OpenSSL::X509::CertificateError
200
- raise RuntimeError, ERR_INVALID_RECIPIENT_CERT
201
- end
202
- else
203
- raise RuntimeError, ERR_INVALID_RECIPIENT
201
+ when String
202
+ begin
203
+ OpenSSL::X509::Certificate.new(Akero.replate(e, Akero::PLATE_CERT, true))
204
+ rescue OpenSSL::X509::CertificateError
205
+ raise ERR_INVALID_RECIPIENT_CERT
206
+ end
207
+ else
208
+ raise ERR_INVALID_RECIPIENT
204
209
  end
205
- }
210
+ end
206
211
  out = _sign(_encrypt(to, _sign(plaintext, false)))
207
212
  ascii_armor ? Akero.replate(out.to_s, PLATE_CRYPTED) : out.to_der
208
213
  end
@@ -218,21 +223,21 @@ class Akero
218
223
  begin
219
224
  body, signer_cert, body_type = verify(ciphertext, nil)
220
225
  rescue ArgumentError
221
- raise RuntimeError, ERR_MSG_MALFORMED_ENV
226
+ raise ERR_MSG_MALFORMED_ENV
222
227
  end
223
228
 
224
229
  case body_type.ord
225
- when 0x00
226
- # public message (signed)
227
- return Message.new(body, signer_cert, :signed)
228
- when 0x01
229
- # private message (signed, crypted, signed)
230
- signed_plaintext = _decrypt(body)
231
- plaintext, verified_cert, body_type = verify(signed_plaintext, signer_cert)
232
- msg = Message.new(plaintext, signer_cert, :encrypted)
233
- return msg
230
+ when 0x00
231
+ # public message (signed)
232
+ return Message.new(body, signer_cert, :signed)
233
+ when 0x01
234
+ # private message (signed, crypted, signed)
235
+ signed_plaintext = _decrypt(body)
236
+ plaintext, _verified_cert, _body_type = verify(signed_plaintext, signer_cert)
237
+ msg = Message.new(plaintext, signer_cert, :encrypted)
238
+ return msg
234
239
  end
235
- raise RuntimeError, ERR_MSG_MALFORMED_BODY
240
+ raise ERR_MSG_MALFORMED_BODY
236
241
  end
237
242
 
238
243
  # @private
@@ -244,9 +249,9 @@ class Akero
244
249
  def to_s
245
250
  inspect
246
251
  end
247
-
252
+
248
253
  #---------------------------------------------------------------------------
249
- protected
254
+ class << self; protected; end
250
255
 
251
256
  # Swap the "license plates" on an ascii-armored message.
252
257
  # This is done for user-friendliness, so stored Akero
@@ -256,9 +261,9 @@ class Akero
256
261
  # @param [Array] plates Array of the two plates to swap
257
262
  # @param [Boolean] reverse Reverse the swap?
258
263
  # @return [String] The replated message
259
- def self.replate(msg, plates, reverse=false)
260
- a,b = reverse ? [1,0] : [0,1]
261
- "-----BEGIN #{plates[b]}-----#{msg.strip[plates[a].length+16..-(plates[a].length+15)]}-----END #{plates[b]}-----\n"
264
+ def self.replate(msg, plates, reverse = false)
265
+ a, b = reverse ? [1, 0] : [0, 1]
266
+ "-----BEGIN #{plates[b]}-----#{msg.strip[plates[a].length + 16..-(plates[a].length + 15)]}-----END #{plates[b]}-----\n"
262
267
  end
263
268
 
264
269
  # Extract fingerprint from an Akero public key.
@@ -268,37 +273,35 @@ class Akero
268
273
  cert.extensions.map.each do |e|
269
274
  return "AK:#{e.value}" if e.oid == 'subjectKeyIdentifier'
270
275
  end
271
- raise RuntimeError, ERR_CERT_CORRUPT
276
+ raise ERR_CERT_CORRUPT
272
277
  end
273
-
278
+
274
279
  #---------------------------------------------------------------------------
275
- private
280
+ private
276
281
 
277
282
  def _decrypt(crypted_msg)
278
- begin
279
- OpenSSL::PKCS7.new(crypted_msg).decrypt(@key, @cert)
280
- rescue OpenSSL::PKCS7::PKCS7Error, "decrypt error"
281
- raise RuntimeError, ERR_DECRYPT
282
- end
283
+ OpenSSL::PKCS7.new(crypted_msg).decrypt(@key, @cert)
284
+ rescue OpenSSL::PKCS7::PKCS7Error, 'decrypt error'
285
+ raise ERR_DECRYPT
283
286
  end
284
287
 
285
- def _encrypt(to, msg, cipher=nil)
286
- cipher ||= OpenSSL::Cipher::new("AES-256-CFB")
287
- OpenSSL::PKCS7::encrypt(to, msg.to_der, cipher, OpenSSL::PKCS7::BINARY)
288
+ def _encrypt(to, msg, cipher = nil)
289
+ cipher ||= OpenSSL::Cipher.new('AES-256-CFB')
290
+ OpenSSL::PKCS7.encrypt(to, msg.to_der, cipher, OpenSSL::PKCS7::BINARY)
288
291
  end
289
292
 
290
- def _sign(message, embed_cert=true)
293
+ def _sign(message, embed_cert = true)
291
294
  flags = embed_cert ? OpenSSL::PKCS7::BINARY : (OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::NOCERTS)
292
295
  case message
293
- when String
294
- type = 0x00
295
- when OpenSSL::PKCS7
296
- type = 0x01
297
- else
298
- raise RuntimeError, ERR_MSG_NOT_STRING_NOR_PKCS7
296
+ when String
297
+ type = 0x00
298
+ when OpenSSL::PKCS7
299
+ type = 0x01
300
+ else
301
+ raise ERR_MSG_NOT_STRING_NOR_PKCS7
299
302
  end
300
303
  message = message.to_der if message.is_a? OpenSSL::PKCS7
301
- OpenSSL::PKCS7::sign(@cert, @key, type.chr + message, [], flags)
304
+ OpenSSL::PKCS7.sign(@cert, @key, type.chr + message, [], flags)
302
305
  end
303
306
 
304
307
  def verify(signed_msg, cert)
@@ -306,15 +309,15 @@ class Akero
306
309
  store = OpenSSL::X509::Store.new
307
310
 
308
311
  if cert.nil?
309
- if signed_msg.certificates.nil? or signed_msg.certificates.length != 1
310
- raise RuntimeError, ERR_MSG_TOO_MANY_SIGNERS
312
+ if signed_msg.certificates.nil? || signed_msg.certificates.length != 1
313
+ raise ERR_MSG_TOO_MANY_SIGNERS
311
314
  end
312
315
 
313
316
  cert = signed_msg.certificates[0]
314
317
  end
315
318
 
316
319
  unless signed_msg.verify([cert], store, nil, OpenSSL::PKCS7::NOINTERN | OpenSSL::PKCS7::NOVERIFY)
317
- raise RuntimeError, ERR_MSG_CORRUPT_CERT
320
+ raise ERR_MSG_CORRUPT_CERT
318
321
  end
319
322
 
320
323
  [signed_msg.data[1..-1], cert, signed_msg.data[0]]
@@ -325,7 +328,7 @@ class Akero
325
328
  # @param [Integer] rsa_bits RSA key length
326
329
  # @param [OpenSSL::Digest] digest Signature digest
327
330
  # @return [Array] rsa_keypair, certificate
328
- def generate_keypair(rsa_bits=DEFAULT_RSA_BITS, digest=DEFAULT_DIGEST)
331
+ def generate_keypair(rsa_bits = DEFAULT_RSA_BITS, digest = DEFAULT_DIGEST)
329
332
  cn = "Akero #{Akero::VERSION}"
330
333
  rsa = OpenSSL::PKey::RSA.new(rsa_bits)
331
334
 
@@ -337,20 +340,19 @@ class Akero
337
340
  cert.issuer = name
338
341
  cert.not_before = Time.now
339
342
  # valid until 2038-01-19 04:14:06 +0100
340
- cert.not_after = Time.at(2147483646)
343
+ cert.not_after = Time.at(2_147_483_646)
341
344
  cert.public_key = rsa.public_key
342
-
345
+
343
346
  ef = OpenSSL::X509::ExtensionFactory.new(nil, cert)
344
347
  ef.issuer_certificate = cert
345
348
  cert.extensions = [
346
- ef.create_extension("basicConstraints","CA:FALSE"),
347
- ef.create_extension("subjectKeyIdentifier", "hash"),
349
+ ef.create_extension('basicConstraints', 'CA:FALSE'),
350
+ ef.create_extension('subjectKeyIdentifier', 'hash')
348
351
  ]
349
- aki = ef.create_extension("authorityKeyIdentifier",
350
- "keyid:always,issuer:always")
352
+ aki = ef.create_extension('authorityKeyIdentifier',
353
+ 'keyid:always,issuer:always')
351
354
  cert.add_extension(aki)
352
355
  cert.sign(rsa, digest.new)
353
356
  [rsa, cert]
354
357
  end
355
358
  end
356
-
data/spec/akero_spec.rb CHANGED
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
  require 'openssl'
3
4
 
4
5
  describe Akero do
5
6
  subject { Akero.new(1024) }
6
7
  describe '#new' do
7
- it "returns instance with unique fingerprint" do
8
+ it 'returns instance with unique fingerprint' do
8
9
  memo = {}
9
10
  10.times do
10
11
  id = Akero.new.id
@@ -15,7 +16,7 @@ describe Akero do
15
16
  end
16
17
 
17
18
  describe '#id' do
18
- it "returns a String that looks like an Akero fingerprint" do
19
+ it 'returns a String that looks like an Akero fingerprint' do
19
20
  subject.id.should be_a String
20
21
  subject.id.should match /^AK(:([a-fA-Z0-9]){2}){20}$/
21
22
  end
@@ -23,13 +24,13 @@ describe Akero do
23
24
 
24
25
  describe '#private_key' do
25
26
  describe 'return value' do
26
- it "is a String that looks like an Akero private key" do
27
+ it 'is a String that looks like an Akero private key' do
27
28
  subject.private_key.should be_a String
28
29
  subject.private_key.should match /^#{Akero::PKEY_HEADER}/
29
30
  subject.private_key.should match /\n#{Akero::PKEY_FOOTER}$/
30
31
  end
31
32
 
32
- it "can be loaded by Akero" do
33
+ it 'can be loaded by Akero' do
33
34
  bob = Akero.load(subject.private_key)
34
35
  bob.id.should == subject.id
35
36
  bob.public_key.should == subject.public_key
@@ -39,29 +40,29 @@ describe Akero do
39
40
 
40
41
  describe '#public_key' do
41
42
  describe 'return value' do
42
- it "is a String that looks like an Akero public key" do
43
+ it 'is a String that looks like an Akero public key' do
43
44
  subject.public_key.should be_a String
44
45
  subject.public_key.should match /^-----BEGIN #{Akero::PLATE_CERT[1]}-----\n/
45
46
  subject.public_key.should match /\n-----END #{Akero::PLATE_CERT[1]}-----\n$/
46
47
  end
47
48
 
48
- it "raises RuntimeError when trying to load as private key" do
49
- lambda {
49
+ it 'raises RuntimeError when trying to load as private key' do
50
+ lambda do
50
51
  bob = Akero.load(subject.public_key)
51
52
  bob.id.should == subject.id
52
53
  bob.public_key.should == subject.public_key
53
- }.should raise_error RuntimeError, Akero::ERR_PKEY_CORRUPT
54
+ end.should raise_error RuntimeError, Akero::ERR_PKEY_CORRUPT
54
55
  end
55
56
  end
56
57
  end
57
58
 
58
59
  describe '#sign' do
59
- ([true, false]).each do |ascii_armor|
60
+ [true, false].each do |ascii_armor|
60
61
  describe "ascii_armor=#{ascii_armor}" do
61
62
  describe 'return value' do
62
63
  if ascii_armor
63
- it "is a String that looks like an Akero signed message" do
64
- plaintext = "Hello world!"
64
+ it 'is a String that looks like an Akero signed message' do
65
+ plaintext = 'Hello world!'
65
66
  signed_msg = subject.sign(plaintext, ascii_armor)
66
67
  signed_msg.should be_a String
67
68
  signed_msg.should match /^-----BEGIN #{Akero::PLATE_SIGNED[1]}-----\n/
@@ -69,8 +70,8 @@ describe Akero do
69
70
  end
70
71
  end
71
72
 
72
- it "contains valid signature" do
73
- plaintext = "Hello world!"
73
+ it 'contains valid signature' do
74
+ plaintext = 'Hello world!'
74
75
  signed_msg = subject.sign(plaintext, ascii_armor)
75
76
  bob = Akero.new
76
77
  msg = bob.receive(signed_msg)
@@ -85,12 +86,12 @@ describe Akero do
85
86
  end
86
87
 
87
88
  describe '#encrypt' do
88
- ([true, false]).each do |ascii_armor|
89
+ [true, false].each do |ascii_armor|
89
90
  describe "ascii_armor=#{ascii_armor}" do
90
91
  describe 'return value' do
91
92
  if ascii_armor
92
- it "is a String that looks like an Akero secret message" do
93
- plaintext = "Hello world!"
93
+ it 'is a String that looks like an Akero secret message' do
94
+ plaintext = 'Hello world!'
94
95
  ciphertext = subject.encrypt(subject.public_key, plaintext, ascii_armor)
95
96
  ciphertext.should be_a String
96
97
  ciphertext.should match /^-----BEGIN #{Akero::PLATE_CRYPTED[1]}-----\n/
@@ -98,25 +99,25 @@ describe Akero do
98
99
  end
99
100
  end
100
101
 
101
- it "raises RuntimeError on invalid recipient (invalid public key)" do
102
- lambda {
103
- msg = "Hello world!"
102
+ it 'raises RuntimeError on invalid recipient (invalid public key)' do
103
+ lambda do
104
+ msg = 'Hello world!'
104
105
  ciphertext = subject.encrypt([subject.public_key, 'foo'], msg)
105
- }.should raise_error RuntimeError, Akero::ERR_INVALID_RECIPIENT_CERT
106
+ end.should raise_error RuntimeError, Akero::ERR_INVALID_RECIPIENT_CERT
106
107
  end
107
108
 
108
- it "raises RuntimeError on invalid recipient (wrong type)" do
109
- lambda {
110
- msg = "Hello world!"
109
+ it 'raises RuntimeError on invalid recipient (wrong type)' do
110
+ lambda do
111
+ msg = 'Hello world!'
111
112
  ciphertext = subject.encrypt([subject.public_key, 42], msg)
112
- }.should raise_error RuntimeError, Akero::ERR_INVALID_RECIPIENT
113
+ end.should raise_error RuntimeError, Akero::ERR_INVALID_RECIPIENT
113
114
  end
114
115
 
115
- it "raises RuntimeError when message is not String" do
116
- lambda {
117
- msg = "Hello world!"
116
+ it 'raises RuntimeError when message is not String' do
117
+ lambda do
118
+ msg = 'Hello world!'
118
119
  ciphertext = subject.encrypt(subject.public_key, 42)
119
- }.should raise_error RuntimeError, Akero::ERR_MSG_NOT_STRING_NOR_PKCS7
120
+ end.should raise_error RuntimeError, Akero::ERR_MSG_NOT_STRING_NOR_PKCS7
120
121
  end
121
122
  end
122
123
  end
@@ -124,18 +125,18 @@ describe Akero do
124
125
  end
125
126
 
126
127
  describe '#receive' do
127
- ([true, false]).each do |ascii_armor|
128
+ [true, false].each do |ascii_armor|
128
129
  describe "ascii_armor=#{ascii_armor}" do
129
- it "decrypts message that was encrypted for self" do
130
- plaintext = "Hello world!"
130
+ it 'decrypts message that was encrypted for self' do
131
+ plaintext = 'Hello world!'
131
132
  ciphertext = subject.encrypt(subject.public_key, plaintext, ascii_armor)
132
133
  msg = subject.receive(ciphertext)
133
134
  msg.body.should == plaintext
134
135
  msg.type.should == :encrypted
135
136
  end
136
137
 
137
- it "decrypts message that was encrypted for self and other recipients" do
138
- plaintext = "Hello world!"
138
+ it 'decrypts message that was encrypted for self and other recipients' do
139
+ plaintext = 'Hello world!'
139
140
  alice = Akero.new
140
141
  bob = Akero.new
141
142
  ciphertext = subject.encrypt([alice.public_key, subject.public_key, bob.public_key], plaintext, ascii_armor)
@@ -144,20 +145,20 @@ describe Akero do
144
145
  msg.type.should == :encrypted
145
146
  end
146
147
 
147
- it "fails to decrypt message that was encrypted only for other recipients" do
148
- lambda {
149
- plaintext = "Hello world!"
148
+ it 'fails to decrypt message that was encrypted only for other recipients' do
149
+ lambda do
150
+ plaintext = 'Hello world!'
150
151
  alice = Akero.new
151
152
  bob = Akero.new
152
153
  ciphertext = subject.encrypt([alice.public_key, bob.public_key], plaintext, ascii_armor)
153
154
  msg = subject.receive(ciphertext)
154
155
  msg.body.should == plaintext
155
156
  msg.type.should == :encrypted
156
- }.should raise_error RuntimeError, Akero::ERR_DECRYPT
157
+ end.should raise_error RuntimeError, Akero::ERR_DECRYPT
157
158
  end
158
159
 
159
- it "extracts signature from signed message" do
160
- plaintext = "Hello world!"
160
+ it 'extracts signature from signed message' do
161
+ plaintext = 'Hello world!'
161
162
  alice = Akero.new
162
163
  signed_msg = subject.sign(plaintext, ascii_armor)
163
164
  msg = alice.receive(signed_msg)
@@ -165,82 +166,82 @@ describe Akero do
165
166
  msg.type.should == :signed
166
167
  end
167
168
 
168
- it "raises RuntimeError on invalid message" do
169
- lambda {
170
- subject.receive("foobar")
171
- }.should raise_error RuntimeError #, Akero::ERR_MSG_MALFORMED_ENV
169
+ it 'raises RuntimeError on invalid message' do
170
+ lambda do
171
+ subject.receive('foobar')
172
+ end.should raise_error RuntimeError # , Akero::ERR_MSG_MALFORMED_ENV
172
173
  end
173
174
 
174
- it "raises RuntimeError when payload does not match envelope signature" do
175
- lambda {
175
+ it 'raises RuntimeError when payload does not match envelope signature' do
176
+ lambda do
176
177
  oscar = Akero.new
177
178
  raw_key = subject.send(:instance_variable_get, '@cert')
178
179
  a = subject.send(:_encrypt, [raw_key], subject.send(:_sign, 'foobar'))
179
180
  b = oscar.send(:_sign, a)
180
181
  c = ascii_armor ? Akero.replate(b.to_s, Akero::PLATE_CRYPTED) : b.to_der
181
182
  subject.receive(c)
182
- }.should raise_error RuntimeError, Akero::ERR_MSG_CORRUPT_CERT
183
+ end.should raise_error RuntimeError, Akero::ERR_MSG_CORRUPT_CERT
183
184
  end
184
185
 
185
- it "raises RuntimeError on malformed inner message" do
186
- lambda {
186
+ it 'raises RuntimeError on malformed inner message' do
187
+ lambda do
187
188
  key, cert = subject.send(:generate_keypair, 1024)
188
- env = OpenSSL::PKCS7::sign(cert, key, 0xff.chr, [], OpenSSL::PKCS7::BINARY)
189
+ env = OpenSSL::PKCS7.sign(cert, key, 0xff.chr, [], OpenSSL::PKCS7::BINARY)
189
190
  broken_msg = Akero.replate(env.to_s, Akero::PLATE_CRYPTED)
190
191
  subject.receive(broken_msg)
191
- }.should raise_error RuntimeError, Akero::ERR_MSG_MALFORMED_BODY
192
+ end.should raise_error RuntimeError, Akero::ERR_MSG_MALFORMED_BODY
192
193
  end
193
194
 
194
- it "raises RuntimeError on unsigned message" do
195
- lambda {
195
+ it 'raises RuntimeError on unsigned message' do
196
+ lambda do
196
197
  raw_key = subject.send(:instance_variable_get, '@cert')
197
- env = OpenSSL::PKCS7::encrypt([raw_key], 'foobar', OpenSSL::Cipher::new("AES-256-CFB"), OpenSSL::PKCS7::BINARY)
198
+ env = OpenSSL::PKCS7.encrypt([raw_key], 'foobar', OpenSSL::Cipher.new('AES-256-CFB'), OpenSSL::PKCS7::BINARY)
198
199
  broken_msg = Akero.replate(env.to_s, Akero::PLATE_CRYPTED)
199
200
  subject.receive(broken_msg)
200
- }.should raise_error RuntimeError, Akero::ERR_MSG_TOO_MANY_SIGNERS
201
+ end.should raise_error RuntimeError, Akero::ERR_MSG_TOO_MANY_SIGNERS
201
202
  end
202
203
  end
203
204
  end
204
205
  end
205
-
206
+
206
207
  describe '#verify' do
207
- it "raises RuntimeError when embedded certificate can not be verified" do
208
- lambda {
208
+ it 'raises RuntimeError when embedded certificate can not be verified' do
209
+ lambda do
209
210
  fake_msg = mock('fake_msg')
210
211
  fake_msg.stub(:verify).and_return(false)
211
212
  fake_msg.stub_chain(:certificates, :length).and_return(1)
212
213
  fake_msg.stub_chain(:certificates, :[]).and_return(nil)
213
214
  subject.send(:verify, fake_msg, nil)
214
- }.should raise_error RuntimeError, Akero::ERR_MSG_CORRUPT_CERT
215
+ end.should raise_error RuntimeError, Akero::ERR_MSG_CORRUPT_CERT
215
216
  end
216
217
  end
217
218
 
218
219
  describe '#inspect' do
219
- it "returns a summary String" do
220
+ it 'returns a summary String' do
220
221
  s = subject.inspect
221
222
  s.should match /id=AK:/
222
223
  end
223
224
  end
224
225
 
225
226
  describe '#to_s' do
226
- it "returns the same value as #inspect" do
227
+ it 'returns the same value as #inspect' do
227
228
  subject.to_s.should == subject.inspect
228
229
  end
229
230
  end
230
-
231
+
231
232
  describe '.fingerprint_from_cert' do
232
- it "raises RuntimeError on invalid cert" do
233
+ it 'raises RuntimeError on invalid cert' do
233
234
  mock_cert = mock('mock_cert')
234
235
  mock_cert.stub_chain(:extensions, :map, :each).and_return(nil)
235
- lambda {
236
+ lambda do
236
237
  Akero.fingerprint_from_cert(mock_cert)
237
- }.should raise_error RuntimeError, Akero::ERR_CERT_CORRUPT
238
+ end.should raise_error RuntimeError, Akero::ERR_CERT_CORRUPT
238
239
  end
239
240
  end
240
241
 
241
242
  describe Akero::Message do
242
243
  describe '#inspect' do
243
- it "returns a summary String" do
244
+ it 'returns a summary String' do
244
245
  signed_msg = subject.sign('')
245
246
  msg = subject.receive(signed_msg)
246
247
  s = msg.inspect
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'simplecov'
2
3
  SimpleCov.start
3
4