argon2 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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