ruby-keychain 0.2.1 → 0.3.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
  SHA1:
3
- metadata.gz: 7aa43e0604719f92cc6fbb6384cbd834d676e2f0
4
- data.tar.gz: 44f35722ea27e51690af50eaa7ac39fbc47ac299
3
+ metadata.gz: f322cf88ad0e198a1c6a869188ae692bae81af38
4
+ data.tar.gz: c26a017ef9ed99821844d0943b697367b10caff6
5
5
  SHA512:
6
- metadata.gz: 74e1561b0e7484444dcba43867552aab54fa050d5751dafdd4c342d1b946358df987bc4d22413825e939e7381b9e8711828f668618ce039d68e15b55f810a815
7
- data.tar.gz: 8c1fe492ea5bd3ec6e1188a7d0a9635949545e4d0972684f470cace3bda273004a10a468875fc423bbd248b94144130821c65e91481036cf553049c1899cc85a
6
+ metadata.gz: 7c3375d692838ca66ed6b89cccae65e93ee738ead6371bac4b32e9facd7407f157189f3288d75f19c91719dc5f62a44eba5f3e4d5da223e605b775591fe89ae5
7
+ data.tar.gz: f309869ceeeb3b9f9a3294089ba1f40c3c80fda4c41a6ffaab997f5274391a383f02325992107b65af2ffe0f8467b4ccdde40eab81f10651a1b8407ca7dcd018
@@ -18,8 +18,7 @@ end
18
18
  class Keychain::Certificate < Sec::Base
19
19
  register_type 'SecCertificate'
20
20
 
21
- ATTR_MAP = {CF::Base.typecast(Sec::kSecAttrAccessible) => :accessible,
22
- CF::Base.typecast(Sec::kSecAttrAccessGroup) => :access_group,
21
+ ATTR_MAP = {CF::Base.typecast(Sec::kSecAttrAccessGroup) => :access_group,
23
22
  CF::Base.typecast(Sec::kSecAttrCertificateType) => :certificate_type,
24
23
  CF::Base.typecast(Sec::kSecAttrCertificateEncoding) => :certificate_encoding,
25
24
  CF::Base.typecast(Sec::kSecAttrLabel) => :label,
@@ -29,6 +28,7 @@ class Keychain::Certificate < Sec::Base
29
28
  CF::Base.typecast(Sec::kSecAttrSubjectKeyID) => :subject_key_id,
30
29
  CF::Base.typecast(Sec::kSecAttrPublicKeyHash) => :public_key_hash}
31
30
 
31
+ ATTR_MAP[CF::Base.typecast(Sec::kSecAttrAccessible)] = :accessible if defined?(Sec::kSecAttrAccessible)
32
32
  ATTR_MAP[CF::Base.typecast(Sec::kSecAttrAccessControl)] = :access_control if defined?(Sec::kSecAttrAccessControl)
33
33
 
34
34
  INVERSE_ATTR_MAP = ATTR_MAP.invert
@@ -43,7 +43,7 @@ class Keychain::Certificate < Sec::Base
43
43
  status = Sec.SecCertificateCopyPublicKey(self, key_ref)
44
44
  Sec.check_osstatus(status)
45
45
 
46
- Keychain::Key.new(key_ref.read_pointer)
46
+ Keychain::Key.new(key_ref.read_pointer).release_on_gc
47
47
  end
48
48
 
49
49
  def x509
@@ -25,7 +25,7 @@ class Keychain::Identity < Sec::Base
25
25
  status = Sec.SecIdentityCopyCertificate(self, certificate_ref)
26
26
  Sec.check_osstatus(status)
27
27
 
28
- Keychain::Certificate.new(certificate_ref.read_pointer)
28
+ Keychain::Certificate.new(certificate_ref.read_pointer).release_on_gc
29
29
  end
30
30
 
31
31
  def private_key
@@ -33,7 +33,7 @@ class Keychain::Identity < Sec::Base
33
33
  status = Sec.SecIdentityCopyPrivateKey(self, key_ref)
34
34
  Sec.check_osstatus(status)
35
35
 
36
- Keychain::Key.new(key_ref.read_pointer)
36
+ Keychain::Key.new(key_ref.read_pointer).release_on_gc
37
37
  end
38
38
 
39
39
  def pkcs12(passphrase='')
@@ -84,7 +84,7 @@ class Keychain::Item < Sec::Base
84
84
  Sec::Query::CLASS => self.klass,
85
85
  Sec::Query::RETURN_DATA => true}.to_cf, out_buffer)
86
86
  Sec.check_osstatus(status)
87
- CF::Base.typecast(out_buffer.read_pointer).to_s
87
+ CF::Base.typecast(out_buffer.read_pointer).release_on_gc.to_s
88
88
  end
89
89
 
90
90
  # Attempts to update the keychain with any changes made to the item
@@ -1,5 +1,8 @@
1
1
  module Sec
