iota-ruby 1.1.8-java

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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +8 -0
  3. data/.gitignore +15 -0
  4. data/.travis.yml +24 -0
  5. data/.yardopts +7 -0
  6. data/CHANGELOG.md +18 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +21 -0
  9. data/README.md +121 -0
  10. data/Rakefile +36 -0
  11. data/bin/iota-console +15 -0
  12. data/examples/multisig.rb +69 -0
  13. data/ext/ccurl/ccurl.c +134 -0
  14. data/ext/ccurl/extconf.rb +22 -0
  15. data/ext/jcurl/JCurl.java +126 -0
  16. data/ext/jcurl/JCurlService.java +36 -0
  17. data/ext/pow/ccurl-0.3.0.dll +0 -0
  18. data/ext/pow/libccurl-0.3.0.dylib +0 -0
  19. data/ext/pow/libccurl-0.3.0.so +0 -0
  20. data/iota-ruby.gemspec +37 -0
  21. data/lib/iota.rb +76 -0
  22. data/lib/iota/api/api.rb +251 -0
  23. data/lib/iota/api/commands.rb +113 -0
  24. data/lib/iota/api/transport.rb +43 -0
  25. data/lib/iota/api/wrappers.rb +429 -0
  26. data/lib/iota/crypto/bundle.rb +163 -0
  27. data/lib/iota/crypto/converter.rb +244 -0
  28. data/lib/iota/crypto/curl.rb +18 -0
  29. data/lib/iota/crypto/curl_c.rb +17 -0
  30. data/lib/iota/crypto/curl_java.rb +18 -0
  31. data/lib/iota/crypto/curl_ruby.rb +70 -0
  32. data/lib/iota/crypto/hmac.rb +27 -0
  33. data/lib/iota/crypto/kerl.rb +82 -0
  34. data/lib/iota/crypto/pow_provider.rb +27 -0
  35. data/lib/iota/crypto/private_key.rb +80 -0
  36. data/lib/iota/crypto/sha3_ruby.rb +122 -0
  37. data/lib/iota/crypto/signing.rb +97 -0
  38. data/lib/iota/models/account.rb +489 -0
  39. data/lib/iota/models/base.rb +13 -0
  40. data/lib/iota/models/bundle.rb +87 -0
  41. data/lib/iota/models/input.rb +38 -0
  42. data/lib/iota/models/seed.rb +33 -0
  43. data/lib/iota/models/transaction.rb +52 -0
  44. data/lib/iota/models/transfer.rb +44 -0
  45. data/lib/iota/multisig/address.rb +41 -0
  46. data/lib/iota/multisig/multisig.rb +244 -0
  47. data/lib/iota/utils/ascii.rb +50 -0
  48. data/lib/iota/utils/broker.rb +124 -0
  49. data/lib/iota/utils/input_validator.rb +149 -0
  50. data/lib/iota/utils/object_validator.rb +34 -0
  51. data/lib/iota/utils/utils.rb +324 -0
  52. data/lib/iota/version.rb +3 -0
  53. data/lib/jcurl.jar +0 -0
  54. data/lib/patch.rb +17 -0
  55. data/test/ascii_test.rb +114 -0
  56. data/test/curl_c_test.rb +31 -0
  57. data/test/curl_java_test.rb +31 -0
  58. data/test/curl_ruby_test.rb +27 -0
  59. data/test/kerl_test.rb +52 -0
  60. data/test/pow_provider_test.rb +36 -0
  61. data/test/sha3_test.rb +71 -0
  62. data/test/test_helper.rb +4 -0
  63. data/test/utils_test.rb +179 -0
  64. metadata +183 -0
