cyberplat_pki 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - rbx-19mode
5
+ - jruby-19mode
6
+ script:
7
+ - ruby -w -S rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cyberplat_pki.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Peter Zotov <whitequark@whitequark.org>
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,47 @@
1
+ # CyberplatPKI
2
+
3
+ CyberplatPKI is an FFI binding for signing Cyberplat requests. It includes both Linux and Windows versions of Cyberplat-provided library as well as the necessary wrapping code.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cyberplat_pki'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cyberplat_pki
18
+
19
+ ## Usage
20
+
21
+ To sign:
22
+
23
+ ``` ruby
24
+ privkey_serialized = File.read("private.key")
25
+ privkey = CyberplatPKI::Key.new_private(privkey_serialized, 'passphrase')
26
+
27
+ signed_data = privkey.sign(data)
28
+ ```
29
+
30
+ To verify:
31
+
32
+ ``` ruby
33
+ pubkey_serialized = File.read("public.key")
34
+ pubkey = CyberplatPKI::Key.new_public(pubkey_serialized, 12345) # serial = 12345
35
+
36
+ pubkey.verify(signed_data) # => true
37
+ ```
38
+
39
+ Note that the library uses Windows line endings (`\r\n`) internally. You must not touch those, or the library will fail. Treat the signed data as binary, or make sure to restore the expected line endings.
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "cyberplat_pki"
7
+ gem.version = "1.0.0"
8
+ gem.authors = ["Peter Zotov"]
9
+ gem.email = ["whitequark@whitequark.org"]
10
+ gem.description = %q{CyberplatPKI is an FFI binding for signing Cyberplat requests.}
11
+ gem.summary = gem.description
12
+ gem.homepage = "http://github.com/whitequark/cyberplat_pki"
13
+
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_dependency "ffi"
20
+ gem.add_development_dependency "rspec"
21
+ end
Binary file
Binary file
@@ -0,0 +1,10 @@
1
+ module CyberplatPKI
2
+ require_relative 'cyberplat_pki/library'
3
+ require_relative 'cyberplat_pki/key'
4
+
5
+ Library.invoke :Initialize
6
+
7
+ at_exit {
8
+ Library.invoke :Done
9
+ }
10
+ end
@@ -0,0 +1,78 @@
1
+ module CyberplatPKI
2
+ class Key
3
+ attr_reader :internal
4
+
5
+ class << self
6
+ def new_private(source, password, engine=Library::DEFAULT_ENGINE)
7
+ internal = Library::Key.new
8
+
9
+ Library.invoke :OpenSecretKey,
10
+ engine,
11
+ source, source.length,
12
+ password,
13
+ internal
14
+
15
+ new(internal)
16
+ end
17
+
18
+ def new_public(source, serial, ca_key=nil, engine=Library::DEFAULT_ENGINE)
19
+ internal = Library::Key.new
20
+
21
+ if ca_key
22
+ ca_key = ca_key.internal
23
+ end
24
+
25
+ Library.invoke :OpenPublicKey,
26
+ engine,
27
+ source, source.length,
28
+ serial,
29
+ internal,
30
+ ca_key
31
+
32
+ new(internal)
33
+ end
34
+
35
+ private :new
36
+ end
37
+
38
+ def initialize(internal)
39
+ @internal = internal
40
+ if defined?(ObjectSpace) &&
41
+ ObjectSpace.respond_to?(:define_finalizer)
42
+ ObjectSpace.define_finalizer(self, lambda {
43
+ Library.invoke :CloseKey, internal
44
+ })
45
+ else
46
+ warn "No ObjectSpace.define_finalizer; Crypt_CloseKey will not be called."
47
+ end
48
+ end
49
+
50
+ def sign(data)
51
+ # Be fucking optimistic. Someone, please teach the morons from
52
+ # cyberplat how to design APIs and document them.
53
+ # I sincerely hope this does not segfault in production.
54
+ result = FFI::MemoryPointer.new(:char, data.length + 1024)
55
+
56
+ result_length = Library.invoke :Sign,
57
+ data, data.size,
58
+ result, result.total,
59
+ @internal
60
+
61
+ result.read_string(result_length)
62
+ end
63
+
64
+ def verify(data_with_signature)
65
+ retval = Library.Crypt_Verify \
66
+ data_with_signature, data_with_signature.size,
67
+ nil, nil,
68
+ @internal
69
+
70
+ if retval == -20 # VERIFY
71
+ false
72
+ else
73
+ Library.handle_error("Crypt_Verify", retval)
74
+ true
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,128 @@
1
+ require 'ffi'
2
+
3
+ module CyberplatPKI::Library
4
+ extend FFI::Library
5
+
6
+ if defined?(Rubinius) # Fuck you.
7
+ if Rubinius.windows?
8
+ tuple = "windows-"
9
+ elsif RUBY_PLATFORM =~ /linux/
10
+ tuple = "linux-"
11
+ else
12
+ tuple = "unknown-"
13
+ end
14
+
15
+ if FFI::Platform::ARCH =~ /x86_64/
16
+ tuple << "x86_64"
17
+ elsif FFI::Platform::ARCH =~ /(i.?|x)86/
18
+ tuple << "i386"
19
+ end
20
+ else
21
+ tuple = "#{FFI::Platform::OS}-#{FFI::Platform::ARCH}"
22
+ end
23
+
24
+ if tuple == 'windows-i386'
25
+ ffi_lib File.expand_path('../../../ext/libipriv32.dll', __FILE__)
26
+ elsif tuple == 'linux-i386'
27
+ ffi_lib File.expand_path('../../../ext/libipriv-linux32.so', __FILE__)
28
+ else
29
+ raise "CyberplatPKI: unsupported platform #{tuple}."
30
+ end
31
+
32
+ Errors = {
33
+ -1 => "BAD_ARGS",
34
+ -2 => "OUT_OF_MEMORY",
35
+ -3 => "INVALID_FORMAT",
36
+ -4 => "NO_DATA_FOUND",
37
+ -5 => "INVALID_PACKET_FORMAT",
38
+ -6 => "UNKNOWN_ALG",
39
+ -7 => "INVALID_KEYLEN",
40
+ -8 => "INVALID_PASSWD",
41
+ -9 => "DOCTYPE",
42
+ -10 => "RADIX_DECODE",
43
+ -11 => "RADIX_ENCODE",
44
+ -12 => "INVALID_ENG",
45
+ -13 => "ENG_NOT_READY",
46
+ -14 => "NOT_SUPPORT",
47
+ -15 => "FILE_NOT_FOUND",
48
+ -16 => "CANT_READ_FILE",
49
+ -17 => "INVALID_KEY",
50
+ -18 => "SEC_ENC",
51
+ -19 => "PUB_KEY_NOT_FOUND",
52
+ -20 => "VERIFY",
53
+ -21 => "CREATE_FILE",
54
+ -22 => "CANT_WRITE_FILE",
55
+ -23 => "INVALID_KEYCARD",
56
+ -24 => "GENKEY",
57
+ -25 => "PUB_ENC",
58
+ -26 => "SEC_DEC",
59
+ }
60
+
61
+ ENGINE_RSAREF = 0
62
+ ENGINE_OPENSSL = 1
63
+ ENGINE_PKCS11 = 2
64
+ ENGINE_WINCRYPT = 3
65
+
66
+ DEFAULT_ENGINE = ENGINE_RSAREF
67
+
68
+ ENGCMD_IS_READY = 0
69
+ ENGCMD_GET_ERROR = 1
70
+ ENGCMD_SET_PIN = 2
71
+ ENGCMD_SET_PKCS11_LIB = 3
72
+ ENGCMD_GET_PKCS11_SLOTS_NUM = 4
73
+ ENGCMD_GET_PKCS11_SLOT_NAME = 5
74
+ ENGCMD_SET_PKCS11_SLOT = 6
75
+ ENGCMD_ENUM_PKCS11_KEYS = 7
76
+ ENGCMD_ENUM_PKCS11_PUBKEYS = 8
77
+
78
+ KEY_TYPE_RSA_SECRET = 1
79
+ KEY_TYPE_RSA_PUBLIC = 2
80
+
81
+ MAX_USERID_LENGTH = 20
82
+
83
+ class Key < FFI::Struct
84
+ layout :eng, :short,
85
+ :type, :short,
86
+ :keyserial, :ulong,
87
+ :userid, [:char, 24],
88
+ :key, :pointer
89
+ end
90
+
91
+ # Only required functions are defined.
92
+ # For all functions, zero return code is success, nonzero is error.
93
+
94
+ # int Crypt_Initialize(void)
95
+ attach_function :Crypt_Initialize, [], :int
96
+
97
+ # int Crypt_OpenSecretKey(int eng, const char* src, int src_len, const char* password, IPRIV_KEY* key)
98
+ attach_function :Crypt_OpenSecretKey, [:int, :string, :int, :string, :pointer], :int
99
+
100
+ # int Crypt_OpenPublicKey(int eng, const char* src, int src_len, int keyserial, IPRIV_KEY* key, IPRIV_KEY* cakey)
101
+ attach_function :Crypt_OpenPublicKey, [:int, :string, :int, :int, :pointer, :pointer], :int
102
+
103
+ # int Crypt_Sign(const char* src, int src_len, char* dst, int dst_len, IPRIV_KEY* key);
104
+ attach_function :Crypt_Sign, [:string, :int, :pointer, :int, :pointer], :int
105
+
106
+ # int Crypt_Verify(const char* src, int src_len, const char** pdst, int* pndst,IPRIV_KEY* key);
107
+ attach_function :Crypt_Verify, [:string, :int, :pointer, :pointer, :pointer], :int
108
+
109
+ # int Crypt_CloseKey(IPRIV_KEY* key)
110
+ attach_function :Crypt_CloseKey, [:pointer], :int
111
+
112
+ # int Crypt_Done(void)
113
+ attach_function :Crypt_Done, [], :int
114
+
115
+ def self.handle_error(function, retval)
116
+ if retval < 0
117
+ error = Errors[retval] || "UNKNOWN"
118
+ raise "CyberplatPKI: Cannot invoke #{function}: #{error} (#{retval})"
119
+ end
120
+
121
+ retval
122
+ end
123
+
124
+ def self.invoke(function, *args)
125
+ function = :"Crypt_#{function}"
126
+ handle_error(function, send(function, *args))
127
+ end
128
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe CyberplatPKI::Key do
4
+ before do
5
+ @pubkey = File.read(File.expand_path('../keys/pubkeys.key', __FILE__))
6
+ @seckey = File.read(File.expand_path('../keys/secret.key', __FILE__))
7
+ end
8
+
9
+ it "should load public keys" do
10
+ lambda {
11
+ CyberplatPKI::Key.new_public(@pubkey, 17033)
12
+ }.should_not raise_error
13
+ end
14
+
15
+ it "fails loading public key from invalid source" do
16
+ lambda {
17
+ CyberplatPKI::Key.new_public(@pubkey, 12345)
18
+ }.should raise_error(/PUB_KEY_NOT_FOUND/)
19
+
20
+ lambda {
21
+ CyberplatPKI::Key.new_public("foo", 17033)
22
+ }.should raise_error(/PUB_KEY_NOT_FOUND/)
23
+ end
24
+
25
+ it "should load private keys" do
26
+ lambda {
27
+ CyberplatPKI::Key.new_private(@seckey, '1111111111')
28
+ }.should_not raise_error
29
+ end
30
+
31
+ it "fails loading private key from invalid source" do
32
+ lambda {
33
+ CyberplatPKI::Key.new_private("foo", '1111111111')
34
+ }.should raise_error(/INVALID_FORMAT/)
35
+ end
36
+
37
+ it "fails loading private key with invalid password" do
38
+ lambda {
39
+ CyberplatPKI::Key.new_private(@seckey, 'foo')
40
+ }.should raise_error(/INVALID_PASSWD/)
41
+ end
42
+
43
+ SIGNED = <<-SIGNED.gsub("\n", "\r\n").freeze
44
+ 0000027201SM000000110000001100000125
45
+ api17032 00017033
46
+ 00000000
47
+ BEGIN
48
+ Hello world
49
+ END
50
+ BEGIN SIGNATURE
51
+ iQBRAwkBAABCiVCdMQoBAcf2AfwOYzgQxyj1jwRv/6JdjCFh+lguLENscUFfaNXu
52
+ OIi4jaGbW8jFrxnUj5AaoeA/WJtFuBayNdBmyiQpeisngU6XsAHH
53
+ =z1vE
54
+ END SIGNATURE
55
+ SIGNED
56
+
57
+ it "can sign and then verify block" do
58
+ privkey = CyberplatPKI::Key.new_private(@seckey, '1111111111')
59
+ pubkey = CyberplatPKI::Key.new_public(@pubkey, 17033)
60
+
61
+ signed = privkey.sign("Hello world")
62
+ pubkey.verify(signed).should be_true
63
+ end
64
+
65
+ it "can verify a block" do
66
+ pubkey = CyberplatPKI::Key.new_public(@pubkey, 17033)
67
+
68
+ pubkey.verify(SIGNED).should be_true
69
+ end
70
+
71
+ it "fails verifying an invalid block" do
72
+ pubkey = CyberplatPKI::Key.new_public(@pubkey, 17033)
73
+
74
+ fail_signed = SIGNED.sub('world', 'wor1d')
75
+ pubkey.verify(fail_signed).should be_false
76
+ end
77
+ end
@@ -0,0 +1,28 @@
1
+ 0000040801NS000001470000010000000125
2
+ api17032 00017033
3
+ api17032 00017033
4
+ BEGIN
5
+ mQBRAwAAQok95z4+AAABAgDrzoyI24MItz/UdYrV7as4xrjhjpYqBG3Owb7dP1pE
6
+ p6Dz4MLJkdWzm+ccjy3pTmjgvqfnaAnRyID4nrwQ9+p9AAURsAGHtAhhcGkxNzAz
7
+ MrABAw==
8
+ =5jFd
9
+ END
10
+ BEGIN SIGNATURE
11
+ iQBRAwkQAABCiT3nPj4BAaxXAgDT6bfpnp513156n2O4H5nyp7LyH6jaR6NrOi1/
12
+ x3Bm+/rzl0mfBFMC8LTVF2ukQ4gX6meCwK+ZaFFdT8UJXVVUsAHH
13
+ =fOzH
14
+ END SIGNATURE
15
+ 0000040401NS000001430000009800000125
16
+ 0J0005 00064182
17
+ api17032 00017033
18
+ BEGIN
19
+ mQBRAwAA+rY8quyyAAABAgDb8iYDU760T8phaS1KFW4wj0cSROQ2dJzkJN5X64y0
20
+ azNAmCCyM9KzGU3M400isSEU+LddQG+HE/hbRsntAURTAAURsAGHtAYwSjAwMDWw
21
+ AQM=
22
+ =HcgR
23
+ END
24
+ BEGIN SIGNATURE
25
+ iQBRAwkQAABCiT3nPnEBAeYFAgCJWP1AnPJ46UrJny81UCv5EVMyaocqBRCbQW9S
26
+ 8bxvV/uixVcWwimyrGv8/aNc9bTeX9TacEELashrOTfXTUSIsAHH
27
+ =S9Y8
28
+ END SIGNATURE
@@ -0,0 +1,15 @@
1
+ 0000051701NM000003810000027300000000
2
+ api17032 00017033
3
+ 00000000
4
+ BEGIN
5
+ lQEEAwAAQok95z4+AAABAgDrzoyI24MItz/UdYrV7as4xrjhjpYqBG3Owb7dP1pE
6
+ p6Dz4MLJkdWzm+ccjy3pTmjgvqfnaAnRyID4nrwQ9+p9AAURATXU8D817k6vAfqv
7
+ qaNX3nRlR6EMHSyDSoMzeMYZ64D5OgHqIt+rnqRLqApwk5tP5ewscxfr6coACuF5
8
+ qLJAKmAtwHRZnY8cWgKzAQBMyV0nshDFbN7+biMSPGobWjhhQ8GlVfi1636/FZqe
9
+ TQEApdjYa7cCBMKNdJojykQ977wVZpcYzDZ0zIWBRhfLez0BAPTvT/ipmFxcjtGG
10
+ z0sFSYk7QVaXIoCIdugQbd4Z+iq8TPK0CGFwaTE3MDMy
11
+ =Uxun
12
+ END
13
+ BEGIN SIGNATURE
14
+
15
+ END SIGNATURE
@@ -0,0 +1,19 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
18
+
19
+ require 'cyberplat_pki'
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cyberplat_pki
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Peter Zotov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: CyberplatPKI is an FFI binding for signing Cyberplat requests.
47
+ email:
48
+ - whitequark@whitequark.org
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - .travis.yml
56
+ - Gemfile
57
+ - LICENSE.txt
58
+ - README.md
59
+ - Rakefile
60
+ - cyberplat_pki.gemspec
61
+ - ext/libipriv-linux32.so
62
+ - ext/libipriv32.dll
63
+ - lib/cyberplat_pki.rb
64
+ - lib/cyberplat_pki/key.rb
65
+ - lib/cyberplat_pki/library.rb
66
+ - spec/key_spec.rb
67
+ - spec/keys/pubkeys.key
68
+ - spec/keys/secret.key
69
+ - spec/spec_helper.rb
70
+ - vendor/linux.zip
71
+ - vendor/win32.zip
72
+ homepage: http://github.com/whitequark/cyberplat_pki
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.23
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: CyberplatPKI is an FFI binding for signing Cyberplat requests.
96
+ test_files:
97
+ - spec/key_spec.rb
98
+ - spec/keys/pubkeys.key
99
+ - spec/keys/secret.key
100
+ - spec/spec_helper.rb
101
+ has_rdoc: