ruby-keychain 0.2.1 → 0.3.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
  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