@@ -0,0 +1,70 @@
1
+ module IOTA
2
+ module Crypto
3
+ class RubyCurl
4
+ NUMBER_OF_ROUNDS = 81
5
+ HASH_LENGTH = 243
6
+ STATE_LENGTH = 3 * HASH_LENGTH
7
+ TRUTH_TABLE = [1, 0, -1, 1, -1, 0, -1, 1, 0]
8
+
9
+ def initialize(rounds = nil)
10
+ @rounds = rounds || NUMBER_OF_ROUNDS
11
+ reset
12
+ end
13
+
14
+ def reset
15
+ @state = [0] * STATE_LENGTH
16
+ end
17
+
18
+ def absorb(trits)
19
+ length = trits.length
20
+ offset = 0
21
+
22
+ while offset < length
23
+ start = offset
24
+ stop = [start + HASH_LENGTH, length].min
25
+
26
+ @state[0...stop-start] = trits.slice(start, stop-start)
27
+ transform
28
+
29
+ offset += HASH_LENGTH
30
+ end
31
+ end
32
+
33
+ def squeeze(trits)
34
+ trits[0...HASH_LENGTH] = @state.slice(0, HASH_LENGTH)
35
+ transform
36
+ end
37
+
38
+ def transform
39
+ previousState = @state.slice(0, @state.length)
40
+ newState = @state.slice(0, @state.length)
41
+
42
+ index = 0
43
+ round = 0
44
+ while round < @rounds
45
+ previousTrit = previousState[index].to_i
46
+
47
+ pos = 0
48
+ while true
49
+ index += (index < 365) ? 364 : -365
50
+ newTrit = previousState[index].to_i
51
+ newState[pos] = TRUTH_TABLE[previousTrit + (3 * newTrit) + 4]
52
+ previousTrit = newTrit
53
+ pos += 1
54
+ break if pos >= STATE_LENGTH
55
+ end
56
+
57
+ previousState = newState
58
+ newState = newState.slice(0, newState.length)
59
+ round += 1
60
+ end
61
+
62
+ @state = newState
63
+ end
64
+
65
+ def version
66
+ "Ruby"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ module IOTA
2
+ module Crypto
3
+ class Hmac
4
+ ROUNDS = 27
5
+
6
+ def initialize(key)
7
+ @key = Converter.trits(key)
8
+ end
9
+
10
+ def addHMAC(bundle)
11
+ curl = Curl.new(ROUNDS)
12
+ (0...bundle.bundle.length).step(1) do |i|
13
+ if bundle.bundle[i].value > 0
14
+ bundleHashTrits = Converter.trits(bundle.bundle[i].bundle)
15
+ hmac = Array.new(243, 0)
16
+ curl.reset
17
+ curl.absorb(@key)
18
+ curl.absorb(bundleHashTrits)
19
+ curl.squeeze(hmac)
20
+ hmacTrytes = Converter.trytes(hmac)
21
+ bundle.bundle[i].signatureMessageFragment = hmacTrytes + bundle.bundle[i].signatureMessageFragment[81...2187]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,82 @@
1
+ module IOTA
2
+ module Crypto
3
+ class Kerl
4
+ BIT_HASH_LENGTH = 384
5
+ HASH_LENGTH = Curl::HASH_LENGTH
6
+
7
+ def initialize
8
+ reset
9
+ end
10
+
11
+ def reset
12
+ unless RUBY_PLATFORM =~ /java/
13
+ require 'digest/sha3'
14
+ @hasher = Digest::SHA3.new(BIT_HASH_LENGTH)
15
+ else
16
+ require "iota/crypto/sha3_ruby"
17
+ @hasher = Digest::RubySHA3.new(BIT_HASH_LENGTH)
18
+ end
19
+ end
20
+
21
+ def absorb(trits, offset = 0, length = nil)
22
+ pad = trits.length % HASH_LENGTH != 0 ? trits.length % HASH_LENGTH : HASH_LENGTH
23
+ trits.concat([0] * (HASH_LENGTH - pad))
24
+
25
+ length = trits.length if length.nil?
26
+
27
+ if length % HASH_LENGTH != 0 || length == 0
28
+ raise StandardError, "Illegal length provided"
29
+ end
30
+
31
+ while offset < length
32
+ limit = [offset + HASH_LENGTH, length].min
33
+
34
+ # If we're copying over a full chunk, zero last trit
35
+ trits[limit - 1] = 0 if limit - offset == HASH_LENGTH
36
+
37
+ signed_bytes = Converter.convertToBytes(trits[offset...limit])
38
+
39
+ # Convert signed bytes into their equivalent unsigned representation
40
+ # In order to use Python's built-in bytes type
41
+ unsigned_bytes = signed_bytes.map{ |b| Converter.convertSign(b) }.pack('c*').force_encoding('UTF-8')
42
+
43
+ @hasher.update(unsigned_bytes)
44
+
45
+ offset += HASH_LENGTH
46
+ end
47
+ end
48
+
49
+ def squeeze(trits, offset = 0, length = nil)
50
+ pad = trits.length % HASH_LENGTH != 0 ? trits.length % HASH_LENGTH : HASH_LENGTH
51
+ trits.concat([0] * (HASH_LENGTH - pad))
52
+
53
+ length = trits.length > 0 ? trits.length : HASH_LENGTH if length.nil?
54
+
55
+ if length % HASH_LENGTH != 0 || length == 0
56
+ raise StandardError, "Illegal length provided"
57
+ end
58
+
59
+ while offset < length
60
+ unsigned_hash = @hasher.digest
61
+
62
+ signed_hash = unsigned_hash.bytes.map { |b| Converter.convertSign(b) }
63
+
64
+ trits_from_hash = Converter.convertToTrits(signed_hash)
65
+ trits_from_hash[HASH_LENGTH - 1] = 0
66
+
67
+ limit = [HASH_LENGTH, length - offset].min
68
+
69
+ trits[offset...offset+limit] = trits_from_hash[0...limit]
70
+
71
+ flipped_bytes = unsigned_hash.bytes.map{ |b| Converter.convertSign(~b)}.pack('c*').force_encoding('UTF-8')
72
+
73
+ # Reset internal state before feeding back in
74
+ reset
75
+ @hasher.update(flipped_bytes)
76
+
77
+ offset += HASH_LENGTH
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,27 @@
1
+ require 'ffi'
2
+
3
+ module IOTA
4
+ module Crypto
5
+ class PowProvider
6
+ extend FFI::Library
7
+
8
+ ccurl_version = '0.3.0'
9
+ lib_root = File.join(File.dirname(__FILE__), '../../../ext/pow')
10
+ if FFI::Platform.windows?
11
+ libccurl_path = "#{lib_root}/ccurl-#{ccurl_version}.dll"
12
+ elsif FFI::Platform.mac?
13
+ libccurl_path = "#{lib_root}/libccurl-#{ccurl_version}.dylib"
14
+ else
15
+ libccurl_path = "#{lib_root}/libccurl-#{ccurl_version}.so"
16
+ end
17
+
18
+ ffi_lib libccurl_path
19
+
20
+ attach_function :ccurl_pow, [ :string, :int ], :string
21
+ attach_function :ccurl_digest_transaction, [ :string ], :string
22
+
23
+ alias_method :pow, :ccurl_pow
24
+ alias_method :digest, :ccurl_digest_transaction
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,80 @@
1
+ module IOTA
2
+ module Crypto
3
+ class PrivateKey
4
+ HASH_LENGTH = Kerl::HASH_LENGTH
5
+
6
+ attr_reader :key
7
+
8
+ def initialize(seed, index, security)
9
+ key = []
10
+ offset = 0
11
+ buffer = []
12
+
13
+ (0...index).step(1) do |i|
14
+ # Treat ``seed`` like a really big number and add ``index``.
15
+ # Note that addition works a little bit differently in balanced ternary.
16
+ (0...seed.length).step(1) do |j|
17
+ seed[j] += 1
18
+
19
+ if seed[j] > 1
20
+ seed[j] = -1
21
+ else
22
+ break
23
+ end
24
+ end
25
+ end
26
+
27
+ kerl = Kerl.new
28
+ kerl.absorb(seed, 0, seed.length)
29
+ kerl.squeeze(seed, 0, seed.length)
30
+ kerl.reset
31
+ kerl.absorb(seed, 0, seed.length)
32
+
33
+ security.times do
34
+ (0...27).step(1) do |i|
35
+ kerl.squeeze(buffer, 0, seed.length)
36
+ (0...HASH_LENGTH).step(1) do |j|
37
+ key[offset] = buffer[j]
38
+ offset += 1
39
+ end
40
+ end
41
+ end
42
+
43
+ @key = key
44
+ end
45
+
46
+ def digests
47
+ digestsArray = []
48
+ buffer = []
49
+
50
+ (0...(@key.length / 6561).floor).step(1) do |i|
51
+ keyFragment = @key.slice(i * 6561, 6561)
52
+
53
+ (0...27).step(1) do |j|
54
+ buffer = keyFragment.slice(j * HASH_LENGTH, HASH_LENGTH);
55
+
56
+ (0...26).step(1) do |k|
57
+ kKerl = Kerl.new
58
+ kKerl.absorb(buffer, 0, buffer.length)
59
+ kKerl.squeeze(buffer, 0, HASH_LENGTH)
60
+ end
61
+
62
+ (0...HASH_LENGTH).step(1) do |k|
63
+ keyFragment[j * HASH_LENGTH + k] = buffer[k]
64
+ end
65
+ end
66
+
67
+ kerl = Kerl.new
68
+ kerl.absorb(keyFragment, 0, keyFragment.length)
69
+ kerl.squeeze(buffer, 0, HASH_LENGTH)
70
+
71
+ (0...HASH_LENGTH).step(1) do |j|
72
+ digestsArray[i * HASH_LENGTH + j] = buffer[j];
73
+ end
74
+ end
75
+
76
+ digestsArray
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,122 @@
1
+ require 'digest'
2
+
3
+ module Digest
4
+ class RubySHA3 < Digest::Class
5
+ PILN = [10, 7, 11, 17, 18, 3, 5, 16,
6
+ 8, 21, 24, 4, 15, 23, 19, 13,
7
+ 12, 2, 20, 14, 22, 9, 6, 1]
8
+
9
+ ROTC = [ 1, 3, 6, 10, 15, 21, 28, 36,
10
+ 45, 55, 2, 14, 27, 41, 56, 8,
11
+ 25, 43, 62, 18, 39, 61, 20, 44]
12
+
13
+ RNDC = [0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
14
+ 0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
15
+ 0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
16
+ 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
17
+ 0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
18
+ 0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
19
+ 0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
20
+ 0x8000000000008080, 0x0000000080000001, 0x8000000080008008]
21
+
22
+ def initialize hash_size = 512
23
+ @size = hash_size / 8
24
+ @buffer = ''
25
+ end
26
+
27
+ def << s
28
+ @buffer << s.unpack('C*').pack('C*')
29
+ self
30
+ end
31
+ alias update <<
32
+
33
+ def reset
34
+ # @buffer.clear # CHANGE: DO NOT CLEAR BUFFER AS WE NEED
35
+ self
36
+ end
37
+
38
+ def digest(data = nil)
39
+ if data
40
+ update(data)
41
+ value = finish
42
+ else
43
+ cloned = dup
44
+ value = cloned.finish
45
+ end
46
+ value
47
+ end
48
+
49
+ def hexdigest(data = nil)
50
+ value = digest(data)
51
+ value.unpack("H*").first
52
+ end
53
+
54
+ def inspect
55
+ "#<#{self.class}: #{hexdigest}>"
56
+ end
57
+
58
+ def finish
59
+ s = Array.new 25, 0
60
+ width = 200 - @size * 2
61
+
62
+ buffer_dup = @buffer.dup
63
+ buffer_dup << "\x01" << "\0" * (width - buffer_dup.size % width)
64
+ buffer_dup[-1] = (buffer_dup[-1].ord | 0x80).chr.unpack('C*').pack('C*')
65
+
66
+ 0.step buffer_dup.size - 1, width do |j|
67
+ quads = buffer_dup[j, width].unpack 'Q*'
68
+ (width / 8).times do |i|
69
+ s[i] ^= quads[i]
70
+ end
71
+
72
+ keccak s
73
+ end
74
+
75
+ s.pack('Q*')[0, @size]
76
+ end
77
+
78
+ private
79
+ def keccak s
80
+ 24.times.each_with_object [] do |round, a|
81
+ # Theta
82
+ 5.times do |i|
83
+ a[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]
84
+ end
85
+
86
+ 5.times do |i|
87
+ t = a[(i + 4) % 5] ^ rotate(a[(i + 1) % 5], 1)
88
+ 0.step 24, 5 do |j|
89
+ s[j + i] ^= t
90
+ end
91
+ end
92
+
93
+ # Rho Pi
94
+ t = s[1]
95
+ 24.times do |i|
96
+ j = PILN[i]
97
+ a[0] = s[j]
98
+ s[j] = rotate t, ROTC[i]
99
+ t = a[0]
100
+ end
101
+
102
+ # Chi
103
+ 0.step 24, 5 do |j|
104
+ 5.times do |i|
105
+ a[i] = s[j + i]
106
+ end
107
+
108
+ 5.times do |i|
109
+ s[j + i] ^= ~a[(i + 1) % 5] & a[(i + 2) % 5]
110
+ end
111
+ end
112
+
113
+ # Iota
114
+ s[0] ^= RNDC[round]
115
+ end
116
+ end
117
+
118
+ def rotate x, y
119
+ (x << y | x >> 64 - y) & (1 << 64) - 1
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,97 @@
1
+ module IOTA
2
+ module Crypto
3
+ class Signing
4
+ HASH_LENGTH = Kerl::HASH_LENGTH
5
+
6
+ class << self
7
+ def address(digests)
8
+ addressTrits = []
9
+
10
+ kerl = Kerl.new
11
+
12
+ kerl.absorb(digests, 0, digests.length)
13
+ kerl.squeeze(addressTrits, 0, Kerl::HASH_LENGTH)
14
+
15
+ addressTrits
16
+ end
17
+
18
+ def digest(normalizedBundleFragment, signatureFragment)
19
+ buffer = []
20
+ kerl = Kerl.new
21
+
22
+ (0...27).step(1) do |i|
23
+ buffer = signatureFragment.slice(i * HASH_LENGTH, HASH_LENGTH)
24
+
25
+ j = normalizedBundleFragment[i] + 13
26
+
27
+ while j > 0
28
+ jKerl = Kerl.new
29
+ jKerl.absorb(buffer, 0, buffer.length)
30
+ jKerl.squeeze(buffer, 0, HASH_LENGTH)
31
+ j -= 1
32
+ end
33
+
34
+ kerl.absorb(buffer, 0, buffer.length)
35
+ end
36
+
37
+ kerl.squeeze(buffer, 0, HASH_LENGTH)
38
+ buffer
39
+ end
40
+
41
+ def signatureFragment(normalizedBundleFragment, keyFragment)
42
+ signatureFragment = keyFragment.slice(0, keyFragment.length)
43
+ hash = []
44
+
45
+ kerl = Kerl.new
46
+
47
+ (0...27).step(1) do |i|
48
+ hash = signatureFragment.slice(i * HASH_LENGTH, HASH_LENGTH)
49
+
50
+ (0...13-normalizedBundleFragment[i]).step(1) do |j|
51
+ kerl.reset
52
+ kerl.absorb(hash, 0, hash.length)
53
+ kerl.squeeze(hash, 0, HASH_LENGTH)
54
+ end
55
+
56
+ (0...HASH_LENGTH).step(1) do |j|
57
+ signatureFragment[i * HASH_LENGTH + j] = hash[j]
58
+ end
59
+ end
60
+
61
+ signatureFragment
62
+ end
63
+
64
+ def validateSignatures(expectedAddress, signatureFragments, bundleHash)
65
+ if !bundleHash
66
+ raise StandardError, "Invalid bundle hash provided"
67
+ end
68
+
69
+ bundle = Bundle.new
70
+
71
+ normalizedBundleFragments = []
72
+ normalizedBundleHash = bundle.normalizedBundle(bundleHash)
73
+
74
+ # Split hash into 3 fragments
75
+ (0...3).step(1) do |i|
76
+ normalizedBundleFragments[i] = normalizedBundleHash.slice(i * 27, 27)
77
+ end
78
+
79
+ # Get digests
80
+ digests = []
81
+ (0...signatureFragments.length).step(1) do |i|
82
+ digestBuffer = digest(normalizedBundleFragments[i % 3], Converter.trits(signatureFragments[i]))
83
+
84
+ (0...HASH_LENGTH).step(1) do |j|
85
+ digests[i * 243 + j] = digestBuffer[j]
86
+ end
87
+ end
88
+
89
+ addressTrits = address(digests)
90
+ address = Converter.trytes(addressTrits)
91
+
92
+ expectedAddress == address
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end