sorcery-argon2 1.0.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,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