cyberplat_pki 1.0.0 → 2.0.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.
- 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
|