2
- attach_variable 'kSecAttrAccessible', :pointer
2
+ begin
3
+ attach_variable 'kSecAttrAccessible', :pointer
4
+ rescue FFI::NotFoundError #Only available in 10.9
5
+ end
3
6
 
4
7
  begin
5
8
  attach_variable 'kSecAttrAccessControl', :pointer
@@ -56,13 +59,18 @@ module Sec
56
59
  end
57
60
 
58
61
  attach_function 'SecItemExport', [:pointer, :SecExternalFormat, :SecItemImportExportFlags, :pointer, :pointer], :osstatus
62
+ attach_function 'SecItemImport', [:pointer, :pointer,
63
+ :SecExternalFormat,
64
+ :SecExternalItemType,
65
+ :SecItemImportExportFlags,
66
+ :pointer, :pointer, :pointer], :osstatus
67
+
59
68
  end
60
69
 
61
70
  class Keychain::Key < Sec::Base
62
71
  register_type 'SecKey'
63
72
 
64
- ATTR_MAP = {CF::Base.typecast(Sec::kSecAttrAccessible) => :accessible,
65
- CF::Base.typecast(Sec::kSecAttrAccessGroup) => :access_group,
73
+ ATTR_MAP = {CF::Base.typecast(Sec::kSecAttrAccessGroup) => :access_group,
66
74
  CF::Base.typecast(Sec::kSecAttrKeyClass) => :key_class,
67
75
  CF::Base.typecast(Sec::kSecAttrLabel) => :label,
68
76
  CF::Base.typecast(Sec::kSecAttrApplicationLabel) => :application_label,
@@ -79,6 +87,7 @@ class Keychain::Key < Sec::Base
79
87
  CF::Base.typecast(Sec::kSecAttrCanWrap) => :can_wrap,
80
88
  CF::Base.typecast(Sec::kSecAttrCanUnwrap) => :can_unwrap}
81
89
 
90
+ ATTR_MAP[CF::Base.typecast(Sec::kSecAttrAccessible)] = :accessible if defined?(Sec::kSecAttrAccessible)
82
91
  ATTR_MAP[CF::Base.typecast(Sec::kSecAttrAccessControl)] = :access_control if defined?(Sec::kSecAttrAccessControl)
83
92
 
84
93
  INVERSE_ATTR_MAP = ATTR_MAP.invert
@@ -1,5 +1,15 @@
1
1
 
2
2
  module Sec
3
+ enum :SecExternalItemType, [:kSecItemTypeUnknown ,
4
+ :kSecItemTypePrivateKey,
5
+ :kSecItemTypePublicKey,
6
+ :kSecItemTypeSessionKey,
7
+ :kSecItemTypeCertificate,
8
+ :kSecItemTypeAggregate]
9
+
10
+ attach_function 'SecAccessCreate', [:pointer, :pointer, :pointer], :osstatus
11
+ attach_function 'SecTrustedApplicationCreateFromPath', [:string, :pointer], :osstatus
12
+
3
13
  attach_function 'SecKeychainCopyDefault', [:pointer], :osstatus
4
14
  attach_function 'SecKeychainDelete', [:keychainref], :osstatus
5
15
  attach_function 'SecKeychainOpen', [:string, :pointer], :osstatus
@@ -19,6 +29,14 @@ module Sec
19
29
  :lock_interval, :uint32
20
30
  end
21
31
 
32
+ class Keychain::TrustedApplication < Sec::Base
33
+ register_type 'SecTrustedApplication'
34
+ end
35
+
36
+ class Keychain::Access < Sec::Base
37
+ register_type 'SecAccess'
38
+ end
39
+
22
40
  attach_function 'SecKeychainSetSettings', [:keychainref, KeychainSettings], :osstatus
23
41
  attach_function 'SecKeychainCopySettings', [:keychainref, KeychainSettings], :osstatus
24
42
 
@@ -50,7 +68,7 @@ module Keychain
50
68
  list = FFI::MemoryPointer.new(:pointer)
51
69
  status = Sec.SecKeychainCopySearchList(list)
52
70
  Sec.check_osstatus(status)
53
- ruby_list = CF::Base.typecast(list.read_pointer).to_ruby
71
+ ruby_list = CF::Base.typecast(list.read_pointer).release_on_gc.to_ruby
54
72
  ruby_list << self unless ruby_list.include?(self)
55
73
  status = Sec.SecKeychainSetSearchList(CF::Array.immutable(ruby_list))
56
74
  Sec.check_osstatus(status)
@@ -102,6 +120,39 @@ module Keychain
102
120
  Scope.new(Sec::Classes::GENERIC, self)
103
121
  end
104
122
 
123
+ # Imports item from string or file to this keychain
124
+ #
125
+ # @param [IO, String] input IO object or String with raw data to import
126
+ # @param [Array <String>] app_list List of applications which will be
127
+ # permitted to access imported items
128
+ # @return [Array <SecKeychainItem>] List of imported keychain objects,
129
+ # each of which may be a SecCertificate, SecKey, or SecIdentity instance
130
+ def import(input, app_list=[])
131
+ input = input.read if input.is_a? IO
132
+
133
+ # Create array of TrustedApplication objects
134
+ trusted_apps = get_trusted_apps(app_list)
135
+
136
+ # Create an Access object
137
+ access_buffer = FFI::MemoryPointer.new(:pointer)
138
+ status = Sec.SecAccessCreate(path.to_cf, trusted_apps, access_buffer)
139
+ Sec.check_osstatus status
140
+ access = CF::Base.typecast(access_buffer.read_pointer)
141
+
142
+ key_params = Sec::SecItemImportExportKeyParameters.new
143
+ key_params[:accessRef] = access
144
+
145
+ # Import item to the keychain
146
+ cf_data = CF::Data.from_string(input).release_on_gc
147
+ cf_array = FFI::MemoryPointer.new(:pointer)
148
+ status = Sec.SecItemImport(cf_data, nil, :kSecFormatUnknown, :kSecItemTypeUnknown, :kSecItemPemArmour, key_params, self, cf_array)
149
+ access.release
150
+ Sec.check_osstatus status
151
+ item_array = CF::Base.typecast(cf_array.read_pointer).release_on_gc
152
+
153
+ item_array.to_ruby
154
+ end
155
+
105
156
  # returns a description of the keychain
106
157
  # @return [String]
107
158
  def inspect
@@ -198,6 +249,17 @@ module Keychain
198
249
  settings
199
250
  end
200
251
 
252
+ def get_trusted_apps apps
253
+ trusted_app_array = apps.map do |path|
254
+ trusted_app_buffer = FFI::MemoryPointer.new(:pointer)
255
+ status = Sec.SecTrustedApplicationCreateFromPath(
256
+ path.encode(Encoding::UTF_8), trusted_app_buffer)
257
+ Sec.check_osstatus(status)
258
+ CF::Base.typecast(trusted_app_buffer.read_pointer).release_on_gc
259
+ end
260
+ trusted_app_array.to_cf
261
+ end
262
+
201
263
  def put_settings settings
202
264
  status = Sec.SecKeychainSetSettings(self, settings)
203
265
  Sec.check_osstatus status
@@ -163,7 +163,7 @@ module Sec
163
163
  Sec::Query::RETURN_REF => false}.to_cf, result)
