sorcery-argon2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,167 @@
1
+ /* Wrapper for argon Ruby bindings
2
+ * lolware.net
3
+ * Much of this code is based on run.c from the reference implementation
4
+ */
5
+
6
+ #include <stdio.h>
7
+ #include <stdint.h>
8
+ #include <inttypes.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #include <time.h>
12
+
13
+ #include "argon2.h"
14
+ #include "core.h"
15
+ #include "encoding.h"
16
+
17
+ #define T_COST_DEF 3
18
+ #define LOG_M_COST_DEF 12 /* 2^12 = 4 MiB */
19
+ #define LANES_DEF 1
20
+ #define THREADS_DEF 1
21
+ #define OUT_LEN 32
22
+ #define SALT_LEN 16
23
+ #define ENCODE_LEN 96 /* Does not include SALT LEN */
24
+
25
+ /* Workaround for https://github.com/technion/ruby-argon2/issues/8. Hopefully temporary */
26
+ static int wrap_compare(const uint8_t *b1, const uint8_t *b2, size_t len) {
27
+ size_t i;
28
+ uint8_t d = 0U;
29
+
30
+ for (i = 0U; i < len; i++) {
31
+ d |= b1[i] ^ b2[i];
32
+ }
33
+ return (int)((1 & ((d - 1) >> 8)) - 1);
34
+ }
35
+
36
+ int argon2_wrap_version(char *out, const char *pwd, size_t pwd_length,
37
+ uint8_t *salt, uint32_t saltlen, uint32_t t_cost, uint32_t m_cost,
38
+ uint32_t lanes, uint8_t *secret, size_t secretlen, uint32_t version,
39
+ argon2_type type)
40
+ {
41
+ uint8_t hash[OUT_LEN];
42
+ argon2_context context;
43
+
44
+ if (!pwd) {
45
+ return ARGON2_PWD_PTR_MISMATCH;
46
+ }
47
+
48
+ if (!salt) {
49
+ return ARGON2_PWD_PTR_MISMATCH;
50
+ }
51
+
52
+ context.out = hash;
53
+ context.outlen = OUT_LEN;
54
+ context.pwd = (uint8_t *)pwd;
55
+ context.pwdlen = pwd_length;
56
+ context.salt = salt;
57
+ context.saltlen = saltlen;
58
+ context.secret = secret;
59
+ context.secretlen = secretlen;
60
+ context.ad = NULL;
61
+ context.adlen = 0;
62
+ context.t_cost = t_cost;
63
+ context.m_cost = m_cost;
64
+ context.lanes = lanes;
65
+ context.threads = lanes;
66
+ context.allocate_cbk = NULL;
67
+ context.free_cbk = NULL;
68
+ context.flags = 0;
69
+ context.version = version;
70
+
71
+ int result;
72
+ if (type == Argon2_i) {
73
+ result = argon2i_ctx(&context);
74
+ } else if (type == Argon2_id) {
75
+ result = argon2id_ctx(&context);
76
+ } else if (type == Argon2_d) {
77
+ result = argon2d_ctx(&context);
78
+ } else {
79
+ // Unsupported type
80
+ return ARGON2_ENCODING_FAIL;
81
+ }
82
+
83
+ if (result != ARGON2_OK)
84
+ return result;
85
+
86
+ encode_string(out, ENCODE_LEN + saltlen, &context, type);
87
+ return ARGON2_OK;
88
+ }
89
+
90
+ /* Since all new hashes will use latest version, this wraps the
91
+ * function including the version
92
+ */
93
+ int argon2_wrap(char *out, const char *pwd, size_t pwd_length,
94
+ uint8_t *salt, uint32_t saltlen, uint32_t t_cost, uint32_t m_cost,
95
+ uint32_t lanes, uint8_t *secret, size_t secretlen)
96
+ {
97
+ return argon2_wrap_version(out, pwd, pwd_length, salt, saltlen,
98
+ t_cost, m_cost, lanes, secret, secretlen, ARGON2_VERSION_13, Argon2_id);
99
+ }
100
+
101
+ int wrap_argon2_verify(const char *encoded, const char *pwd,
102
+ const size_t pwdlen,
103
+ uint8_t *secret, size_t secretlen)
104
+ {
105
+ argon2_context ctx;
106
+ int ret;
107
+ char *out;
108
+ memset(&ctx, 0, sizeof(argon2_context));
109
+ size_t encoded_len;
110
+ argon2_type type;
111
+
112
+ encoded_len = strlen(encoded);
113
+ /* larger than max possible values */
114
+ ctx.saltlen = encoded_len;
115
+ ctx.outlen = encoded_len;
116
+
117
+ ctx.salt = malloc(ctx.saltlen);
118
+ ctx.out = malloc(ctx.outlen);
119
+ if (!ctx.out || !ctx.salt) {
120
+ free(ctx.salt);
121
+ free(ctx.out);
122
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
123
+ }
124
+
125
+ if (memcmp(encoded, "$argon2id", strlen("$argon2id")) == 0) {
126
+ type = Argon2_id;
127
+ } else if (memcmp(encoded, "$argon2i", strlen("$argon2i")) == 0) {
128
+ type = Argon2_i;
129
+ } else if (memcmp(encoded, "$argon2d", strlen("$argon2d")) == 0) {
130
+ type = Argon2_d;
131
+ } else {
132
+ // Other types not yet supported
133
+ free(ctx.salt);
134
+ free(ctx.out);
135
+ return ARGON2_DECODING_FAIL;
136
+ }
137
+
138
+ if (decode_string(&ctx, encoded, type) != ARGON2_OK) {
139
+ free(ctx.salt);
140
+ free(ctx.out);
141
+ return ARGON2_DECODING_FAIL;
142
+ }
143
+
144
+ out = malloc(ENCODE_LEN + ctx.saltlen);
145
+ if(!out) {
146
+ free(ctx.salt);
147
+ free(ctx.out);
148
+ return ARGON2_DECODING_FAIL;
149
+ }
150
+
151
+ ret = argon2_wrap_version(out, pwd, pwdlen, ctx.salt, ctx.saltlen,
152
+ ctx.t_cost, ctx.m_cost, ctx.lanes, secret, secretlen,
153
+ ctx.version, type);
154
+
155
+ free(ctx.salt);
156
+
157
+ if (ret != ARGON2_OK || wrap_compare((uint8_t*)out, (uint8_t*)encoded,
158
+ strlen(encoded))) {
159
+ free(ctx.out);
160
+ free(out);
161
+ return ARGON2_DECODING_FAIL;
162
+ }
163
+ free(ctx.out);
164
+ free(out);
165
+
166
+ return ARGON2_OK;
167
+ }
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ #I must admit I have no understanding of why this empty file works.
@@ -0,0 +1,117 @@
1
+ /* Wrapper for argon Ruby bindings
2
+ * lolware.net
3
+ * Much of this code is based on run.c from the reference implementation
4
+ */
5
+ #include <stdio.h>
6
+ #include <stdint.h>
7
+ #include <inttypes.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+ #include <time.h>
11
+ #include <assert.h>
12
+
13
+ #include "argon2.h"
14
+
15
+ #define OUT_LEN 32
16
+ #define SALT_LEN 16
17
+
18
+ /**
19
+ * Hashes a password with Argon2i, producing a raw hash
20
+ * @param t_cost Number of iterations
21
+ * @param m_cost Sets memory usage to 2^m_cost kibibytes
22
+ * @param parallelism Number of threads and compute lanes
23
+ * @param pwd Pointer to password
24
+ * @param pwdlen Password size in bytes
25
+ * @param salt Pointer to salt
26
+ * @param saltlen Salt size in bytes
27
+ * @param hash Buffer where to write the raw hash
28
+ * @param hashlen Desired length of the hash in bytes
29
+ * @pre Different parallelism levels will give different results
30
+ * @pre Returns ARGON2_OK if successful
31
+
32
+ int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
33
+ const uint32_t parallelism, const void *pwd,
34
+ const size_t pwdlen, const void *salt,
35
+ const size_t saltlen, void *hash, const size_t hashlen);
36
+
37
+ */
38
+
39
+ unsigned int argon2_wrap(char *out, const char *pwd, size_t pwd_length,
40
+ uint8_t *salt, uint32_t saltlen, uint32_t t_cost, uint32_t m_cost,
41
+ uint32_t lanes, uint8_t *secret, size_t secretlen);
42
+
43
+ int wrap_argon2_verify(const char *encoded, const char *pwd,
44
+ const size_t pwdlen,
45
+ uint8_t *secret, size_t secretlen);
46
+
47
+
48
+ int main()
49
+ {
50
+ unsigned char out[OUT_LEN];
51
+ unsigned char hex_out[OUT_LEN*2 + 4]; /* Allow space for NULL byute */
52
+ char out2[300];
53
+ char *pwd = NULL;
54
+ uint8_t salt[SALT_LEN];
55
+ int i, ret;
56
+
57
+ memset(salt, 0x00, SALT_LEN); /* pad with null bytes */
58
+ memcpy(salt, "somesalt", 8);
59
+
60
+
61
+ #define RAWTEST(T, M, P, PWD, REF) \
62
+ pwd = strdup(PWD); \
63
+ assert(pwd); \
64
+ ret = argon2i_hash_raw(T, 1<<M, P, pwd, strlen(pwd), salt, SALT_LEN, out, OUT_LEN); \
65
+ assert(ret == ARGON2_OK); \
66
+ for(i=0; i<OUT_LEN; ++i ) \
67
+ sprintf((char*)(hex_out + i*2), "%02x", out[i] ); \
68
+ assert(memcmp(hex_out, REF, OUT_LEN*2) == 0); \
69
+ free(pwd); \
70
+ printf( "Ref test: %s: PASS\n", REF);
71
+
72
+ RAWTEST(2, 16, 1, "password", "1c7eeef9e0e969b3024722fc864a1ca9f6ca20da73f9bf3f1731881beae2039e");
73
+ RAWTEST(2, 18, 1, "password", "5c6dfd2712110cf88f1426059b01d87f8210d5368da0e7ee68586e9d4af4954b");
74
+ RAWTEST(2, 8, 1, "password", "dfebf9d4eadd6859f4cc6a9bb20043fd9da7e1e36bdacdbb05ca569f463269f8");
75
+ RAWTEST(1, 16, 1, "password", "fabd1ddbd86a101d326ac2abe79660202b10192925d2fd2483085df94df0c91a");
76
+ RAWTEST(4, 16, 1, "password", "b3b4cb3d6e2c1cb1e7bffdb966ab3ceafae701d6b7789c3f1e6c6b22d82d99d5");
77
+ RAWTEST(2, 16, 1, "differentpassword", "b2db9d7c0d1288951aec4b6e1cd3835ea29a7da2ac13e6f48554a26b127146f9");
78
+ memcpy(salt, "diffsalt", 8);
79
+ RAWTEST(2, 16, 1, "password", "bb6686865f2c1093f70f543c9535f807d5b42d5dc6d71f14a4a7a291913e05e0");
80
+
81
+
82
+ #define WRAP_TEST(T, M, PWD, REF) \
83
+ pwd = strdup(PWD); \
84
+ argon2_wrap(out2, pwd, strlen(PWD), salt, strlen((const char *)salt),T, 1<<M, 1, NULL, 0); \
85
+ free(pwd); \
86
+ assert(memcmp(out2, REF, strlen(REF)) == 0); \
87
+ printf( "Ref test: %s: PASS\n", REF);
88
+
89
+ memcpy(salt, "somesalt", 8);
90
+ WRAP_TEST(2, 16, "password",
91
+ "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc");
92
+
93
+ WRAP_TEST(2, 8, "password",
94
+ "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4");
95
+
96
+ WRAP_TEST(2, 16, "differentpassword",
97
+ "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94");
98
+
99
+ ret = wrap_argon2_verify("$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQAAAAAAAAAAA$3+v51OrdaFn0zGqbsgBD/Z2n4eNr2s27BcpWn0Yyafg", "password",
100
+ strlen("password"), NULL, 0);
101
+ assert(ret == ARGON2_OK);
102
+ printf("Verify OK test i variant: PASS\n");
103
+
104
+ ret = wrap_argon2_verify("$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc", "password",
105
+ strlen("password"), NULL, 0);
106
+ assert(ret == ARGON2_OK);
107
+ printf("Verify OK test id variant: PASS\n");
108
+
109
+ ret = wrap_argon2_verify("$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQAAAAAAAAAAA$iUr0/y4tJvPOFfd6fhwl20W04gQ56ZYXcroZnK3bAB4", "notpassword",
110
+ strlen("notpassword"), NULL, 0);
111
+ assert(ret == ARGON2_DECODING_FAIL);
112
+ printf("Verify FAIL test: PASS\n");
113
+ return 0;
114
+
115
+
116
+ }
117
+
data/lib/argon2.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # This Ruby Gem provides FFI bindings and a simplified interface to the Argon2
5
+ # algorithm. Argon2 is the official winner of the Password Hashing Competition,
6
+ # a several year project to identify a successor to bcrypt/PBKDF/scrypt methods
7
+ # of securely storing passwords. This is an independent project and not official
8
+ # from the PHC team.
9
+ #
10
+ module Argon2; end
11
+
12
+ require 'argon2/constants'
13
+ require 'argon2/ffi_engine'
14
+ require 'argon2/version'
15
+ require 'argon2/errors'
16
+ require 'argon2/engine'
17
+ require 'argon2/password'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Argon2
4
+ ##
5
+ # Constants utilised in several parts of the Argon2 module
6
+ #
7
+ module Constants
8
+ SALT_LEN = 16 # Standard recommendation from the Argon2 spec
9
+ OUT_LEN = 32 # Binary, unencoded output
10
+ ENCODE_LEN = 108 # Encoded output
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Argon2
6
+ ##
7
+ # The engine class shields users from the FFI interface.
8
+ # It is generally not advised to directly use this class.
9
+ #
10
+ class Engine
11
+ ##
12
+ # Generates a random, binary string for use as a salt.
13
+ #
14
+ def self.saltgen
15
+ SecureRandom.random_bytes(Argon2::Constants::SALT_LEN)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Argon2
4
+ ##
5
+ # Generic error to catch anything the Argon2 ruby library throws.
6
+ #
7
+ class Error < StandardError; end
8
+
9
+ ##
10
+ # Various errors for invalid parameters passed to the library.
11
+ #
12
+ module Errors
13
+ ##
14
+ # Raised when an invalid Argon2 hash has been passed to Argon2::Password.new
15
+ #
16
+ class InvalidHash < Argon2::Error; end
17
+
18
+ ##
19
+ # Raised when a valid Argon2 hash was passed to Argon2::Password, but the
20
+ # version information is missing or corrupted.
21
+ #
22
+ class InvalidVersion < InvalidHash; end
23
+
24
+ ##
25
+ # Abstract error class that isn't raised directly, but allows you to catch
26
+ # any cost error, regardless of which value was invalid.
27
+ #
28
+ class InvalidCost < InvalidHash; end
29
+
30
+ ##
31
+ # Raised when an invalid time cost has been passed to
32
+ # Argon2::Password.create, or the hash passed to Argon2::Password.new
33
+ # was valid but the time cost information is missing or corrupted.
34
+ #
35
+ class InvalidTCost < InvalidCost; end
36
+
37
+ ##
38
+ # Raised when an invalid memory cost has been passed to
39
+ # Argon2::Password.create, or the hash passed to Argon2::Password.new
40
+ # was valid but the memory cost information is missing or corrupted.
41
+ #
42
+ class InvalidMCost < InvalidCost; end
43
+
44
+ ##
45
+ # Raised when an invalid parallelism cost has been passed to
46
+ # Argon2::Password.create, or the hash passed to Argon2::Password.new
47
+ # was valid but the parallelism cost information is missing or corrupted.
48
+ #
49
+ class InvalidPCost < InvalidCost; end
50
+
51
+ ##
52
+ # Raised when a non-string object is passed to Argon2::Password.create
53
+ #
54
+ class InvalidPassword < Argon2::Error
55
+ def initialize(msg = "Invalid password (expected a String)")
56
+ super
57
+ end
58
+ end
59
+
60
+ ##
61
+ # Raised when an invalid salt length was passed to
62
+ # Argon2::Engine.hash_argon2id_encode
63
+ #
64
+ class InvalidSaltSize < Argon2::Error; end
65
+
66
+ ##
67
+ # Raised when the output length passed to Argon2::Engine.hash_argon2i or
68
+ # Argon2::Engine.hash_argon2id is invalid.
69
+ #
70
+ class InvalidOutputLength < Argon2::Error; end
71
+
72
+ ##
73
+ # Error raised by/caught from the Argon2 C Library.
74
+ #
75
+ # See Argon2::ERRORS for a full list of related error codes.
76
+ #
77
+ class ExtError < Argon2::Error; end
78
+ end
79
+
80
+ ##
81
+ # Defines an array of errors that matches the enum list of errors from
82
+ # argon2.h. This allows return values to propagate errors through the FFI. Any
83
+ # error from this list will be thrown as an Argon2::Errors::ExtError
84
+ #
85
+ ERRORS = %w[
86
+ ARGON2_OK
87
+ ARGON2_OUTPUT_PTR_NULL
88
+ ARGON2_OUTPUT_TOO_SHORT
89
+ ARGON2_OUTPUT_TOO_LONG
90
+ ARGON2_PWD_TOO_SHORT
91
+ ARGON2_PWD_TOO_LONG
92
+ ARGON2_SALT_TOO_SHORT
93
+ ARGON2_SALT_TOO_LONG
94
+ ARGON2_AD_TOO_SHORT
95
+ ARGON2_AD_TOO_LONG
96
+ ARGON2_SECRET_TOO_SHORT
97
+ ARGON2_SECRET_TOO_LONG
98
+ ARGON2_TIME_TOO_SMALL
99
+ ARGON2_TIME_TOO_LARGE
100
+ ARGON2_MEMORY_TOO_LITTLE
101
+ ARGON2_MEMORY_TOO_MUCH
102
+ ARGON2_LANES_TOO_FEW
103
+ ARGON2_LANES_TOO_MANY
104
+ ARGON2_PWD_PTR_MISMATCH
105
+ ARGON2_SALT_PTR_MISMATCH
106
+ ARGON2_SECRET_PTR_MISMATCH
107
+ ARGON2_AD_PTR_MISMATCH
108
+ ARGON2_MEMORY_ALLOCATION_ERROR
109
+ ARGON2_FREE_MEMORY_CBK_NULL
110
+ ARGON2_ALLOCATE_MEMORY_CBK_NULL
111
+ ARGON2_INCORRECT_PARAMETER
112
+ ARGON2_INCORRECT_TYPE
113
+ ARGON2_OUT_PTR_MISMATCH
114
+ ARGON2_THREADS_TOO_FEW
115
+ ARGON2_THREADS_TOO_MANY
116
+ ARGON2_MISSING_ARGS
117
+ ARGON2_ENCODING_FAIL
118
+ ARGON2_DECODING_FAIL
119
+ ARGON2_THREAD_FAIL
120
+ ].freeze
121
+ end