cyberplat_pki 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -1
- data/cyberplat_pki.gemspec +6 -3
- data/lib/cyberplat_pki/document.rb +82 -0
- data/lib/cyberplat_pki/document_io_routines.rb +104 -0
- data/lib/cyberplat_pki/idea_cfb.rb +108 -0
- data/lib/cyberplat_pki/key.rb +124 -52
- data/lib/cyberplat_pki/key_id.rb +15 -0
- data/lib/cyberplat_pki/key_packet.rb +45 -0
- data/lib/cyberplat_pki/packet.rb +31 -0
- data/lib/cyberplat_pki/packet_io_routines.rb +120 -0
- data/lib/cyberplat_pki/private_key_packet.rb +49 -0
- data/lib/cyberplat_pki/signature_packet.rb +42 -0
- data/lib/cyberplat_pki/trust_packet.rb +17 -0
- data/lib/cyberplat_pki/user_id_packet.rb +17 -0
- data/lib/cyberplat_pki.rb +20 -7
- data/spec/key_spec.rb +1 -1
- metadata +34 -12
- data/ext/libipriv-linux32.so +0 -0
- data/ext/libipriv32.dll +0 -0
- data/lib/cyberplat_pki/library.rb +0 -128
- data/vendor/linux.zip +0 -0
- data/vendor/win32.zip +0 -0
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# CyberplatPKI
|
2
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.
|
3
|
+
CyberplatPKI 1.0 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. Note that this version only works on 32-bit Linux and Windows.
|
4
|
+
|
5
|
+
CyberplatPKI 2.0 is a pure-Ruby reimplementation of the reverse-engineered signing algorithm. It should be completely compatible with the vendor-provided library. This version works everywhere.
|
6
|
+
|
7
|
+
Select the variant you'd like to install with the version specification. `gem 'cyberplat_pki', '~> 1.0'` requests the FFI version, and `gem 'cyberplat_pki', '~> 2.0'` requests the pure-Ruby one.
|
4
8
|
|
5
9
|
## Installation
|
6
10
|
|
data/cyberplat_pki.gemspec
CHANGED
@@ -4,10 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = "cyberplat_pki"
|
7
|
-
gem.version = "
|
7
|
+
gem.version = "2.0.0"
|
8
8
|
gem.authors = ["Peter Zotov"]
|
9
9
|
gem.email = ["whitequark@whitequark.org"]
|
10
|
-
gem.description = %q{CyberplatPKI is
|
10
|
+
gem.description = %q{CyberplatPKI is a library for signing Cyberplat requests.}
|
11
11
|
gem.summary = gem.description
|
12
12
|
gem.homepage = "http://github.com/whitequark/cyberplat_pki"
|
13
13
|
|
@@ -16,6 +16,9 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
17
|
gem.require_paths = ["lib"]
|
18
18
|
|
19
|
-
gem.add_dependency "ffi"
|
20
19
|
gem.add_development_dependency "rspec"
|
20
|
+
gem.add_dependency 'digest-crc' # For CRC24
|
21
|
+
gem.add_dependency 'crypt' # For IDEA
|
22
|
+
gem.add_dependency "jruby-openssl" if RUBY_PLATFORM == "java"
|
23
|
+
gem.add_dependency "openssl" if RUBY_PLATFORM == "ruby"
|
21
24
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "stringio"
|
2
|
+
require "base64"
|
3
|
+
require "digest/crc24"
|
4
|
+
|
5
|
+
module CyberplatPKI
|
6
|
+
class Document
|
7
|
+
attr_accessor :engine, :type, :subject, :ca, :body, :signature, :data_length
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@engine = nil
|
11
|
+
@type = nil
|
12
|
+
@subject = nil
|
13
|
+
@ca = nil
|
14
|
+
@body = nil
|
15
|
+
@signature = nil
|
16
|
+
@unknown1 = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.load(source)
|
20
|
+
source = source.sub /^[ \t\n\r]*/, ''
|
21
|
+
|
22
|
+
io = StringIO.new source, "rb"
|
23
|
+
io.extend DocumentIORoutines
|
24
|
+
|
25
|
+
documents = []
|
26
|
+
|
27
|
+
until io.eof?
|
28
|
+
begin
|
29
|
+
documents << io.read_document
|
30
|
+
rescue EOFError => e
|
31
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (unexpected end of document)"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
documents
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.save(documents)
|
39
|
+
io = StringIO.new '', "wb"
|
40
|
+
io.extend DocumentIORoutines
|
41
|
+
|
42
|
+
documents.each { |document| io.write_document document }
|
43
|
+
|
44
|
+
io.string[0...-2] # Strip trailing CRLF of last document
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.encode64(data)
|
48
|
+
encoded = Base64.encode64(data).gsub /\n/, "\r\n"
|
49
|
+
|
50
|
+
crc = Digest::CRC24.checksum data
|
51
|
+
|
52
|
+
encoded << "=#{Base64.encode64([ crc ].pack("N")[1..-1])[0..-2]}"
|
53
|
+
|
54
|
+
encoded
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.decode64(data)
|
58
|
+
lines = data.split "\r\n"
|
59
|
+
|
60
|
+
data = ""
|
61
|
+
crc = nil
|
62
|
+
|
63
|
+
lines.each do |line|
|
64
|
+
if line[0] == '='
|
65
|
+
crc, = "\0".concat(Base64.decode64(line[1..-1])).unpack('N')
|
66
|
+
else
|
67
|
+
data << line
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
data = Base64.decode64 data
|
72
|
+
|
73
|
+
if !crc.nil?
|
74
|
+
calculated_crc = Digest::CRC24.checksum data
|
75
|
+
|
76
|
+
raise "CyberplatPKI: CRYPT_ERR_RADIX_DECODE (invalid data checksum)" if calculated_crc != crc
|
77
|
+
end
|
78
|
+
|
79
|
+
data
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module CyberplatPKI
|
2
|
+
module DocumentIORoutines
|
3
|
+
def readline_crlf
|
4
|
+
line = readline "\r\n"
|
5
|
+
|
6
|
+
line.sub! /\r\n$/, ''
|
7
|
+
|
8
|
+
line
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_key_id
|
12
|
+
line = readline_crlf
|
13
|
+
|
14
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (invalid key ID line: '#{line}'. Expecting 28 characters, got #{line.length})" unless line.length == 28
|
15
|
+
|
16
|
+
KeyId.new line[0...20].rstrip!, cut_int(line, 20...28)
|
17
|
+
end
|
18
|
+
|
19
|
+
def write_key_id(key_id)
|
20
|
+
printf "%-20s%08d\r\n", key_id.key_name, key_id.key_serial
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_block(header, trailer, data_size)
|
24
|
+
line = readline_crlf
|
25
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (unexpected header '#{line}', expecting '#{header}')" if line != header
|
26
|
+
|
27
|
+
data_start = pos
|
28
|
+
seek data_size, IO::SEEK_CUR
|
29
|
+
|
30
|
+
line = readline_crlf
|
31
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (unexpected trailer '#{line}', expecting '')" if line != ''
|
32
|
+
|
33
|
+
line = readline_crlf
|
34
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (unexpected trailer '#{line}', expecting '#{trailer}')" if line != trailer
|
35
|
+
|
36
|
+
block_end = tell
|
37
|
+
|
38
|
+
seek data_start
|
39
|
+
data = read data_size
|
40
|
+
seek block_end
|
41
|
+
|
42
|
+
data
|
43
|
+
end
|
44
|
+
|
45
|
+
def write_block(header, trailer, data)
|
46
|
+
write "#{header}\r\n#{data}\r\n#{trailer}\r\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
def read_document
|
50
|
+
document_start = pos
|
51
|
+
|
52
|
+
header = readline_crlf
|
53
|
+
|
54
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (invalid document header: '#{header}'. Expecting 36 characters, got #{header.length})" unless header.length == 36
|
55
|
+
|
56
|
+
doc = Document.new
|
57
|
+
|
58
|
+
length = cut_int header, 0...8 # Length of following data (e.g. document length - 8)
|
59
|
+
doc.engine = cut_int header, 8...10 # Engine ID
|
60
|
+
doc.type = header[10...12].to_sym # Document type
|
61
|
+
body_block = cut_int header, 12...20 # Body block size
|
62
|
+
doc.data_length = cut_int header, 20...28 # Length of body in signatures
|
63
|
+
signature_block = cut_int header, 28...36 # Signature block size
|
64
|
+
|
65
|
+
doc.subject = read_key_id
|
66
|
+
doc.ca = read_key_id
|
67
|
+
|
68
|
+
doc.body = read_block 'BEGIN', 'END', body_block
|
69
|
+
doc.signature = read_block 'BEGIN SIGNATURE', 'END SIGNATURE', signature_block
|
70
|
+
|
71
|
+
seek document_start + 8 + length + 2
|
72
|
+
|
73
|
+
doc
|
74
|
+
end
|
75
|
+
|
76
|
+
def write_document(doc)
|
77
|
+
start_pos = pos
|
78
|
+
|
79
|
+
printf "XXXXXXXX%02d%s%08d%08d%08d\r\n", doc.engine, doc.type.to_s, doc.body.length, doc.data_length, doc.signature.length
|
80
|
+
write_key_id doc.subject
|
81
|
+
write_key_id doc.ca
|
82
|
+
|
83
|
+
write_block 'BEGIN', 'END', doc.body
|
84
|
+
write_block 'BEGIN SIGNATURE', 'END SIGNATURE', doc.signature
|
85
|
+
|
86
|
+
end_pos = pos
|
87
|
+
seek start_pos
|
88
|
+
printf "%08d", end_pos - start_pos - 10
|
89
|
+
seek end_pos
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def cut_int(string, range)
|
95
|
+
slice = string[range]
|
96
|
+
|
97
|
+
begin
|
98
|
+
Integer slice, 10
|
99
|
+
rescue ArgumentError => e
|
100
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (invalid integer in document: '#{slice}')"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "crypt/idea"
|
2
|
+
|
3
|
+
module CyberplatPKI
|
4
|
+
class IdeaCfb
|
5
|
+
BLOCK_SIZE = 8
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@encbuf = "\0" * BLOCK_SIZE
|
9
|
+
@fr = "\0" * BLOCK_SIZE
|
10
|
+
@fre = "\0" * BLOCK_SIZE
|
11
|
+
@pos = 0
|
12
|
+
@keys = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_encryption(key)
|
16
|
+
@idea = Crypt::IDEA.new key.unpack('n*'), Crypt::IDEA::ENCRYPT
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_decryption(key)
|
20
|
+
@idea = Crypt::IDEA.new key.unpack('n*'), Crypt::IDEA::DECRYPT
|
21
|
+
end
|
22
|
+
|
23
|
+
def encrypt(data)
|
24
|
+
cfb_engine data, method(:mix_enc), @idea.method(:encrypt_block)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def decrypt(data)
|
29
|
+
cfb_engine data, method(:mix_dec), @idea.method(:decrypt_block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def resync
|
33
|
+
if @pos != 0
|
34
|
+
@fr[0...BLOCK_SIZE - @pos] = @encbuf[@pos...BLOCK_SIZE]
|
35
|
+
@fr[BLOCK_SIZE - @pos...BLOCK_SIZE] = @encbuf[0...@pos]
|
36
|
+
|
37
|
+
@encbuf = "\0" * BLOCK_SIZE
|
38
|
+
@pos = 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def idea_block(data)
|
45
|
+
data
|
46
|
+
end
|
47
|
+
|
48
|
+
def mix_enc(data)
|
49
|
+
outbuf = "\0" * data.length
|
50
|
+
|
51
|
+
(@pos...@pos + data.length).each_with_index do |i, index|
|
52
|
+
outbuf[index] = @encbuf[i] = (@fre[i].ord ^ data[index].ord).chr
|
53
|
+
end
|
54
|
+
|
55
|
+
@pos += data.length
|
56
|
+
|
57
|
+
outbuf
|
58
|
+
end
|
59
|
+
|
60
|
+
def mix_dec(data)
|
61
|
+
outbuf = "\0" * data.length
|
62
|
+
|
63
|
+
(@pos...@pos + data.length).each_with_index do |i, index|
|
64
|
+
@encbuf[i] = data[index]
|
65
|
+
outbuf[index] = (@fre[i].ord ^ @encbuf[i].ord).chr
|
66
|
+
end
|
67
|
+
|
68
|
+
@pos += data.length
|
69
|
+
|
70
|
+
outbuf
|
71
|
+
end
|
72
|
+
|
73
|
+
def cfb_engine(data, mix, crypt_block)
|
74
|
+
pos = 0
|
75
|
+
dst = ""
|
76
|
+
|
77
|
+
while pos < data.length && @pos > 0
|
78
|
+
n = [ BLOCK_SIZE - @pos, data.length - pos ].min
|
79
|
+
|
80
|
+
dst << mix.call(data[pos...pos + n])
|
81
|
+
pos += n
|
82
|
+
|
83
|
+
if @pos == BLOCK_SIZE
|
84
|
+
@fr = @encbuf
|
85
|
+
@encbuf = "\0" * BLOCK_SIZE
|
86
|
+
@pos = 0
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
while pos < data.length
|
91
|
+
@fre = crypt_block.call @fr
|
92
|
+
|
93
|
+
n = [ BLOCK_SIZE, data.length - pos ].min
|
94
|
+
|
95
|
+
dst << mix.call(data[pos...pos + n])
|
96
|
+
pos += n
|
97
|
+
|
98
|
+
if @pos == BLOCK_SIZE
|
99
|
+
@fr = @encbuf
|
100
|
+
@encbuf = "\0" * BLOCK_SIZE
|
101
|
+
@pos = 0
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
dst
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/cyberplat_pki/key.rb
CHANGED
@@ -1,78 +1,150 @@
|
|
1
|
+
require "openssl"
|
2
|
+
|
1
3
|
module CyberplatPKI
|
2
4
|
class Key
|
3
|
-
|
5
|
+
module Helpers
|
6
|
+
def self.key_from_document_set(list, type, serial, password = nil)
|
7
|
+
document = list.find do |doc|
|
8
|
+
doc.type == type && (serial == 0 || doc.subject.key_serial == serial)
|
9
|
+
end
|
4
10
|
|
5
|
-
|
6
|
-
def new_private(source, password, engine=Library::DEFAULT_ENGINE)
|
7
|
-
internal = Library::Key.new
|
11
|
+
raise "CyberplatPKI: CRYPT_ERR_PUB_KEY_NOT_FOUND (key with specified serial has not been found in the document)" if document.nil?
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
key = Key.new
|
14
|
+
key.serial = document.subject.key_serial
|
15
|
+
key.name = document.subject.key_name
|
16
|
+
key.packets = Packet.load Document.decode64(document.body), password
|
17
|
+
key.signature = Packet.load Document.decode64(document.signature), password
|
14
18
|
|
15
|
-
|
19
|
+
[ key, document ]
|
16
20
|
end
|
17
21
|
|
18
|
-
def
|
19
|
-
|
22
|
+
def self.find_record(list, record_class)
|
23
|
+
record = list.find { |record| record.kind_of? record_class }
|
20
24
|
|
21
|
-
if
|
22
|
-
ca_key = ca_key.internal
|
23
|
-
end
|
25
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (#{record_class.name} not found in the document)" if record.nil?
|
24
26
|
|
25
|
-
|
26
|
-
engine,
|
27
|
-
source, source.length,
|
28
|
-
serial,
|
29
|
-
internal,
|
30
|
-
ca_key
|
31
|
-
|
32
|
-
new(internal)
|
27
|
+
record
|
33
28
|
end
|
29
|
+
end
|
34
30
|
|
35
|
-
|
31
|
+
attr_accessor :serial, :name, :signature, :ca_key, :packets
|
32
|
+
|
33
|
+
def self.new_private(source, password = nil)
|
34
|
+
documents = Document.load source
|
35
|
+
|
36
|
+
key, document = Helpers.key_from_document_set documents, :NM, 0, password
|
37
|
+
key.ca_key = key
|
38
|
+
|
39
|
+
key
|
36
40
|
end
|
37
41
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
def self.new_public(source, serial, ca_key = nil)
|
43
|
+
documents = Document.load source
|
44
|
+
|
45
|
+
key, document = Helpers.key_from_document_set documents, :NS, serial
|
46
|
+
if ca_key.nil?
|
47
|
+
if document.subject == document.ca
|
48
|
+
key.ca_key = key
|
49
|
+
else
|
50
|
+
key.ca_key, ca_document = Helpers.key_from_document_set documents, :NS, document.ca.key_serial
|
51
|
+
key.ca_key.ca_key = key.ca_key
|
52
|
+
|
53
|
+
key.ca_key.validate
|
54
|
+
end
|
45
55
|
else
|
46
|
-
|
56
|
+
key.ca_key = ca_key
|
47
57
|
end
|
58
|
+
|
59
|
+
key.validate
|
60
|
+
|
61
|
+
key
|
62
|
+
end
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
@serial = nil
|
66
|
+
@name = nil
|
67
|
+
@signature = nil
|
68
|
+
@ca_key = nil
|
69
|
+
@packets = nil
|
48
70
|
end
|
49
71
|
|
50
72
|
def sign(data)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
73
|
+
signature = SignaturePacket.new
|
74
|
+
|
75
|
+
signature.metadata = [
|
76
|
+
0x01, # Signature type
|
77
|
+
serial, # Signing key serial number
|
78
|
+
Time.now.to_i # Timestamp
|
79
|
+
].pack("CNN")
|
80
|
+
|
81
|
+
key_packet = Helpers.find_record packets, KeyPacket
|
55
82
|
|
56
|
-
|
57
|
-
|
58
|
-
result, result.total,
|
59
|
-
@internal
|
83
|
+
digest = OpenSSL::Digest::MD5.new
|
84
|
+
signature.signature = key_packet.key.sign digest, data + signature.metadata
|
60
85
|
|
61
|
-
|
86
|
+
# Re-hash to get 'first word of digest'
|
87
|
+
digest.reset
|
88
|
+
digest.update data
|
89
|
+
digest.update signature.metadata
|
90
|
+
signature.hash_msw = digest.digest[0..1]
|
91
|
+
|
92
|
+
signature_block = Document.encode64 Packet.save([ signature ])
|
93
|
+
|
94
|
+
doc = Document.new
|
95
|
+
doc.engine = 1
|
96
|
+
doc.type = :SM
|
97
|
+
doc.subject = KeyId.new @name, @serial
|
98
|
+
doc.ca = KeyId.new '', 0
|
99
|
+
doc.data_length = data.length
|
100
|
+
doc.body = data
|
101
|
+
doc.signature = signature_block
|
102
|
+
|
103
|
+
text = Document.save [ doc ]
|
104
|
+
|
105
|
+
text
|
62
106
|
end
|
63
107
|
|
64
108
|
def verify(data_with_signature)
|
65
|
-
|
66
|
-
data_with_signature, data_with_signature.size,
|
67
|
-
nil, nil,
|
68
|
-
@internal
|
109
|
+
documents = Document.load data_with_signature
|
69
110
|
|
70
|
-
if
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
111
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_FORMAT (expected one document of type SM)" if documents.length != 1 || documents[0].type != :SM
|
112
|
+
|
113
|
+
document, = *documents
|
114
|
+
|
115
|
+
signature_packet, = Packet.load Document.decode64(document.signature)
|
116
|
+
key_packet = Helpers.find_record packets, KeyPacket
|
117
|
+
|
118
|
+
digest = OpenSSL::Digest::MD5.new
|
119
|
+
signature = signature_packet.signature.ljust key_packet.key.n.num_bytes, 0.chr
|
120
|
+
key_packet.key.verify digest, signature, document.body + signature_packet.metadata
|
121
|
+
end
|
122
|
+
|
123
|
+
def validate
|
124
|
+
signature_packet = Helpers.find_record signature, SignaturePacket
|
125
|
+
key_packet = Helpers.find_record packets, KeyPacket
|
126
|
+
user_id_packet = Helpers.find_record packets, UserIdPacket
|
127
|
+
ca_key_packet = Helpers.find_record ca_key.packets, KeyPacket
|
128
|
+
|
129
|
+
io = StringIO.open ''.encode('BINARY'), 'wb'
|
130
|
+
io.extend PacketIORoutines
|
131
|
+
|
132
|
+
io.seek 3
|
133
|
+
key_packet.save io, nil
|
134
|
+
|
135
|
+
data_length = io.pos - 3
|
136
|
+
io.rewind
|
137
|
+
io.write [ 0x99, data_length ].pack "Cn"
|
138
|
+
io.seek 0, IO::SEEK_END
|
139
|
+
|
140
|
+
io.write user_id_packet.user_id
|
141
|
+
io.write signature_packet.metadata
|
142
|
+
|
143
|
+
digest = OpenSSL::Digest::MD5.new
|
144
|
+
signature = signature_packet.signature.rjust ca_key_packet.key.n.num_bytes, 0.chr
|
145
|
+
valid = ca_key_packet.key.verify digest, signature, io.string
|
146
|
+
|
147
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_KEY (key signature verification failed)" unless valid
|
76
148
|
end
|
77
149
|
end
|
78
|
-
end
|
150
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module CyberplatPKI
|
2
|
+
class KeyId
|
3
|
+
attr_accessor :key_name, :key_serial
|
4
|
+
|
5
|
+
def initialize(key_name = nil, key_serial = nil)
|
6
|
+
@key_name = key_name
|
7
|
+
@key_serial = key_serial
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
key_name == other.key_name && key_serial == other.key_serial
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "openssl"
|
2
|
+
|
3
|
+
module CyberplatPKI
|
4
|
+
class KeyPacket < Packet
|
5
|
+
attr_accessor :serial, :timestamp, :valid_days, :algorithm, :key
|
6
|
+
|
7
|
+
def self.load(io, context)
|
8
|
+
version = io.readbyte
|
9
|
+
|
10
|
+
# RFC4880 says:
|
11
|
+
#
|
12
|
+
# V3 keys are deprecated. They contain three weaknesses. First, it is
|
13
|
+
# relatively easy to construct a V3 key that has the same Key ID as any
|
14
|
+
# other key because the Key ID is simply the low 64 bits of the public
|
15
|
+
# modulus. Secondly, because the fingerprint of a V3 key hashes the
|
16
|
+
# key material, but not its length, there is an increased opportunity
|
17
|
+
# for fingerprint collisions. Third, there are weaknesses in the MD5
|
18
|
+
# hash algorithm that make developers prefer other algorithms. See
|
19
|
+
# below for a fuller discussion of Key IDs and fingerprints.
|
20
|
+
#
|
21
|
+
# Beware.
|
22
|
+
|
23
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (unsupported key version: #{version})" if version != 0x03
|
24
|
+
|
25
|
+
key = KeyPacket.new
|
26
|
+
|
27
|
+
key.serial, key.timestamp, key.valid_days, algorithm = io.read(11).unpack "NNnC"
|
28
|
+
|
29
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (unsupported algorithm #{algorithm})" if algorithm != 1
|
30
|
+
|
31
|
+
key.key = OpenSSL::PKey::RSA.new
|
32
|
+
key.key.n = io.read_mpi
|
33
|
+
key.key.e = io.read_mpi
|
34
|
+
|
35
|
+
key
|
36
|
+
end
|
37
|
+
|
38
|
+
def save(io, context)
|
39
|
+
io.write [ 3, serial, timestamp, valid_days, 1 ].pack("CNNnC")
|
40
|
+
|
41
|
+
io.write_mpi key.n
|
42
|
+
io.write_mpi key.e
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
module CyberplatPKI
|
4
|
+
class Packet
|
5
|
+
def self.load(source, password = nil)
|
6
|
+
io = StringIO.new source, "rb"
|
7
|
+
io.extend PacketIORoutines
|
8
|
+
|
9
|
+
packets = []
|
10
|
+
|
11
|
+
until io.eof?
|
12
|
+
begin
|
13
|
+
packets << io.read_packet(password)
|
14
|
+
rescue EOFError => e
|
15
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (unexpected end of packet)"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
packets
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.save(packets, password = nil)
|
23
|
+
io = StringIO.new '', "wb"
|
24
|
+
io.extend PacketIORoutines
|
25
|
+
|
26
|
+
packets.each { |packet| io.write_packet packet, password }
|
27
|
+
|
28
|
+
io.string
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require "stringio"
|
2
|
+
require "openssl"
|
3
|
+
|
4
|
+
module CyberplatPKI
|
5
|
+
module PacketIORoutines
|
6
|
+
attr_accessor :cipher, :checksum
|
7
|
+
|
8
|
+
def read_packet(password = nil)
|
9
|
+
header = readbyte
|
10
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (invalid packet header)" if (header & 0xC0) != 0x80
|
11
|
+
|
12
|
+
case header & 3
|
13
|
+
when 0
|
14
|
+
data_length = readbyte
|
15
|
+
|
16
|
+
when 1
|
17
|
+
data_length, = read(2).unpack 'n'
|
18
|
+
|
19
|
+
when 2
|
20
|
+
data_length, = read(4).unpack 'N'
|
21
|
+
|
22
|
+
else
|
23
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (invalid packet length type: #{header % 3}"
|
24
|
+
end
|
25
|
+
|
26
|
+
packet_type = (header >> 2) & 0x0F
|
27
|
+
|
28
|
+
data = read data_length
|
29
|
+
packet_class = PACKET_TYPES[packet_type]
|
30
|
+
|
31
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (unsupported packet type: #{packet_type})" if packet_class.nil?
|
32
|
+
|
33
|
+
StringIO.open(data, "rb") do |io|
|
34
|
+
io.extend PacketIORoutines
|
35
|
+
packet_class.load io, context_for_password(password)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def write_packet(packet, password = nil)
|
40
|
+
io = StringIO.open ''.encode('BINARY'), 'wb'
|
41
|
+
io.extend PacketIORoutines
|
42
|
+
packet.save io, context_for_password(password)
|
43
|
+
|
44
|
+
data = io.string
|
45
|
+
|
46
|
+
packet_type = (PACKET_TYPES.key(packet.class) << 2) | 0x80
|
47
|
+
|
48
|
+
case data.length
|
49
|
+
when 0x00..0xFF
|
50
|
+
putc packet_type
|
51
|
+
putc data.length
|
52
|
+
|
53
|
+
when 0x0100..0xFFFF
|
54
|
+
putc packet_type | 1
|
55
|
+
|
56
|
+
write [ data.length ].pack("n")
|
57
|
+
|
58
|
+
when 0x00010000..0xFFFFFFFF
|
59
|
+
putc packet_type | 2
|
60
|
+
|
61
|
+
write [ data.length ].pack("N")
|
62
|
+
end
|
63
|
+
|
64
|
+
write data
|
65
|
+
end
|
66
|
+
|
67
|
+
def read_mpi
|
68
|
+
header = read(2)
|
69
|
+
|
70
|
+
mpi_bits, = header.unpack("n")
|
71
|
+
data = read((mpi_bits + 7) / 8)
|
72
|
+
|
73
|
+
if !cipher.nil?
|
74
|
+
cipher.resync
|
75
|
+
data = cipher.decrypt data
|
76
|
+
end
|
77
|
+
|
78
|
+
add_checksum header
|
79
|
+
add_checksum data
|
80
|
+
|
81
|
+
OpenSSL::BN.new data, 2
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_mpi(bn)
|
85
|
+
header = [ bn.num_bits ].pack("n")
|
86
|
+
data = bn.to_s 2
|
87
|
+
|
88
|
+
add_checksum header
|
89
|
+
add_checksum data
|
90
|
+
|
91
|
+
write header
|
92
|
+
|
93
|
+
if !cipher.nil?
|
94
|
+
cipher.resync
|
95
|
+
data = cipher.encrypt data
|
96
|
+
end
|
97
|
+
|
98
|
+
write data
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def context_for_password(password)
|
104
|
+
if password.nil?
|
105
|
+
nil
|
106
|
+
else
|
107
|
+
context = IdeaCfb.new
|
108
|
+
context.start_encryption OpenSSL::Digest::MD5.digest(password) # yes, encryption
|
109
|
+
|
110
|
+
context
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def add_checksum(string)
|
115
|
+
if !checksum.nil?
|
116
|
+
@checksum = (@checksum + string.sum(16)) & 0xFFFF
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module CyberplatPKI
|
2
|
+
class PrivateKeyPacket < KeyPacket
|
3
|
+
def self.load(io, context)
|
4
|
+
key = super
|
5
|
+
|
6
|
+
cipher = io.readbyte
|
7
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (unsupported private key cipher: #{cipher})" if cipher != 1 # IDEA-CFB + MD5
|
8
|
+
|
9
|
+
iv = io.read 8
|
10
|
+
context.decrypt iv
|
11
|
+
|
12
|
+
public_key = key.key
|
13
|
+
key.key = OpenSSL::PKey::RSA.new
|
14
|
+
|
15
|
+
io.cipher = context
|
16
|
+
io.checksum = 0
|
17
|
+
|
18
|
+
key.key.d = io.read_mpi
|
19
|
+
key.key.p = io.read_mpi
|
20
|
+
key.key.q = io.read_mpi
|
21
|
+
dummy = io.read_mpi
|
22
|
+
|
23
|
+
calculated_checksum = io.checksum
|
24
|
+
|
25
|
+
io.checksum = nil
|
26
|
+
io.cipher = nil
|
27
|
+
|
28
|
+
checksum, = io.read(2).unpack("n")
|
29
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PASSWD (invalid MPI checksum. Expected #{checksum.to_s 16}, calculated #{calculated_checksum.to_s 16})" if checksum != calculated_checksum
|
30
|
+
|
31
|
+
key.key.dmp1 = key.key.d % (key.key.p - 1)
|
32
|
+
key.key.dmq1 = key.key.d % (key.key.q - 1)
|
33
|
+
key.key.iqmp = key.key.q.mod_inverse key.key.p
|
34
|
+
|
35
|
+
# jruby-openssl requires public key parameters to be set LAST
|
36
|
+
key.key.n = public_key.n
|
37
|
+
key.key.e = public_key.e
|
38
|
+
|
39
|
+
key
|
40
|
+
end
|
41
|
+
|
42
|
+
def save(io, context)
|
43
|
+
super
|
44
|
+
|
45
|
+
raise NotImplementedError, "CyberplatPKI: PrivateKeyPacket#save is not implemented"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module CyberplatPKI
|
2
|
+
class SignaturePacket < Packet
|
3
|
+
attr_accessor :metadata, :signature, :hash_msw
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@metadata = nil
|
7
|
+
@signature = nil
|
8
|
+
@hash_msw = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.load(io, context)
|
12
|
+
version = io.readbyte
|
13
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (unsupported signature length: #{version})" if version != 3
|
14
|
+
|
15
|
+
metadata_length = io.readbyte
|
16
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (invalid metadata length: #{metadata_length})" if metadata_length > 32
|
17
|
+
|
18
|
+
signature = new
|
19
|
+
|
20
|
+
signature.metadata = io.read metadata_length
|
21
|
+
|
22
|
+
key_algorithm, hash_algorithm = io.read(2).unpack("CC")
|
23
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (invalid public key algorithm: #{key_algorithm})" if key_algorithm != 0x01
|
24
|
+
raise "CyberplatPKI: CRYPT_ERR_INVALID_PACKET_FORMAT (invalid hash key algorithm: #{key_algorithm})" if hash_algorithm != 0x01
|
25
|
+
|
26
|
+
signature.hash_msw = io.read(2)
|
27
|
+
signature_bits, = io.read(2).unpack("n")
|
28
|
+
signature.signature = io.read (signature_bits + 7) / 8
|
29
|
+
|
30
|
+
signature
|
31
|
+
end
|
32
|
+
|
33
|
+
def save(io, context)
|
34
|
+
io.write [ 3, metadata.length ].pack "CC"
|
35
|
+
io.write metadata
|
36
|
+
io.write [ 1, 1 ].pack "CC"
|
37
|
+
io.write hash_msw
|
38
|
+
io.write [ signature.length * 8 ].pack "n"
|
39
|
+
io.write signature
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CyberplatPKI
|
2
|
+
class TrustPacket < Packet
|
3
|
+
attr_accessor :trust
|
4
|
+
|
5
|
+
def initialize(trust = nil)
|
6
|
+
@trust = trust
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.load(io, context)
|
10
|
+
new io.read
|
11
|
+
end
|
12
|
+
|
13
|
+
def save(io, context)
|
14
|
+
io.write @trust
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CyberplatPKI
|
2
|
+
class UserIdPacket < Packet
|
3
|
+
attr_accessor :user_id
|
4
|
+
|
5
|
+
def initialize(user_id = nil)
|
6
|
+
@user_id = user_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.load(io, context)
|
10
|
+
new io.read
|
11
|
+
end
|
12
|
+
|
13
|
+
def save(io, context)
|
14
|
+
io.write @user_id
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/cyberplat_pki.rb
CHANGED
@@ -1,10 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require_relative 'cyberplat_pki/key_id'
|
2
|
+
require_relative 'cyberplat_pki/document'
|
3
|
+
require_relative 'cyberplat_pki/document_io_routines'
|
4
|
+
require_relative 'cyberplat_pki/packet'
|
5
|
+
require_relative 'cyberplat_pki/signature_packet'
|
6
|
+
require_relative 'cyberplat_pki/key_packet'
|
7
|
+
require_relative 'cyberplat_pki/private_key_packet'
|
8
|
+
require_relative 'cyberplat_pki/trust_packet'
|
9
|
+
require_relative 'cyberplat_pki/user_id_packet'
|
10
|
+
require_relative 'cyberplat_pki/packet_io_routines'
|
11
|
+
require_relative 'cyberplat_pki/key'
|
12
|
+
require_relative 'cyberplat_pki/idea_cfb'
|
6
13
|
|
7
|
-
|
8
|
-
|
14
|
+
module CyberplatPKI
|
15
|
+
PACKET_TYPES = {
|
16
|
+
2 => SignaturePacket,
|
17
|
+
5 => PrivateKeyPacket,
|
18
|
+
6 => KeyPacket,
|
19
|
+
12 => TrustPacket,
|
20
|
+
13 => UserIdPacket
|
9
21
|
}
|
10
22
|
end
|
23
|
+
|
data/spec/key_spec.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cyberplat_pki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,10 +9,26 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
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: digest-crc
|
16
32
|
requirement: !ruby/object:Gem::Requirement
|
17
33
|
none: false
|
18
34
|
requirements:
|
@@ -28,14 +44,14 @@ dependencies:
|
|
28
44
|
- !ruby/object:Gem::Version
|
29
45
|
version: '0'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
47
|
+
name: crypt
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
33
49
|
none: false
|
34
50
|
requirements:
|
35
51
|
- - ! '>='
|
36
52
|
- !ruby/object:Gem::Version
|
37
53
|
version: '0'
|
38
|
-
type: :
|
54
|
+
type: :runtime
|
39
55
|
prerelease: false
|
40
56
|
version_requirements: !ruby/object:Gem::Requirement
|
41
57
|
none: false
|
@@ -43,7 +59,7 @@ dependencies:
|
|
43
59
|
- - ! '>='
|
44
60
|
- !ruby/object:Gem::Version
|
45
61
|
version: '0'
|
46
|
-
description: CyberplatPKI is
|
62
|
+
description: CyberplatPKI is a library for signing Cyberplat requests.
|
47
63
|
email:
|
48
64
|
- whitequark@whitequark.org
|
49
65
|
executables: []
|
@@ -58,17 +74,23 @@ files:
|
|
58
74
|
- README.md
|
59
75
|
- Rakefile
|
60
76
|
- cyberplat_pki.gemspec
|
61
|
-
- ext/libipriv-linux32.so
|
62
|
-
- ext/libipriv32.dll
|
63
77
|
- lib/cyberplat_pki.rb
|
78
|
+
- lib/cyberplat_pki/document.rb
|
79
|
+
- lib/cyberplat_pki/document_io_routines.rb
|
80
|
+
- lib/cyberplat_pki/idea_cfb.rb
|
64
81
|
- lib/cyberplat_pki/key.rb
|
65
|
-
- lib/cyberplat_pki/
|
82
|
+
- lib/cyberplat_pki/key_id.rb
|
83
|
+
- lib/cyberplat_pki/key_packet.rb
|
84
|
+
- lib/cyberplat_pki/packet.rb
|
85
|
+
- lib/cyberplat_pki/packet_io_routines.rb
|
86
|
+
- lib/cyberplat_pki/private_key_packet.rb
|
87
|
+
- lib/cyberplat_pki/signature_packet.rb
|
88
|
+
- lib/cyberplat_pki/trust_packet.rb
|
89
|
+
- lib/cyberplat_pki/user_id_packet.rb
|
66
90
|
- spec/key_spec.rb
|
67
91
|
- spec/keys/pubkeys.key
|
68
92
|
- spec/keys/secret.key
|
69
93
|
- spec/spec_helper.rb
|
70
|
-
- vendor/linux.zip
|
71
|
-
- vendor/win32.zip
|
72
94
|
homepage: http://github.com/whitequark/cyberplat_pki
|
73
95
|
licenses: []
|
74
96
|
post_install_message:
|
@@ -92,7 +114,7 @@ rubyforge_project:
|
|
92
114
|
rubygems_version: 1.8.23
|
93
115
|
signing_key:
|
94
116
|
specification_version: 3
|
95
|
-
summary: CyberplatPKI is
|
117
|
+
summary: CyberplatPKI is a library for signing Cyberplat requests.
|
96
118
|
test_files:
|
97
119
|
- spec/key_spec.rb
|
98
120
|
- spec/keys/pubkeys.key
|
data/ext/libipriv-linux32.so
DELETED
Binary file
|
data/ext/libipriv32.dll
DELETED
Binary file
|
@@ -1,128 +0,0 @@
|
|
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
|
data/vendor/linux.zip
DELETED
Binary file
|
data/vendor/win32.zip
DELETED
Binary file
|