argon2 0.0.2 → 0.1.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,7 @@
1
+
2
+ #include "argon2.h"
3
+
4
+ int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
5
+ argon2_type type);
6
+
7
+ int decode_string(argon2_context *ctx, const char *str, argon2_type type);
@@ -31,12 +31,11 @@
31
31
  #define UNUSED_PARAMETER(x) (void)(x)
32
32
 
33
33
  static void usage(const char *cmd) {
34
- printf("Usage: %s pwd salt [-d] [-t iterations] [-m memory] "
34
+ printf("Usage: %s salt [-d] [-t iterations] [-m memory] "
35
35
  "[-p parallelism]\n",
36
36
  cmd);
37
-
37
+ printf("\tPassword is read from stdin\n");
38
38
  printf("Parameters:\n");
39
- printf("\tpwd\t\tThe password to hash\n");
40
39
  printf("\tsalt\t\tThe salt to use, at most 16 characters\n");
41
40
  printf("\t-d\t\tUse Argon2d instead of Argon2i (which is the default)\n");
42
41
  printf("\t-t N\t\tSets the number of iterations to N (default = %d)\n",
@@ -66,11 +65,12 @@ Base64-encoded hash string
66
65
  */
67
66
  static void run(uint8_t *out, char *pwd, uint8_t *salt, uint32_t t_cost,
68
67
  uint32_t m_cost, uint32_t lanes, uint32_t threads,
69
- const char *type) {
68
+ argon2_type type) {
70
69
  clock_t start_time, stop_time;
71
- unsigned pwd_length;
72
- argon2_context context;
70
+ unsigned pwdlen;
71
+ char encoded[300];
73
72
  uint32_t i;
73
+ int result;
74
74
 
75
75
  start_time = clock();
76
76
 
@@ -83,57 +83,31 @@ static void run(uint8_t *out, char *pwd, uint8_t *salt, uint32_t t_cost,
83
83
  fatal("salt missing");
84
84
  }
85
85
 
86
- pwd_length = strlen(pwd);
87
-
88
- UNUSED_PARAMETER(threads);
89
-
90
- context.out = out;
91
- context.outlen = OUT_LEN;
92
- context.pwd = (uint8_t *)pwd;
93
- context.pwdlen = pwd_length;
94
- context.salt = salt;
95
- context.saltlen = SALT_LEN;
96
- context.secret = NULL;
97
- context.secretlen = 0;
98
- context.ad = NULL;
99
- context.adlen = 0;
100
- context.t_cost = t_cost;
101
- context.m_cost = m_cost;
102
- context.lanes = lanes;
103
- context.threads = lanes;
104
- context.allocate_cbk = NULL;
105
- context.free_cbk = NULL;
106
- context.flags = ARGON2_FLAG_CLEAR_PASSWORD;
107
-
108
- if (!strcmp(type, "d")) {
109
- int result = argon2d(&context);
110
- if (result != ARGON2_OK)
111
- fatal(error_message(result));
112
- } else if (!strcmp(type, "i")) {
113
- int result = argon2i(&context);
114
- if (result != ARGON2_OK)
115
- fatal(error_message(result));
116
- } else {
117
- secure_wipe_memory(pwd, strlen(pwd));
118
- fatal("wrong Argon2 type");
119
- }
86
+ pwdlen = strlen(pwd);
87
+
88
+ UNUSED_PARAMETER(lanes);
89
+
90
+ result = argon2_hash(t_cost, m_cost, threads, pwd, pwdlen, salt, SALT_LEN,
91
+ out, OUT_LEN, encoded, sizeof encoded, type);
92
+ if (result != ARGON2_OK)
93
+ fatal(error_message(result));
120
94
 
121
95
  stop_time = clock();
122
96
 
123
- /* add back when proper decoding */
124
- /*
125
- char encoded[300];
126
- encode_string(encoded, sizeof encoded, &context);
127
- printf("%s\n", encoded);
128
- */
129
97
  printf("Hash:\t\t");
130
- for (i = 0; i < context.outlen; ++i) {
131
- printf("%02x", context.out[i]);
98
+ for (i = 0; i < OUT_LEN; ++i) {
99
+ printf("%02x", out[i]);
132
100
  }
133
101
  printf("\n");
102
+ printf("Encoded:\t%s\n", encoded);
134
103
 
135
104
  printf("%2.3f seconds\n",
136
105
  ((double)stop_time - start_time) / (CLOCKS_PER_SEC));
106
+
107
+ result = argon2_verify(encoded, pwd, pwdlen, type);
108
+ if (result != ARGON2_OK)
109
+ fatal(error_message(result));
110
+ printf("Verification ok\n");
137
111
  }
138
112
 
139
113
  int main(int argc, char *argv[]) {
@@ -142,26 +116,33 @@ int main(int argc, char *argv[]) {
142
116
  uint32_t t_cost = T_COST_DEF;
143
117
  uint32_t lanes = LANES_DEF;
144
118
  uint32_t threads = THREADS_DEF;
145
- char *pwd = NULL;
146
119
  uint8_t salt[SALT_LEN];
147
- const char *type = "i";
120
+ argon2_type type = Argon2_i;
148
121
  int i;
122
+ size_t n;
123
+ char pwd[128];
149
124
 
150
- if (argc < 3) {
125
+ if (argc < 2) {
151
126
  usage(argv[0]);
152
127
  return ARGON2_MISSING_ARGS;
153
128
  }
154
129
 
155
- /* get password and salt from command line */
156
- pwd = argv[1];
157
- if (strlen(argv[2]) > SALT_LEN) {
130
+ /* get password from stdin */
131
+ while ((n = fread(pwd, 1, sizeof pwd - 1, stdin)) > 0) {
132
+ pwd[n] = '\0';
133
+ if (pwd[n - 1] == '\n')
134
+ pwd[n - 1] = '\0';
135
+ }
136
+
137
+ /* get salt from command line */
138
+ if (strlen(argv[1]) > SALT_LEN) {
158
139
  fatal("salt too long");
159
140
  }
160
141
  memset(salt, 0x00, SALT_LEN); /* pad with null bytes */
161
- memcpy(salt, argv[2], strlen(argv[2]));
142
+ memcpy(salt, argv[1], strlen(argv[1]));
162
143
 
163
144
  /* parse options */
164
- for (i = 3; i < argc; i++) {
145
+ for (i = 2; i < argc; i++) {
165
146
  const char *a = argv[i];
166
147
  unsigned long input = 0;
167
148
  if (!strcmp(a, "-m")) {
@@ -208,12 +189,16 @@ int main(int argc, char *argv[]) {
208
189
  fatal("missing -p argument");
209
190
  }
210
191
  } else if (!strcmp(a, "-d")) {
211
- type = "d";
192
+ type = Argon2_d;
212
193
  } else {
213
194
  fatal("unknown argument");
214
195
  }
215
196
  }
216
- printf("Type:\t\tArgon2%c\n", type[0]);
197
+ if (type == Argon2_i) {
198
+ printf("Type:\t\tArgon2i\n");
199
+ } else {
200
+ printf("Type:\t\tArgon2d\n");
201
+ }
217
202
  printf("Iterations:\t%" PRIu32 " \n", t_cost);
218
203
  printf("Memory:\t\t%" PRIu32 " KiB\n", m_cost);
219
204
  printf("Parallelism:\t%" PRIu32 " \n", lanes);
@@ -5,17 +5,36 @@ require 'argon2/errors'
5
5
  require 'argon2/engine.rb'
6
6
 
7
7
  module Argon2
8
+ # Front-end API for the Argon2 module.
8
9
  class Password
9
10
  def initialize(options = {})
10
- #TODO: Verify inputs
11
11
  @t_cost = options[:t_cost] || 2
12
+ raise ArgonHashFail, "Invalid t_cost" if @t_cost < 1 || @t_cost > 10
12
13
  @m_cost = options[:m_cost] || 16
14
+ raise ArgonHashFail, "Invalid m_cost" if @t_cost < 1 || @t_cost > 31
13
15
  @salt = options[:salt_do_not_supply] || Engine.saltgen
16
+ @secret = options[:secret]
14
17
  end
15
18
 
16
19
  def hash(pass)
20
+ # It is technically possible to include NULL bytes, however our C
21
+ # uses strlen(). It is not deemed suitable to accept a user password
22
+ # with such a character.
23
+ raise ArgonHashFail, "NULL bytes not permitted" if /\0/.match(pass)
17
24
  Argon2::Engine.hash_argon2i_encode(
18
- pass, @salt, @t_cost, @m_cost)
25
+ pass, @salt, @t_cost, @m_cost, @secret)
26
+ end
27
+
28
+ #Helper class, just creates defaults and calls hash()
29
+ def self.hash(pass)
30
+ argon2 = Argon2::Password.new
31
+ argon2.hash(pass)
32
+ end
33
+
34
+ def self.verify_password(pass, hash, secret = nil)
35
+ raise ArgonHashFail, "Invalid hash" unless
36
+ /^\$argon2i\$.{,110}/.match hash
37
+ Argon2::Engine.argon2i_verify(pass, hash, secret)
19
38
  end
20
39
  end
21
40
  end
@@ -1,6 +1,9 @@
1
1
  module Argon2
2
+ # Constants utilised in several parts of the Argon2 module
3
+ # SALT_LEN is a standard recommendation from the Argon2 spec.
2
4
  module Constants
3
5
  SALT_LEN = 16
4
6
  OUT_LEN = 32 #Binary, unencoded output
7
+ ENCODE_LEN = 108 #Encoded output
5
8
  end
6
9
  end
@@ -1,10 +1,10 @@
1
1
  require 'securerandom'
2
2
 
3
3
  module Argon2
4
+ # Generates a random, binary string for use as a salt.
4
5
  class Engine
5
6
  def self.saltgen
6
7
  SecureRandom.random_bytes(Argon2::Constants::SALT_LEN)
7
8
  end
8
9
  end
9
10
  end
10
-
@@ -1,36 +1,41 @@
1
+ # Defines an array of errors that matches the enum list of errors from
2
+ # argon2.h. This allows return values to propagate errors through the FFI.
1
3
  module Argon2
2
4
  class ArgonHashFail < StandardError; end
3
5
  ERRORS = [
4
- 'ARGON2_OK',
5
- 'ARGON2_OUTPUT_PTR_NULL',
6
- 'ARGON2_OUTPUT_TOO_SHORT',
7
- 'ARGON2_OUTPUT_TOO_LONG',
8
- 'ARGON2_PWD_TOO_SHORT',
9
- 'ARGON2_PWD_TOO_LONG',
10
- 'ARGON2_SALT_TOO_SHORT',
11
- 'ARGON2_SALT_TOO_LONG',
12
- 'ARGON2_AD_TOO_SHORT',
13
- 'ARGON2_AD_TOO_LONG',
14
- 'ARGON2_SECRET_TOO_SHORT',
15
- 'ARGON2_SECRET_TOO_LONG',
16
- 'ARGON2_TIME_TOO_SMALL',
17
- 'ARGON2_TIME_TOO_LARGE',
18
- 'ARGON2_MEMORY_TOO_LITTLE',
19
- 'ARGON2_MEMORY_TOO_MUCH',
20
- 'ARGON2_LANES_TOO_FEW',
21
- 'ARGON2_LANES_TOO_MANY',
22
- 'ARGON2_PWD_PTR_MISMATCH',
23
- 'ARGON2_SALT_PTR_MISMATCH',
24
- 'ARGON2_SECRET_PTR_MISMATCH',
6
+ 'ARGON2_OK',
7
+ 'ARGON2_OUTPUT_PTR_NULL',
8
+ 'ARGON2_OUTPUT_TOO_SHORT',
9
+ 'ARGON2_OUTPUT_TOO_LONG',
10
+ 'ARGON2_PWD_TOO_SHORT',
11
+ 'ARGON2_PWD_TOO_LONG',
12
+ 'ARGON2_SALT_TOO_SHORT',
13
+ 'ARGON2_SALT_TOO_LONG',
14
+ 'ARGON2_AD_TOO_SHORT',
15
+ 'ARGON2_AD_TOO_LONG',
16
+ 'ARGON2_SECRET_TOO_SHORT',
17
+ 'ARGON2_SECRET_TOO_LONG',
18
+ 'ARGON2_TIME_TOO_SMALL',
19
+ 'ARGON2_TIME_TOO_LARGE',
20
+ 'ARGON2_MEMORY_TOO_LITTLE',
21
+ 'ARGON2_MEMORY_TOO_MUCH',
22
+ 'ARGON2_LANES_TOO_FEW',
23
+ 'ARGON2_LANES_TOO_MANY',
24
+ 'ARGON2_PWD_PTR_MISMATCH',
25
+ 'ARGON2_SALT_PTR_MISMATCH',
26
+ 'ARGON2_SECRET_PTR_MISMATCH',
25
27
  'ARGON2_AD_PTR_MISMATCH',
26
- 'ARGON2_MEMORY_ALLOCATION_ERROR',
27
- 'ARGON2_FREE_MEMORY_CBK_NULL',
28
- 'ARGON2_ALLOCATE_MEMORY_CBK_NULL',
29
- 'ARGON2_INCORRECT_PARAMETER',
30
- 'ARGON2_INCORRECT_TYPE',
31
- 'ARGON2_OUT_PTR_MISMATCH',
32
- 'ARGON2_THREADS_TOO_FEW',
33
- 'ARGON2_THREADS_TOO_MANY',
34
- 'ARGON2_MISSING_ARGS'
28
+ 'ARGON2_MEMORY_ALLOCATION_ERROR',
29
+ 'ARGON2_FREE_MEMORY_CBK_NULL',
30
+ 'ARGON2_ALLOCATE_MEMORY_CBK_NULL',
31
+ 'ARGON2_INCORRECT_PARAMETER',
32
+ 'ARGON2_INCORRECT_TYPE',
33
+ 'ARGON2_OUT_PTR_MISMATCH',
34
+ 'ARGON2_THREADS_TOO_FEW',
35
+ 'ARGON2_THREADS_TOO_MANY',
36
+ 'ARGON2_MISSING_ARGS',
37
+ 'ARGON2_ENCODING_FAIL',
38
+ 'ARGON2_DECODING_FAIL'
39
+
35
40
  ]
36
41
  end
@@ -2,46 +2,68 @@ require 'ffi'
2
2
  require 'ffi-compiler/loader'
3
3
 
4
4
  module Argon2
5
+ #Direct external bindings. Call these methods via the Engine class to ensure points are dealt with
5
6
  module Ext
6
7
  extend FFI::Library
7
- ffi_lib FFI::Compiler::Loader.find('argon2_wrap')
8
- #int hash_argon2i(void *out, size_t outlen, const void *in, size_t inlen,
9
- # const void *salt, size_t saltlen, unsigned int t_cost,
10
- # unsigned int m_cost);
8
+ ffi_lib FFI::Compiler::Loader.find('argon2_wrap')
11
9
 
12
- attach_function :hash_argon2i, [:pointer, :size_t, :pointer, :size_t,
13
- :pointer, :size_t, :uint, :uint ], :int, :blocking => true
10
+ #int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
11
+ # const uint32_t parallelism, const void *pwd,
12
+ # const size_t pwdlen, const void *salt,
13
+ # const size_t saltlen, void *hash, const size_t hashlen);
14
14
 
15
- #void argon2_wrap(uint8_t *out, char *pwd, uint8_t *salt, uint32_t t_cost,
16
- # uint32_t m_cost, uint32_t lanes);
17
- attach_function :argon2_wrap, [:pointer, :pointer, :pointer, :uint, :uint, :uint], :uint, :blocking => true
15
+ attach_function :argon2i_hash_raw, [
16
+ :uint, :uint, :uint, :pointer,
17
+ :size_t, :pointer, :size_t, :pointer, :size_t], :int, :blocking => true
18
18
 
19
+ #void argon2_wrap(uint8_t *out, char *pwd, uint8_t *salt, uint32_t t_cost,
20
+ # uint32_t m_cost, uint32_t lanes,
21
+ # uint8_t *secret, uint32_t secretlen)
22
+ attach_function :argon2_wrap, [
23
+ :pointer, :pointer, :pointer, :uint,
24
+ :uint, :uint, :pointer, :size_t], :uint, :blocking => true
25
+
26
+ #int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen);
27
+ attach_function :wrap_argon2_verify, [:pointer, :pointer, :size_t,
28
+ :pointer, :size_t], :int, :blocking => true
19
29
  end
20
30
 
31
+ # The engine class shields users from the FFI interface.
32
+ # It is generally not advised to directly use this class.
21
33
  class Engine
22
- # The engine class shields users from the FFI interface.
23
- # It is generally not advised to directly use this class.
24
34
  def self.hash_argon2i(password, salt, t_cost, m_cost)
25
35
  result = ''
26
36
  FFI::MemoryPointer.new(:char, Constants::OUT_LEN) do |buffer|
27
- ret = Ext.hash_argon2i(buffer, Constants::OUT_LEN, password, password.length, salt, salt.length, t_cost, (1<<m_cost))
28
- raise ArgonHashFail.new(ERRORS[ret]) unless ret == 0
37
+ ret = Ext.argon2i_hash_raw(t_cost, 1 << m_cost, 1, password,
38
+ password.length, salt, salt.length,
39
+ buffer, Constants::OUT_LEN)
40
+ raise ArgonHashFail, ERRORS[ret] unless ret == 0
29
41
  result = buffer.read_string(Constants::OUT_LEN)
30
42
  end
31
- result.unpack('H*').join
43
+ result.unpack('H*').join
32
44
  end
33
45
 
34
- def self.hash_argon2i_encode(password, salt, t_cost, m_cost)
46
+ def self.hash_argon2i_encode(password, salt, t_cost, m_cost, secret)
35
47
  result = ''
48
+ secretlen = secret.nil? ? 0 : secret.length
36
49
  if salt.length != Constants::SALT_LEN
37
- raise ArgonHashFail.new("Invalid salt size")
50
+ raise ArgonHashFail, "Invalid salt size"
38
51
  end
39
- FFI::MemoryPointer.new(:char, 300) do |buffer|
40
- ret = Ext.argon2_wrap(buffer, password, salt, t_cost, (1<<m_cost), 1)
41
- raise ArgonHashFail.new(ERRORS[ret]) unless ret == 0
42
- result = buffer.read_string(300)
52
+ FFI::MemoryPointer.new(:char, Constants::ENCODE_LEN) do |buffer|
53
+ ret = Ext.argon2_wrap(buffer, password, salt, t_cost, (1 << m_cost),
54
+ 1, secret, secretlen)
55
+ raise ArgonHashFail, ERRORS[ret] unless ret == 0
56
+ result = buffer.read_string(Constants::ENCODE_LEN)
43
57
  end
44
- result.gsub("\0", '')
58
+ result.delete "\0"
59
+ end
60
+
61
+ def self.argon2i_verify(pwd, hash, secret)
62
+ secretlen = secret.nil? ? 0 : secret.length
63
+ ret = Ext.wrap_argon2_verify(hash, pwd, pwd.length, secret, secretlen)
64
+ return false if ERRORS[ret] == 'ARGON2_DECODING_FAIL'
65
+ raise ArgonHashFail, ERRORS[ret] unless ret == 0
66
+ true
45
67
  end
46
68
  end
47
69
  end
@@ -1,4 +1,4 @@
1
+ # Standard Gem version constant.
1
2
  module Argon2
2
- VERSION = "0.0.2"
3
+ VERSION = "0.1.0"
3
4
  end
4
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: argon2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Technion
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-06 00:00:00.000000000 Z
11
+ date: 2015-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -45,6 +45,9 @@ dependencies:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.10'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.10.5
48
51
  type: :development
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
@@ -52,35 +55,72 @@ dependencies:
52
55
  - - "~>"
53
56
  - !ruby/object:Gem::Version
54
57
  version: '1.10'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.10.5
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: rake
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '10.0'
67
+ version: '10.4'
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 10.4.2
62
71
  type: :development
63
72
  prerelease: false
64
73
  version_requirements: !ruby/object:Gem::Requirement
65
74
  requirements:
66
75
  - - "~>"
67
76
  - !ruby/object:Gem::Version
68
- version: '10.0'
77
+ version: '10.4'
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 10.4.2
69
81
  - !ruby/object:Gem::Dependency
70
82
  name: minitest
71
83
  requirement: !ruby/object:Gem::Requirement
72
84
  requirements:
73
85
  - - "~>"
74
86
  - !ruby/object:Gem::Version
75
- version: '5'
87
+ version: '5.8'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '5.8'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rubocop
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '0.35'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '0.35'
109
+ - !ruby/object:Gem::Dependency
110
+ name: codeclimate-test-reporter
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '0.4'
76
116
  type: :development
77
117
  prerelease: false
78
118
  version_requirements: !ruby/object:Gem::Requirement
79
119
  requirements:
80
120
  - - "~>"
81
121
  - !ruby/object:Gem::Version
82
- version: '5'
83
- description: Not remotely finished Argon2 binding
122
+ version: '0.4'
123
+ description: Argon2 FFI binding
84
124
  email:
85
125
  - technion@lolware.net
86
126
  executables: []
@@ -90,6 +130,7 @@ extra_rdoc_files: []
90
130
  files:
91
131
  - ".gitignore"
92
132
  - ".gitmodules"
133
+ - ".rubocop.yml"
93
134
  - ".travis.yml"
94
135
  - Gemfile
95
136
  - LICENSE.txt
@@ -132,6 +173,9 @@ files:
132
173
  - ext/phc-winner-argon2/src/core.c
133
174
  - ext/phc-winner-argon2/src/core.h
134
175
  - ext/phc-winner-argon2/src/core.o
176
+ - ext/phc-winner-argon2/src/encoding.c
177
+ - ext/phc-winner-argon2/src/encoding.h
178
+ - ext/phc-winner-argon2/src/encoding.o
135
179
  - ext/phc-winner-argon2/src/genkat.c
136
180
  - ext/phc-winner-argon2/src/genkat.h
137
181
  - ext/phc-winner-argon2/src/opt.c