paygate-ruby 0.1.8 → 0.2.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.
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paygate
4
+ module ActionView
5
+ module FormHelper
6
+ PAYGATE_FORM_TEXT_FIELDS = {
7
+ mid: {
8
+ placeholder: 'Member ID'
9
+ },
10
+
11
+ locale: {
12
+ name: 'langcode',
13
+ default: 'KR',
14
+ placeholder: 'Language'
15
+ },
16
+
17
+ charset: {
18
+ default: 'UTF-8',
19
+ placeholder: 'Charset'
20
+ },
21
+
22
+ title: {
23
+ name: 'goodname',
24
+ placeholder: 'Title'
25
+ },
26
+
27
+ currency: {
28
+ name: 'goodcurrency',
29
+ default: 'WON',
30
+ placeholder: 'Currency'
31
+ },
32
+
33
+ amount: {
34
+ name: 'unitprice',
35
+ placeholder: 'Amount'
36
+ },
37
+
38
+ meta1: {
39
+ name: 'goodoption1',
40
+ placeholder: 'Good Option 1'
41
+ },
42
+
43
+ meta2: {
44
+ name: 'goodoption2',
45
+ placeholder: 'Good Option 2'
46
+ },
47
+
48
+ meta3: {
49
+ name: 'goodoption3',
50
+ placeholder: 'Good Option 3'
51
+ },
52
+
53
+ meta4: {
54
+ name: 'goodoption4',
55
+ placeholder: 'Good Option 4'
56
+ },
57
+
58
+ meta5: {
59
+ name: 'goodoption5',
60
+ placeholder: 'Good Option 5'
61
+ },
62
+
63
+ pay_method: {
64
+ name: 'paymethod',
65
+ default: 'card',
66
+ placeholder: 'Pay Method'
67
+ },
68
+
69
+ customer_name: {
70
+ name: 'receipttoname',
71
+ placeholder: 'Customer Name'
72
+ },
73
+
74
+ customer_email: {
75
+ name: 'receipttoemail',
76
+ placeholder: 'Customer Email'
77
+ },
78
+
79
+ card_number: {
80
+ name: 'cardnumber',
81
+ placeholder: 'Card Number'
82
+ },
83
+
84
+ expiry_year: {
85
+ name: 'cardexpireyear',
86
+ placeholder: 'Expiry Year'
87
+ },
88
+
89
+ expiry_month: {
90
+ name: 'cardexpiremonth',
91
+ placeholder: 'Expiry Month'
92
+ },
93
+
94
+ cvv: {
95
+ name: 'cardsecretnumber',
96
+ placeholder: 'CVV'
97
+ },
98
+
99
+ card_auth_code: {
100
+ name: 'cardauthcode',
101
+ placeholder: 'Card Auth Code'
102
+ },
103
+
104
+ response_code: {
105
+ name: 'replycode',
106
+ placeholder: 'Response Code'
107
+ },
108
+
109
+ response_message: {
110
+ name: 'replyMsg',
111
+ placeholder: 'Response Message'
112
+ },
113
+
114
+ tid: {
115
+ placeholder: 'TID'
116
+ },
117
+
118
+ profile_no: {
119
+ placeholder: 'Profile No'
120
+ },
121
+
122
+ hash_result: {
123
+ name: 'hashresult',
124
+ placeholder: 'Hash Result'
125
+ }
126
+ }.freeze
127
+
128
+ def paygate_open_pay_api_js_url
129
+ if Paygate.configuration.mode == :live
130
+ 'https://api.paygate.net/ajax/common/OpenPayAPI.js'
131
+ else
132
+ 'https://stgapi.paygate.net/ajax/common/OpenPayAPI.js'
133
+ end
134
+ end
135
+
136
+ def paygate_open_pay_api_form(options = {})
137
+ form_tag({}, name: 'PGIOForm') do
138
+ fields = []
139
+
140
+ PAYGATE_FORM_TEXT_FIELDS.each do |key, opts|
141
+ arg_opts = options[key] || {}
142
+ fields << text_field_tag(
143
+ key,
144
+ arg_opts[:value] || opts[:default],
145
+ name: opts[:name] || key.to_s,
146
+ placeholder: arg_opts[:placeholder] || opts[:placeholder]
147
+ ).html_safe
148
+ end
149
+
150
+ fields.join.html_safe
151
+ end.html_safe
152
+ end
153
+
154
+ def paygate_open_pay_api_screen
155
+ content_tag(:div, nil, id: 'PGIOscreen')
156
+ end
157
+ end
158
+ end
159
+ end
data/lib/paygate/aes.rb CHANGED
@@ -1,150 +1,152 @@
1
- module Paygate
2
- class Aes
3
- # Pre-computed multiplicative inverse in GF(2^8)
4
- S_BOX = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
5
- 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
6
- 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
7
- 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
8
- 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
9
- 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
10
- 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
11
- 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
12
- 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
13
- 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
14
- 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
15
- 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
16
- 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
17
- 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
18
- 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
19
- 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16].freeze
20
-
21
- # Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)]
22
- R_CON = [[0x00, 0x00, 0x00, 0x00],
23
- [0x01, 0x00, 0x00, 0x00],
24
- [0x02, 0x00, 0x00, 0x00],
25
- [0x04, 0x00, 0x00, 0x00],
26
- [0x08, 0x00, 0x00, 0x00],
27
- [0x10, 0x00, 0x00, 0x00],
28
- [0x20, 0x00, 0x00, 0x00],
29
- [0x40, 0x00, 0x00, 0x00],
30
- [0x80, 0x00, 0x00, 0x00],
31
- [0x1b, 0x00, 0x00, 0x00],
32
- [0x36, 0x00, 0x00, 0x00] ].freeze
33
-
34
- # Block size (in words): no of columns in state (fixed for AES)
35
- CIPHER_BLOCK_SIZE = 4.freeze
36
-
37
- # AES Cipher function: encrypt 'input' state with Rijndael algorithm
38
- # applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
39
- # @param int[] input 16-byte (128-bit) input state array
40
- # @param int[][] w Key schedule as 2D byte-array (Nr+1 x Nb bytes)
41
- # @returns int[] Encrypted output state array
42
- def self.cipher(input, w)
43
- nr = w.length / CIPHER_BLOCK_SIZE - 1 # no of rounds: 10/12/14 for 128/192/256-bit keys
44
- state = [[],[],[],[]] # initialize 4x4 byte-array 'state' with input
45
-
46
- (0...(4 * CIPHER_BLOCK_SIZE)).each do |i|
47
- state[i % 4][(i / 4.0).floor] = input[i]
48
- end
49
-
50
- state = add_round_key(state, w, 0)
51
- (1...nr).each do |round|
52
- state = sub_bytes(state)
53
- state = shift_rows(state)
54
- state = mix_columns(state)
55
- state = add_round_key(state, w, round)
56
- end
57
-
58
- state = sub_bytes(state)
59
- state = shift_rows(state)
60
- state = add_round_key(state, w, nr)
61
-
62
- output = []
63
- (0...(4 * CIPHER_BLOCK_SIZE)).each { |i| output[i] = state[i % 4][(i / 4.0).floor] }
64
- output
65
- end
66
-
67
- # Perform Key Expansion to generate a Key Schedule
68
- #
69
- # @param int[] key Key as 16/24/32-byte array
70
- # @returns int[][] Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes)
71
- def self.key_expansion(key)
72
- nk = key.length / 4 # key length (in words): 4/6/8 for 128/192/256-bit keys
73
- nr = nk + 6 # no of rounds: 10/12/14 for 128/192/256-bit keys
74
-
75
- w = []
76
- temp = []
77
- 0.upto(nk - 1){ |i| w[i] = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]] }
78
- (nk...(CIPHER_BLOCK_SIZE * (nr + 1))).each do |i|
79
- w[i] = []
80
- 0.upto(3){ |t| temp[t] = w[i - 1][t] }
81
- if i % nk == 0
82
- temp = sub_word(rotate_word(temp))
83
- 0.upto(3){ |t| temp[t] ^= R_CON[i / nk][t]}
84
- elsif nk > 6 && i % nk == 4
85
- temp = sub_word(temp)
86
- end
87
- 0.upto(3){ |t| w[i][t] = w[i - nk][t] ^ temp[t] }
88
- end
89
- w
90
- end
91
-
92
- # apply SBox to state
93
- def self.sub_bytes(state)
94
- 0.upto(3) do |r|
95
- 0.upto(CIPHER_BLOCK_SIZE - 1){ |c| state[r][c] = S_BOX[state[r][c]] }
96
- end
97
- state
98
- end
99
-
100
- # shift row r of state S left by r bytes
101
- def self.shift_rows(state)
102
- t = []
103
- 1.upto(3) do |r|
104
- 0.upto(3){ |c| t[c] = state[r][(c + r) % CIPHER_BLOCK_SIZE] } # shift into temp copy
105
- 0.upto(3){ |c| state[r][c] = t[c] } # and copy back
106
- end # note that this will work for nb = 4,5,6, but not 7,8 (always 4 for AES):
107
- state # see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
108
- end
109
-
110
- # combine bytes of each col of state S
111
- def self.mix_columns(state)
112
- 0.upto(3) do |c|
113
- a=[] # 'a' is a copy of the current column from 'state'
114
- b=[] # 'b' is a•{02} in GF(2^8)
115
- 0.upto(3) do |i|
116
- a[i] = state[i][c]
117
- b[i] = (state[i][c] & 0x80 == 0) ? (state[i][c] << 1) ^ 0x011b : state[i][c] << 1
118
- end
119
- # a[n] ^ b[n] is a•{03} in GF(2^8)
120
- state[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3] # 2*a0 + 3*a1 + a2 + a3
121
- state[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3] # a0 * 2*a1 + 3*a2 + a3
122
- state[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3] # a0 + a1 + 2*a2 + 3*a3
123
- state[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3] # 3*a0 + a1 + a2 + 2*a3
124
- end
125
- state
126
- end
127
-
128
- # xor Round Key into state S
129
- def self.add_round_key(state, w, rnd)
130
- 0.upto(3) do |r|
131
- 0.upto(CIPHER_BLOCK_SIZE - 1){ |c| state[r][c] ^= w[rnd * 4 + c][r]}
132
- end
133
- state
134
- end
135
-
136
- # apply SBox to 4-byte word w
137
- def self.sub_word(word)
138
- 0.upto(3){ |i| word[i] = S_BOX[word[i]] }
139
- word
140
- end
141
-
142
- # rotate 4-byte word w left by one byte
143
- def self.rotate_word(word)
144
- tmp = word[0]
145
- 0.upto(2){ |i| word[i] = word[i + 1] }
146
- word[3] = tmp
147
- word
148
- end
149
- end
150
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Paygate
4
+ class Aes
5
+ # Pre-computed multiplicative inverse in GF(2^8)
6
+ S_BOX = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
7
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
8
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
9
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
10
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
11
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
12
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
13
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
14
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
15
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
16
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
17
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
18
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
19
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
20
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
21
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16].freeze
22
+
23
+ # Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)]
24
+ R_CON = [[0x00, 0x00, 0x00, 0x00],
25
+ [0x01, 0x00, 0x00, 0x00],
26
+ [0x02, 0x00, 0x00, 0x00],
27
+ [0x04, 0x00, 0x00, 0x00],
28
+ [0x08, 0x00, 0x00, 0x00],
29
+ [0x10, 0x00, 0x00, 0x00],
30
+ [0x20, 0x00, 0x00, 0x00],
31
+ [0x40, 0x00, 0x00, 0x00],
32
+ [0x80, 0x00, 0x00, 0x00],
33
+ [0x1b, 0x00, 0x00, 0x00],
34
+ [0x36, 0x00, 0x00, 0x00]].freeze
35
+
36
+ # Block size (in words): no of columns in state (fixed for AES)
37
+ CIPHER_BLOCK_SIZE = 4
38
+
39
+ # AES Cipher function: encrypt 'input' state with Rijndael algorithm
40
+ # applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
41
+ # @param int[] input 16-byte (128-bit) input state array
42
+ # @param int[][] w Key schedule as 2D byte-array (Nr+1 x Nb bytes)
43
+ # @returns int[] Encrypted output state array
44
+ def self.cipher(input, w)
45
+ nr = (w.length / CIPHER_BLOCK_SIZE) - 1 # no of rounds: 10/12/14 for 128/192/256-bit keys
46
+ state = [[], [], [], []] # initialize 4x4 byte-array 'state' with input
47
+
48
+ (0...(4 * CIPHER_BLOCK_SIZE)).each do |i|
49
+ state[i % 4][(i / 4.0).floor] = input[i]
50
+ end
51
+
52
+ state = add_round_key(state, w, 0)
53
+ (1...nr).each do |round|
54
+ state = sub_bytes(state)
55
+ state = shift_rows(state)
56
+ state = mix_columns(state)
57
+ state = add_round_key(state, w, round)
58
+ end
59
+
60
+ state = sub_bytes(state)
61
+ state = shift_rows(state)
62
+ state = add_round_key(state, w, nr)
63
+
64
+ output = []
65
+ (0...(4 * CIPHER_BLOCK_SIZE)).each { |i| output[i] = state[i % 4][(i / 4.0).floor] }
66
+ output
67
+ end
68
+
69
+ # Perform Key Expansion to generate a Key Schedule
70
+ #
71
+ # @param int[] key Key as 16/24/32-byte array
72
+ # @returns int[][] Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes)
73
+ def self.key_expansion(key)
74
+ nk = key.length / 4 # key length (in words): 4/6/8 for 128/192/256-bit keys
75
+ nr = nk + 6 # no of rounds: 10/12/14 for 128/192/256-bit keys
76
+
77
+ w = []
78
+ temp = []
79
+ 0.upto(nk - 1) { |i| w[i] = [key[4 * i], key[(4 * i) + 1], key[(4 * i) + 2], key[(4 * i) + 3]] }
80
+ (nk...(CIPHER_BLOCK_SIZE * (nr + 1))).each do |i|
81
+ w[i] = []
82
+ 0.upto(3) { |t| temp[t] = w[i - 1][t] }
83
+ if (i % nk).zero?
84
+ temp = sub_word(rotate_word(temp))
85
+ 0.upto(3) { |t| temp[t] ^= R_CON[i / nk][t] }
86
+ elsif nk > 6 && i % nk == 4
87
+ temp = sub_word(temp)
88
+ end
89
+ 0.upto(3) { |t| w[i][t] = w[i - nk][t] ^ temp[t] }
90
+ end
91
+ w
92
+ end
93
+
94
+ # apply SBox to state
95
+ def self.sub_bytes(state)
96
+ 0.upto(3) do |r|
97
+ 0.upto(CIPHER_BLOCK_SIZE - 1) { |c| state[r][c] = S_BOX[state[r][c]] }
98
+ end
99
+ state
100
+ end
101
+
102
+ # shift row r of state S left by r bytes
103
+ def self.shift_rows(state)
104
+ t = []
105
+ 1.upto(3) do |r|
106
+ 0.upto(3) { |c| t[c] = state[r][(c + r) % CIPHER_BLOCK_SIZE] } # shift into temp copy
107
+ 0.upto(3) { |c| state[r][c] = t[c] } # and copy back
108
+ end
109
+ state # see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
110
+ end
111
+
112
+ # combine bytes of each col of state S
113
+ def self.mix_columns(state)
114
+ 0.upto(3) do |c|
115
+ a = [] # 'a' is a copy of the current column from 'state'
116
+ b = [] # 'b' is a•{02} in GF(2^8)
117
+ 0.upto(3) do |i|
118
+ a[i] = state[i][c]
119
+ b[i] = (state[i][c] & 0x80).zero? ? (state[i][c] << 1) ^ 0x011b : state[i][c] << 1
120
+ end
121
+ # a[n] ^ b[n] is a•{03} in GF(2^8)
122
+ state[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3] # 2*a0 + 3*a1 + a2 + a3
123
+ state[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3] # a0 * 2*a1 + 3*a2 + a3
124
+ state[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3] # a0 + a1 + 2*a2 + 3*a3
125
+ state[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3] # 3*a0 + a1 + a2 + 2*a3
126
+ end
127
+ state
128
+ end
129
+
130
+ # xor Round Key into state S
131
+ def self.add_round_key(state, w, rnd)
132
+ 0.upto(3) do |r|
133
+ 0.upto(CIPHER_BLOCK_SIZE - 1) { |c| state[r][c] ^= w[(rnd * 4) + c][r] }
134
+ end
135
+ state
136
+ end
137
+
138
+ # apply SBox to 4-byte word w
139
+ def self.sub_word(word)
140
+ 0.upto(3) { |i| word[i] = S_BOX[word[i]] }
141
+ word
142
+ end
143
+
144
+ # rotate 4-byte word w left by one byte
145
+ def self.rotate_word(word)
146
+ tmp = word[0]
147
+ 0.upto(2) { |i| word[i] = word[i + 1] }
148
+ word[3] = tmp
149
+ word
150
+ end
151
+ end
152
+ end