164
164
  Sec.check_osstatus(status)
165
165
 
166
- cf_dict = CF::Base.typecast(result.read_pointer)
166
+ cf_dict = CF::Base.typecast(result.read_pointer).release_on_gc
167
167
  update_self_from_dictionary(cf_dict)
168
168
  end
169
169
  end
@@ -1,4 +1,4 @@
1
1
  module Keychain
2
2
  # The current version string
3
- VERSION = '0.2.1'
3
+ VERSION = '0.3.0'
4
4
  end
@@ -57,6 +57,28 @@ describe Keychain do
57
57
  end
58
58
  end
59
59
 
60
+ describe 'import' do
61
+ before(:all) do
62
+ @keychain = Keychain.create(File.join(Dir.tmpdir, "keychain_spec_#{Time.now.to_i}_#{Time.now.usec}_#{rand(1000)}.keychain"), 'pass')
63
+ @rsa_key = OpenSSL::PKey::RSA.new(2048).to_s
64
+ end
65
+
66
+ it 'should import item to the keychain' do
67
+ imported_key = @keychain.import(@rsa_key, ['/usr/bin/codesign']).first
68
+ imported_key.load_attributes
69
+ found_key = Keychain::Scope.new(Sec::Classes::KEY, @keychain).all.first
70
+ expect(imported_key.attributes).to eq(found_key.attributes)
71
+ end
72
+
73
+ it 'should raise an exception for duplicated item' do
74
+ expect { @keychain.import(@rsa_key) }.to raise_error(Keychain::DuplicateItemError)
75
+ end
76
+
77
+ after(:all) do
78
+ @keychain.delete
79
+ end
80
+ end
81
+
60
82
  describe 'exists?' do
61
83
  context 'the keychain exists' do
62
84
  it 'should return true' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-keychain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frederick Cheung
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-15 00:00:00.000000000 Z
11
+ date: 2015-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -44,20 +44,20 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.1'
47
+ version: '3.3'
48
48
  - - ">="
49
49
  - !ruby/object:Gem::Version
50
- version: 3.1.0
50
+ version: 3.3.0
51
51
  type: :development
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - "~>"
56
56
  - !ruby/object:Gem::Version
57
- version: '3.1'
57
+ version: '3.3'
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 3.1.0
60
+ version: 3.3.0
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: rake
63
63
  requirement: !ruby/object:Gem::Requirement