diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,24 @@
+# 1.2.0
+
+- Updated embedded phc-winner-argon2, so that hashes are generated
+  using version 1.3 of the argon2 specification.
+  
+  Note that that hashes generated using this version are different than
+  hashes generated using previous versions, so anything that compares them
+  or relies on them being stable may be broken by this update. 
+  However, Crypto.Argon2.verify will continue to be able to verify
+  hashes produced by previous versions.
+
+- Use CSize for portability instead of Word64, fixing build on 32 bit
+  systems. This changed the constructors of Argon2Exception, an API change.
+
+- Bug fix: Crypto.Argon2.hash returned a ByteString truncated at the first
+  NULL.
+
+- Added use-system-library build flag.
+
+- Build against `base-4.9`
+
 # 1.1.0
 
 - First stable release. Same API as 1.0.0, but now features documentation and
diff --git a/argon2.cabal b/argon2.cabal
--- a/argon2.cabal
+++ b/argon2.cabal
@@ -1,5 +1,5 @@
 name:                argon2
-version:             1.1.0
+version:             1.2.0
 synopsis:            Haskell bindings to libargon2 - the reference implementation of the Argon2 password-hashing function
 -- description:         
 homepage:            https://github.com/ocharles/argon2.git
@@ -12,20 +12,36 @@
 build-type:          Simple
 extra-source-files:  Changelog.md
 cabal-version:       >=1.10
+extra-source-files: phc-winner-argon2/src/blake2/blake2.h
+                    phc-winner-argon2/src/blake2/blake2-impl.h
+                    phc-winner-argon2/src/blake2/blamka-round-ref.h
+                    phc-winner-argon2/include/argon2.h
 
+flag use-system-library
+  description: Link against system libargon2 library rather than embedded copy
+  default: False
+
 library
   exposed-modules:     Crypto.Argon2.FFI
                        Crypto.Argon2
-  build-depends:       base >=4.8 && <4.9, bytestring, text, transformers
+  build-depends:       base >=4.8 && <4.10, bytestring, text, transformers
   hs-source-dirs:      src
   default-language:    Haskell2010
-  c-sources: phc-winner-argon2/src/argon2.c
-             phc-winner-argon2/src/core.c
-             phc-winner-argon2/src/blake2/blake2b.c
-             phc-winner-argon2/src/thread.c
-             phc-winner-argon2/src/ref.c
-             phc-winner-argon2/src/encoding.c
-  include-dirs: phc-winner-argon2/src
+  if flag(use-system-library)
+    extra-libraries: argon2
+  else
+    c-sources: phc-winner-argon2/src/argon2.c
+               phc-winner-argon2/src/core.c
+               phc-winner-argon2/src/blake2/blake2b.c
+               phc-winner-argon2/src/thread.c
+               phc-winner-argon2/src/ref.c
+               phc-winner-argon2/src/encoding.c
+               phc-winner-argon2/src/encoding.h
+               phc-winner-argon2/src/core.h
+               phc-winner-argon2/src/thread.h
+               phc-winner-argon2/src/ref.h
+    include-dirs: phc-winner-argon2/src
+                  phc-winner-argon2/include
 
 test-suite tests
   type: exitcode-stdio-1.0
diff --git a/phc-winner-argon2/include/argon2.h b/phc-winner-argon2/include/argon2.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/include/argon2.h
@@ -0,0 +1,374 @@
+/*
+ * Argon2 source code package
+ *
+ * Written by Daniel Dinu and Dmitry Khovratovich, 2015
+ *
+ * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication
+ * along with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef ARGON2_H
+#define ARGON2_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <limits.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Symbols visibility control */
+#ifdef A2_VISCTL
+#define ARGON2_PUBLIC __attribute__((visibility("default")))
+#elif _MSC_VER
+#define ARGON2_PUBLIC __declspec(dllexport)
+#else
+#define ARGON2_PUBLIC
+#endif
+
+/*
+ * Argon2 input parameter restrictions
+ */
+
+/* Minimum and maximum number of lanes (degree of parallelism) */
+#define ARGON2_MIN_LANES UINT32_C(1)
+#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF)
+
+/* Minimum and maximum number of threads */
+#define ARGON2_MIN_THREADS UINT32_C(1)
+#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF)
+
+/* Number of synchronization points between lanes per pass */
+#define ARGON2_SYNC_POINTS UINT32_C(4)
+
+/* Minimum and maximum digest size in bytes */
+#define ARGON2_MIN_OUTLEN UINT32_C(4)
+#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */
+#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */
+
+#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
+/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */
+#define ARGON2_MAX_MEMORY_BITS                                                 \
+    ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1))
+#define ARGON2_MAX_MEMORY                                                      \
+    ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS)
+
+/* Minimum and maximum number of passes */
+#define ARGON2_MIN_TIME UINT32_C(1)
+#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum password length in bytes */
+#define ARGON2_MIN_PWD_LENGTH UINT32_C(0)
+#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum associated data length in bytes */
+#define ARGON2_MIN_AD_LENGTH UINT32_C(0)
+#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum salt length in bytes */
+#define ARGON2_MIN_SALT_LENGTH UINT32_C(8)
+#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum key length in bytes */
+#define ARGON2_MIN_SECRET UINT32_C(0)
+#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF)
+
+#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0)
+#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1)
+#define ARGON2_FLAG_CLEAR_MEMORY (UINT32_C(1) << 2)
+#define ARGON2_DEFAULT_FLAGS (ARGON2_FLAG_CLEAR_MEMORY)
+
+/* Error codes */
+typedef enum Argon2_ErrorCodes {
+    ARGON2_OK = 0,
+
+    ARGON2_OUTPUT_PTR_NULL = -1,
+
+    ARGON2_OUTPUT_TOO_SHORT = -2,
+    ARGON2_OUTPUT_TOO_LONG = -3,
+
+    ARGON2_PWD_TOO_SHORT = -4,
+    ARGON2_PWD_TOO_LONG = -5,
+
+    ARGON2_SALT_TOO_SHORT = -6,
+    ARGON2_SALT_TOO_LONG = -7,
+
+    ARGON2_AD_TOO_SHORT = -8,
+    ARGON2_AD_TOO_LONG = -9,
+
+    ARGON2_SECRET_TOO_SHORT = -10,
+    ARGON2_SECRET_TOO_LONG = -11,
+
+    ARGON2_TIME_TOO_SMALL = -12,
+    ARGON2_TIME_TOO_LARGE = -13,
+
+    ARGON2_MEMORY_TOO_LITTLE = -14,
+    ARGON2_MEMORY_TOO_MUCH = -15,
+
+    ARGON2_LANES_TOO_FEW = -16,
+    ARGON2_LANES_TOO_MANY = -17,
+
+    ARGON2_PWD_PTR_MISMATCH = -18,    /* NULL ptr with non-zero length */
+    ARGON2_SALT_PTR_MISMATCH = -19,   /* NULL ptr with non-zero length */
+    ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */
+    ARGON2_AD_PTR_MISMATCH = -21,     /* NULL ptr with non-zero length */
+
+    ARGON2_MEMORY_ALLOCATION_ERROR = -22,
+
+    ARGON2_FREE_MEMORY_CBK_NULL = -23,
+    ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24,
+
+    ARGON2_INCORRECT_PARAMETER = -25,
+    ARGON2_INCORRECT_TYPE = -26,
+
+    ARGON2_OUT_PTR_MISMATCH = -27,
+
+    ARGON2_THREADS_TOO_FEW = -28,
+    ARGON2_THREADS_TOO_MANY = -29,
+
+    ARGON2_MISSING_ARGS = -30,
+
+    ARGON2_ENCODING_FAIL = -31,
+
+    ARGON2_DECODING_FAIL = -32,
+
+    ARGON2_THREAD_FAIL = -33,
+
+    ARGON2_DECODING_LENGTH_FAIL = -34,
+
+    ARGON2_VERIFY_MISMATCH = -35
+} argon2_error_codes;
+
+/* Memory allocator types --- for external allocation */
+typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate);
+typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate);
+
+/* Argon2 external data structures */
+
+/*
+ *****
+ * Context: structure to hold Argon2 inputs:
+ *  output array and its length,
+ *  password and its length,
+ *  salt and its length,
+ *  secret and its length,
+ *  associated data and its length,
+ *  number of passes, amount of used memory (in KBytes, can be rounded up a bit)
+ *  number of parallel threads that will be run.
+ * All the parameters above affect the output hash value.
+ * Additionally, two function pointers can be provided to allocate and
+ * deallocate the memory (if NULL, memory will be allocated internally).
+ * Also, three flags indicate whether to erase password, secret as soon as they
+ * are pre-hashed (and thus not needed anymore), and the entire memory
+ *****
+ * Simplest situation: you have output array out[8], password is stored in
+ * pwd[32], salt is stored in salt[16], you do not have keys nor associated
+ * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
+ * 4 parallel lanes.
+ * You want to erase the password, but you're OK with last pass not being
+ * erased. You want to use the default memory allocator.
+ * Then you initialize:
+ Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false)
+ */
+typedef struct Argon2_Context {
+    uint8_t *out;    /* output array */
+    uint32_t outlen; /* digest length */
+
+    uint8_t *pwd;    /* password array */
+    uint32_t pwdlen; /* password length */
+
+    uint8_t *salt;    /* salt array */
+    uint32_t saltlen; /* salt length */
+
+    uint8_t *secret;    /* key array */
+    uint32_t secretlen; /* key length */
+
+    uint8_t *ad;    /* associated data array */
+    uint32_t adlen; /* associated data length */
+
+    uint32_t t_cost;  /* number of passes */
+    uint32_t m_cost;  /* amount of memory requested (KB) */
+    uint32_t lanes;   /* number of lanes */
+    uint32_t threads; /* maximum number of threads */
+
+    uint32_t version; /* version number */
+
+    allocate_fptr allocate_cbk; /* pointer to memory allocator */
+    deallocate_fptr free_cbk;   /* pointer to memory deallocator */
+
+    uint32_t flags; /* array of bool options */
+} argon2_context;
+
+/* Argon2 primitive type */
+typedef enum Argon2_type { Argon2_d = 0, Argon2_i = 1 } argon2_type;
+
+/* Version of the algorithm */
+typedef enum Argon2_version {
+    ARGON2_VERSION_10 = 0x10,
+    ARGON2_VERSION_13 = 0x13,
+    ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
+} argon2_version;
+
+/*
+ * Function that performs memory-hard hashing with certain degree of parallelism
+ * @param  context  Pointer to the Argon2 internal structure
+ * @return Error code if smth is wrong, ARGON2_OK otherwise
+ */
+ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type);
+
+/**
+ * Hashes a password with Argon2i, producing an encoded hash
+ * @param t_cost Number of iterations
+ * @param m_cost Sets memory usage to m_cost kibibytes
+ * @param parallelism Number of threads and compute lanes
+ * @param pwd Pointer to password
+ * @param pwdlen Password size in bytes
+ * @param salt Pointer to salt
+ * @param saltlen Salt size in bytes
+ * @param hashlen Desired length of the hash in bytes
+ * @param encoded Buffer where to write the encoded hash
+ * @param encodedlen Size of the buffer (thus max size of the encoded hash)
+ * @pre   Different parallelism levels will give different results
+ * @pre   Returns ARGON2_OK if successful
+ */
+ARGON2_PUBLIC int argon2i_hash_encoded(const uint32_t t_cost,
+                                       const uint32_t m_cost,
+                                       const uint32_t parallelism,
+                                       const void *pwd, const size_t pwdlen,
+                                       const void *salt, const size_t saltlen,
+                                       const size_t hashlen, char *encoded,
+                                       const size_t encodedlen);
+
+/**
+ * Hashes a password with Argon2i, producing a raw hash by allocating memory at
+ * @hash
+ * @param t_cost Number of iterations
+ * @param m_cost Sets memory usage to m_cost kibibytes
+ * @param parallelism Number of threads and compute lanes
+ * @param pwd Pointer to password
+ * @param pwdlen Password size in bytes
+ * @param salt Pointer to salt
+ * @param saltlen Salt size in bytes
+ * @param hash Buffer where to write the raw hash - updated by the function
+ * @param hashlen Desired length of the hash in bytes
+ * @pre   Different parallelism levels will give different results
+ * @pre   Returns ARGON2_OK if successful
+ */
+ARGON2_PUBLIC int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
+                                   const uint32_t parallelism, const void *pwd,
+                                   const size_t pwdlen, const void *salt,
+                                   const size_t saltlen, void *hash,
+                                   const size_t hashlen);
+
+ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost,
+                                       const uint32_t m_cost,
+                                       const uint32_t parallelism,
+                                       const void *pwd, const size_t pwdlen,
+                                       const void *salt, const size_t saltlen,
+                                       const size_t hashlen, char *encoded,
+                                       const size_t encodedlen);
+
+ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
+                                   const uint32_t parallelism, const void *pwd,
+                                   const size_t pwdlen, const void *salt,
+                                   const size_t saltlen, void *hash,
+                                   const size_t hashlen);
+
+/* generic function underlying the above ones */
+ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
+                              const uint32_t parallelism, const void *pwd,
+                              const size_t pwdlen, const void *salt,
+                              const size_t saltlen, void *hash,
+                              const size_t hashlen, char *encoded,
+                              const size_t encodedlen, argon2_type type,
+                              const uint32_t version);
+
+/**
+ * Verifies a password against an encoded string
+ * Encoded string is restricted as in validate_inputs()
+ * @param encoded String encoding parameters, salt, hash
+ * @param pwd Pointer to password
+ * @pre   Returns ARGON2_OK if successful
+ */
+ARGON2_PUBLIC int argon2i_verify(const char *encoded, const void *pwd,
+                                 const size_t pwdlen);
+
+ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd,
+                                 const size_t pwdlen);
+
+/* generic function underlying the above ones */
+ARGON2_PUBLIC int argon2_verify(const char *encoded, const void *pwd,
+                                const size_t pwdlen, argon2_type type);
+
+/**
+ * Argon2d: Version of Argon2 that picks memory blocks depending
+ * on the password and salt. Only for side-channel-free
+ * environment!!
+ *****
+ * @param  context  Pointer to current Argon2 context
+ * @return  Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2d_ctx(argon2_context *context);
+
+/**
+ * Argon2i: Version of Argon2 that picks memory blocks
+ * independent on the password and salt. Good for side-channels,
+ * but worse w.r.t. tradeoff attacks if only one pass is used.
+ *****
+ * @param  context  Pointer to current Argon2 context
+ * @return  Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2i_ctx(argon2_context *context);
+
+/**
+ * Verify if a given password is correct for Argon2d hashing
+ * @param  context  Pointer to current Argon2 context
+ * @param  hash  The password hash to verify. The length of the hash is
+ * specified by the context outlen member
+ * @return  Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2d_verify_ctx(argon2_context *context, const char *hash);
+
+/**
+ * Verify if a given password is correct for Argon2i hashing
+ * @param  context  Pointer to current Argon2 context
+ * @param  hash  The password hash to verify. The length of the hash is
+ * specified by the context outlen member
+ * @return  Zero if successful, a non zero error code otherwise
+ */
+ARGON2_PUBLIC int argon2i_verify_ctx(argon2_context *context, const char *hash);
+
+/* generic function underlying the above ones */
+ARGON2_PUBLIC int argon2_verify_ctx(argon2_context *context, const char *hash,
+                                    argon2_type type);
+
+/**
+ * Get the associated error message for given error code
+ * @return  The error message associated with the given error code
+ */
+ARGON2_PUBLIC const char *argon2_error_message(int error_code);
+
+/**
+ * Returns the encoded hash length for the given input parameters
+ * @param t_cost  Number of iterations
+ * @param m_cost  Memory usage in kibibytes
+ * @param parallelism  Number of threads; used to compute lanes
+ * @param saltlen  Salt size in bytes
+ * @param hashlen  Hash size in bytes
+ * @return  The encoded hash length in bytes
+ */
+ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost,
+                                       uint32_t parallelism, uint32_t saltlen,
+                                       uint32_t hashlen);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/phc-winner-argon2/src/argon2.c b/phc-winner-argon2/src/argon2.c
--- a/phc-winner-argon2/src/argon2.c
+++ b/phc-winner-argon2/src/argon2.c
@@ -19,94 +19,8 @@
 #include "encoding.h"
 #include "core.h"
 
-/* Error messages */
-static const char *Argon2_ErrorMessage[] = {
-    /*{ARGON2_OK, */ "OK",
-    /*},
 
-    {ARGON2_OUTPUT_PTR_NULL, */ "Output pointer is NULL",
-    /*},
-
-{ARGON2_OUTPUT_TOO_SHORT, */ "Output is too short",
-    /*},
-{ARGON2_OUTPUT_TOO_LONG, */ "Output is too long",
-    /*},
-
-{ARGON2_PWD_TOO_SHORT, */ "Password is too short",
-    /*},
-{ARGON2_PWD_TOO_LONG, */ "Password is too long",
-    /*},
-
-{ARGON2_SALT_TOO_SHORT, */ "Salt is too short",
-    /*},
-{ARGON2_SALT_TOO_LONG, */ "Salt is too long",
-    /*},
-
-{ARGON2_AD_TOO_SHORT, */ "Associated data is too short",
-    /*},
-{ARGON2_AD_TOO_LONG, */ "Associated date is too long",
-    /*},
-
-{ARGON2_SECRET_TOO_SHORT, */ "Secret is too short",
-    /*},
-{ARGON2_SECRET_TOO_LONG, */ "Secret is too long",
-    /*},
-
-{ARGON2_TIME_TOO_SMALL, */ "Time cost is too small",
-    /*},
-{ARGON2_TIME_TOO_LARGE, */ "Time cost is too large",
-    /*},
-
-{ARGON2_MEMORY_TOO_LITTLE, */ "Memory cost is too small",
-    /*},
-{ARGON2_MEMORY_TOO_MUCH, */ "Memory cost is too large",
-    /*},
-
-{ARGON2_LANES_TOO_FEW, */ "Too few lanes",
-    /*},
-{ARGON2_LANES_TOO_MANY, */ "Too many lanes",
-    /*},
-
-{ARGON2_PWD_PTR_MISMATCH, */ "Password pointer is NULL, but password length is not 0",
-    /*},
-{ARGON2_SALT_PTR_MISMATCH, */ "Salt pointer is NULL, but salt length is not 0",
-    /*},
-{ARGON2_SECRET_PTR_MISMATCH, */ "Secret pointer is NULL, but secret length is not 0",
-    /*},
-{ARGON2_AD_PTR_MISMATCH, */ "Associated data pointer is NULL, but ad length is not 0",
-    /*},
-
-{ARGON2_MEMORY_ALLOCATION_ERROR, */ "Memory allocation error",
-    /*},
-
-{ARGON2_FREE_MEMORY_CBK_NULL, */ "The free memory callback is NULL",
-    /*},
-{ARGON2_ALLOCATE_MEMORY_CBK_NULL, */ "The allocate memory callback is NULL",
-    /*},
-
-{ARGON2_INCORRECT_PARAMETER, */ "Argon2_Context context is NULL",
-    /*},
-{ARGON2_INCORRECT_TYPE, */ "There is no such version of Argon2",
-    /*},
-
-{ARGON2_OUT_PTR_MISMATCH, */ "Output pointer mismatch",
-    /*},
-
-{ARGON2_THREADS_TOO_FEW, */ "Not enough threads",
-    /*},
-{ARGON2_THREADS_TOO_MANY, */ "Too many threads",
-    /*},
-{ARGON2_MISSING_ARGS, */ "Missing arguments",
-    /*},
-{ARGON2_ENCODING_FAIL, */ "Encoding failed",
-    /*},
-{ARGON2_DECODING_FAIL, */ "Decoding failed",
-    /*},
-{ARGON2_THREAD_FAIL */ "Threading failure", /*},*/
-};
-
-
-int argon2_core(argon2_context *context, argon2_type type) {
+int argon2_ctx(argon2_context *context, argon2_type type) {
     /* 1. Validate all inputs */
     int result = validate_inputs(context);
     uint32_t memory_blocks, segment_length;
@@ -132,6 +46,7 @@
     /* Ensure that all segments have equal length */
     memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS);
 
+    instance.version = context->version;
     instance.memory = NULL;
     instance.passes = context->t_cost;
     instance.memory_blocks = memory_blocks;
@@ -162,29 +77,23 @@
     return ARGON2_OK;
 }
 
-
 int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
                 const uint32_t parallelism, const void *pwd,
                 const size_t pwdlen, const void *salt, const size_t saltlen,
                 void *hash, const size_t hashlen, char *encoded,
-                const size_t encodedlen, argon2_type type) {
+                const size_t encodedlen, argon2_type type, 
+                const uint32_t version){
 
     argon2_context context;
     int result;
     uint8_t *out;
 
-    /* Detect and reject overflowing sizes */
-    /* TODO: This should probably be fixed in the function signature */
-    if (pwdlen > UINT32_MAX) {
-        return ARGON2_PWD_TOO_LONG;
-    }
-
-    if (hashlen > UINT32_MAX) {
+    if (hashlen > ARGON2_MAX_OUTLEN) {
         return ARGON2_OUTPUT_TOO_LONG;
     }
 
-    if (saltlen > UINT32_MAX) {
-        return ARGON2_SALT_TOO_LONG;
+    if (hashlen < ARGON2_MIN_OUTLEN) {
+        return ARGON2_OUTPUT_TOO_SHORT;
     }
 
     out = malloc(hashlen);
@@ -209,11 +118,12 @@
     context.allocate_cbk = NULL;
     context.free_cbk = NULL;
     context.flags = ARGON2_DEFAULT_FLAGS;
+    context.version = version;
 
-    result = argon2_core(&context, type);
+    result = argon2_ctx(&context, type);
 
     if (result != ARGON2_OK) {
-        memset(out, 0x00, hashlen);
+        secure_wipe_memory(out, hashlen);
         free(out);
         return result;
     }
@@ -225,14 +135,14 @@
 
     /* if encoding requested, write it */
     if (encoded && encodedlen) {
-        if (!encode_string(encoded, encodedlen, &context, type)) {
-            memset(out, 0x00, hashlen);
-            memset(encoded, 0x00, encodedlen);
+        if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) {
+            secure_wipe_memory(out, hashlen); /* wipe buffers if error */
+            secure_wipe_memory(encoded, encodedlen);
             free(out);
             return ARGON2_ENCODING_FAIL;
         }
     }
-
+    secure_wipe_memory(out, hashlen);
     free(out);
 
     return ARGON2_OK;
@@ -245,7 +155,8 @@
                          char *encoded, const size_t encodedlen) {
 
     return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
-                       NULL, hashlen, encoded, encodedlen, Argon2_i);
+                       NULL, hashlen, encoded, encodedlen, Argon2_i,
+                       ARGON2_VERSION_NUMBER);
 }
 
 int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
@@ -254,7 +165,7 @@
                      const size_t saltlen, void *hash, const size_t hashlen) {
 
     return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
-                       hash, hashlen, NULL, 0, Argon2_i);
+                       hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER);
 }
 
 int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
@@ -264,7 +175,8 @@
                          char *encoded, const size_t encodedlen) {
 
     return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
-                       NULL, hashlen, encoded, encodedlen, Argon2_d);
+                       NULL, hashlen, encoded, encodedlen, Argon2_d,
+                       ARGON2_VERSION_NUMBER);
 }
 
 int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
@@ -273,7 +185,7 @@
                      const size_t saltlen, void *hash, const size_t hashlen) {
 
     return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
-                       hash, hashlen, NULL, 0, Argon2_d);
+                       hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER);
 }
 
 static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) {
@@ -292,12 +204,30 @@
     argon2_context ctx;
     uint8_t *out;
     int ret;
+    int decode_result;
+    uint32_t encoded_len;
+    size_t encoded_len_tmp;
 
+    if(encoded == NULL) {
+        return ARGON2_DECODING_FAIL;
+    }
+
+    encoded_len_tmp = strlen(encoded);
     /* max values, to be updated in decode_string */
-    ctx.adlen = 512;
-    ctx.saltlen = 512;
-    ctx.outlen = 512;
+    if (UINT32_MAX < encoded_len_tmp) {
+        return ARGON2_DECODING_FAIL;
+    }
 
+    encoded_len = (uint32_t)encoded_len_tmp;
+    ctx.adlen = encoded_len;
+    ctx.saltlen = encoded_len;
+    ctx.outlen = encoded_len;
+    ctx.allocate_cbk = NULL;
+    ctx.free_cbk = NULL;
+    ctx.secret = NULL;
+    ctx.secretlen = 0;
+    ctx.pwdlen = 0;
+    ctx.pwd = NULL;
     ctx.ad = malloc(ctx.adlen);
     ctx.salt = malloc(ctx.saltlen);
     ctx.out = malloc(ctx.outlen);
@@ -314,30 +244,29 @@
         free(ctx.out);
         return ARGON2_MEMORY_ALLOCATION_ERROR;
     }
-
-    if(decode_string(&ctx, encoded, type) != 1) {
+    decode_result = decode_string(&ctx, encoded, type);
+    if (decode_result != ARGON2_OK) {
         free(ctx.ad);
         free(ctx.salt);
         free(ctx.out);
         free(out);
-        return ARGON2_DECODING_FAIL;
+        return decode_result;
     }
 
-    ret = argon2_hash(ctx.t_cost, ctx.m_cost, ctx.threads, pwd, pwdlen, ctx.salt,
-                ctx.saltlen, out, ctx.outlen, NULL, 0, type);
+    ret = argon2_hash(ctx.t_cost, ctx.m_cost, ctx.threads, pwd, pwdlen,
+                      ctx.salt, ctx.saltlen, out, ctx.outlen, NULL, 0, type,
+                      ctx.version);
 
     free(ctx.ad);
     free(ctx.salt);
 
-    if (ret != ARGON2_OK || argon2_compare(out, ctx.out, ctx.outlen)) {
-        free(out);
-        free(ctx.out);
-        return ARGON2_DECODING_FAIL;
+    if (ret == ARGON2_OK && argon2_compare(out, ctx.out, ctx.outlen)) {
+        ret = ARGON2_VERIFY_MISMATCH;
     }
     free(out);
     free(ctx.out);
 
-    return ARGON2_OK;
+    return ret;
 }
 
 int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
@@ -350,17 +279,22 @@
     return argon2_verify(encoded, pwd, pwdlen, Argon2_d);
 }
 
-int argon2d(argon2_context *context) { return argon2_core(context, Argon2_d); }
+int argon2d_ctx(argon2_context *context) {
+    return argon2_ctx(context, Argon2_d);
+}
 
-int argon2i(argon2_context *context) { return argon2_core(context, Argon2_i); }
+int argon2i_ctx(argon2_context *context) {
+    return argon2_ctx(context, Argon2_i);
+}
 
-int verify_d(argon2_context *context, const char *hash) {
+int argon2_verify_ctx(argon2_context *context, const char *hash,
+                      argon2_type type) {
     int result;
     if (0 == context->outlen || NULL == hash) {
         return ARGON2_OUT_PTR_MISMATCH;
     }
 
-    result = argon2_core(context, Argon2_d);
+    result = argon2_ctx(context, type);
 
     if (ARGON2_OK != result) {
         return result;
@@ -369,33 +303,96 @@
     return 0 == memcmp(hash, context->out, context->outlen);
 }
 
-int verify_i(argon2_context *context, const char *hash) {
-    int result;
-    if (0 == context->outlen || NULL == hash) {
-        return ARGON2_OUT_PTR_MISMATCH;
-    }
+int argon2d_verify_ctx(argon2_context *context, const char *hash) {
+    return argon2_verify_ctx(context, hash, Argon2_d);
+}
 
-    result = argon2_core(context, Argon2_i);
+int argon2i_verify_ctx(argon2_context *context, const char *hash) {
+    return argon2_verify_ctx(context, hash, Argon2_i);
+}
 
-    if (ARGON2_OK != result) {
-        return result;
+const char *argon2_error_message(int error_code) {
+    switch (error_code) {
+    case ARGON2_OK:
+        return "OK";
+    case ARGON2_OUTPUT_PTR_NULL:
+        return "Output pointer is NULL";
+    case ARGON2_OUTPUT_TOO_SHORT:
+        return "Output is too short";
+    case ARGON2_OUTPUT_TOO_LONG:
+        return "Output is too long";
+    case ARGON2_PWD_TOO_SHORT:
+        return "Password is too short";
+    case ARGON2_PWD_TOO_LONG:
+        return "Password is too long";
+    case ARGON2_SALT_TOO_SHORT:
+        return "Salt is too short";
+    case ARGON2_SALT_TOO_LONG:
+        return "Salt is too long";
+    case ARGON2_AD_TOO_SHORT:
+        return "Associated data is too short";
+    case ARGON2_AD_TOO_LONG:
+        return "Associated data is too long";
+    case ARGON2_SECRET_TOO_SHORT:
+        return "Secret is too short";
+    case ARGON2_SECRET_TOO_LONG:
+        return "Secret is too long";
+    case ARGON2_TIME_TOO_SMALL:
+        return "Time cost is too small";
+    case ARGON2_TIME_TOO_LARGE:
+        return "Time cost is too large";
+    case ARGON2_MEMORY_TOO_LITTLE:
+        return "Memory cost is too small";
+    case ARGON2_MEMORY_TOO_MUCH:
+        return "Memory cost is too large";
+    case ARGON2_LANES_TOO_FEW:
+        return "Too few lanes";
+    case ARGON2_LANES_TOO_MANY:
+        return "Too many lanes";
+    case ARGON2_PWD_PTR_MISMATCH:
+        return "Password pointer is NULL, but password length is not 0";
+    case ARGON2_SALT_PTR_MISMATCH:
+        return "Salt pointer is NULL, but salt length is not 0";
+    case ARGON2_SECRET_PTR_MISMATCH:
+        return "Secret pointer is NULL, but secret length is not 0";
+    case ARGON2_AD_PTR_MISMATCH:
+        return "Associated data pointer is NULL, but ad length is not 0";
+    case ARGON2_MEMORY_ALLOCATION_ERROR:
+        return "Memory allocation error";
+    case ARGON2_FREE_MEMORY_CBK_NULL:
+        return "The free memory callback is NULL";
+    case ARGON2_ALLOCATE_MEMORY_CBK_NULL:
+        return "The allocate memory callback is NULL";
+    case ARGON2_INCORRECT_PARAMETER:
+        return "Argon2_Context context is NULL";
+    case ARGON2_INCORRECT_TYPE:
+        return "There is no such version of Argon2";
+    case ARGON2_OUT_PTR_MISMATCH:
+        return "Output pointer mismatch";
+    case ARGON2_THREADS_TOO_FEW:
+        return "Not enough threads";
+    case ARGON2_THREADS_TOO_MANY:
+        return "Too many threads";
+    case ARGON2_MISSING_ARGS:
+        return "Missing arguments";
+    case ARGON2_ENCODING_FAIL:
+        return "Encoding failed";
+    case ARGON2_DECODING_FAIL:
+        return "Decoding failed";
+    case ARGON2_THREAD_FAIL:
+        return "Threading failure";
+    case ARGON2_DECODING_LENGTH_FAIL:
+        return "Some of encoded parameters are too long or too short";
+    case ARGON2_VERIFY_MISMATCH:
+        return "The password does not match the supplied hash";
+    default:
+        return "Unknown error code";
     }
-
-    return 0 == memcmp(hash, context->out, context->outlen);
 }
 
-
-const char *error_message(int error_code) {
-    enum {
-        /* Make sure---at compile time---that the enum size matches the array
-           size */
-        ERROR_STRING_CHECK =
-            1 /
-            !!((sizeof(Argon2_ErrorMessage) / sizeof(Argon2_ErrorMessage[0])) ==
-               ARGON2_ERROR_CODES_LENGTH)
-    };
-    if (error_code >= 0 && error_code < ARGON2_ERROR_CODES_LENGTH) {
-        return Argon2_ErrorMessage[(argon2_error_codes)error_code];
-    }
-    return "Unknown error code.";
+size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism,
+                         uint32_t saltlen, uint32_t hashlen) {
+    return strlen("$argon2x$v=$m=,t=,p=$$") + numlen(t_cost) + numlen(m_cost)
+        + numlen(parallelism) + b64len(saltlen) + b64len(hashlen)
+        + numlen(ARGON2_VERSION_NUMBER) + 1;
 }
diff --git a/phc-winner-argon2/src/blake2/blake2-impl.h b/phc-winner-argon2/src/blake2/blake2-impl.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/src/blake2/blake2-impl.h
@@ -0,0 +1,139 @@
+#ifndef PORTABLE_BLAKE2_IMPL_H
+#define PORTABLE_BLAKE2_IMPL_H
+
+#include <stdint.h>
+#include <string.h>
+
+#if defined(_MSC_VER)
+#define BLAKE2_INLINE __inline
+#elif defined(__GNUC__) || defined(__clang__)
+#define BLAKE2_INLINE __inline__
+#else
+#define BLAKE2_INLINE
+#endif
+
+/* Argon2 Team - Begin Code */
+/*
+   Not an exhaustive list, but should cover the majority of modern platforms
+   Additionally, the code will always be correct---this is only a performance
+   tweak.
+*/
+#if (defined(__BYTE_ORDER__) &&                                                \
+     (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) ||                           \
+    defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
+    defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) ||       \
+    defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) ||                \
+    defined(_M_ARM)
+#define NATIVE_LITTLE_ENDIAN
+#endif
+/* Argon2 Team - End Code */
+
+static BLAKE2_INLINE uint32_t load32(const void *src) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+    uint32_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    const uint8_t *p = (const uint8_t *)src;
+    uint32_t w = *p++;
+    w |= (uint32_t)(*p++) << 8;
+    w |= (uint32_t)(*p++) << 16;
+    w |= (uint32_t)(*p++) << 24;
+    return w;
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load64(const void *src) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+    uint64_t w;
+    memcpy(&w, src, sizeof w);
+    return w;
+#else
+    const uint8_t *p = (const uint8_t *)src;
+    uint64_t w = *p++;
+    w |= (uint64_t)(*p++) << 8;
+    w |= (uint64_t)(*p++) << 16;
+    w |= (uint64_t)(*p++) << 24;
+    w |= (uint64_t)(*p++) << 32;
+    w |= (uint64_t)(*p++) << 40;
+    w |= (uint64_t)(*p++) << 48;
+    w |= (uint64_t)(*p++) << 56;
+    return w;
+#endif
+}
+
+static BLAKE2_INLINE void store32(void *dst, uint32_t w) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+    memcpy(dst, &w, sizeof w);
+#else
+    uint8_t *p = (uint8_t *)dst;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+#endif
+}
+
+static BLAKE2_INLINE void store64(void *dst, uint64_t w) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+    memcpy(dst, &w, sizeof w);
+#else
+    uint8_t *p = (uint8_t *)dst;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load48(const void *src) {
+    const uint8_t *p = (const uint8_t *)src;
+    uint64_t w = *p++;
+    w |= (uint64_t)(*p++) << 8;
+    w |= (uint64_t)(*p++) << 16;
+    w |= (uint64_t)(*p++) << 24;
+    w |= (uint64_t)(*p++) << 32;
+    w |= (uint64_t)(*p++) << 40;
+    return w;
+}
+
+static BLAKE2_INLINE void store48(void *dst, uint64_t w) {
+    uint8_t *p = (uint8_t *)dst;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+    w >>= 8;
+    *p++ = (uint8_t)w;
+}
+
+static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) {
+    return (w >> c) | (w << (32 - c));
+}
+
+static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
+    return (w >> c) | (w << (64 - c));
+}
+
+void secure_wipe_memory(void *v, size_t n);
+
+#endif
diff --git a/phc-winner-argon2/src/blake2/blake2.h b/phc-winner-argon2/src/blake2/blake2.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/src/blake2/blake2.h
@@ -0,0 +1,74 @@
+#ifndef PORTABLE_BLAKE2_H
+#define PORTABLE_BLAKE2_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+enum blake2b_constant {
+    BLAKE2B_BLOCKBYTES = 128,
+    BLAKE2B_OUTBYTES = 64,
+    BLAKE2B_KEYBYTES = 64,
+    BLAKE2B_SALTBYTES = 16,
+    BLAKE2B_PERSONALBYTES = 16
+};
+
+#pragma pack(push, 1)
+typedef struct __blake2b_param {
+    uint8_t digest_length;                   /* 1 */
+    uint8_t key_length;                      /* 2 */
+    uint8_t fanout;                          /* 3 */
+    uint8_t depth;                           /* 4 */
+    uint32_t leaf_length;                    /* 8 */
+    uint64_t node_offset;                    /* 16 */
+    uint8_t node_depth;                      /* 17 */
+    uint8_t inner_length;                    /* 18 */
+    uint8_t reserved[14];                    /* 32 */
+    uint8_t salt[BLAKE2B_SALTBYTES];         /* 48 */
+    uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
+} blake2b_param;
+#pragma pack(pop)
+
+typedef struct __blake2b_state {
+    uint64_t h[8];
+    uint64_t t[2];
+    uint64_t f[2];
+    uint8_t buf[BLAKE2B_BLOCKBYTES];
+    unsigned buflen;
+    unsigned outlen;
+    uint8_t last_node;
+} blake2b_state;
+
+/* Ensure param structs have not been wrongly padded */
+/* Poor man's static_assert */
+enum {
+    blake2_size_check_0 = 1 / !!(CHAR_BIT == 8),
+    blake2_size_check_2 =
+        1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT)
+};
+
+/* Streaming API */
+int blake2b_init(blake2b_state *S, size_t outlen);
+int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
+                     size_t keylen);
+int blake2b_init_param(blake2b_state *S, const blake2b_param *P);
+int blake2b_update(blake2b_state *S, const void *in, size_t inlen);
+int blake2b_final(blake2b_state *S, void *out, size_t outlen);
+
+/* Simple API */
+int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
+            const void *key, size_t keylen);
+
+/* Argon2 Team - Begin Code */
+int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen);
+/* Argon2 Team - End Code */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/phc-winner-argon2/src/blake2/blake2b.c b/phc-winner-argon2/src/blake2/blake2b.c
--- a/phc-winner-argon2/src/blake2/blake2b.c
+++ b/phc-winner-argon2/src/blake2/blake2b.c
@@ -44,7 +44,7 @@
 }
 
 static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) {
-    burn(S, sizeof(*S));      /* wipe */
+    secure_wipe_memory(S, sizeof(*S));      /* wipe */
     blake2b_set_lastblock(S); /* invalidate for further use */
 }
 
@@ -140,7 +140,7 @@
         memset(block, 0, BLAKE2B_BLOCKBYTES);
         memcpy(block, key, keylen);
         blake2b_update(S, block, BLAKE2B_BLOCKBYTES);
-        burn(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */
+        secure_wipe_memory(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */
     }
     return 0;
 }
@@ -267,9 +267,9 @@
     }
 
     memcpy(out, buffer, S->outlen);
-    burn(buffer, sizeof(buffer));
-    burn(S->buf, sizeof(S->buf));
-    burn(S->h, sizeof(S->h));
+    secure_wipe_memory(buffer, sizeof(buffer));
+    secure_wipe_memory(S->buf, sizeof(S->buf));
+    secure_wipe_memory(S->h, sizeof(S->h));
     return 0;
 }
 
@@ -307,7 +307,7 @@
     ret = blake2b_final(&S, out, outlen);
 
 fail:
-    burn(&S, sizeof(S));
+    secure_wipe_memory(&S, sizeof(S));
     return ret;
 }
 
@@ -365,7 +365,7 @@
         memcpy(out, out_buffer, toproduce);
     }
 fail:
-    burn(&blake_state, sizeof(blake_state));
+    secure_wipe_memory(&blake_state, sizeof(blake_state));
     return ret;
 #undef TRY
 }
diff --git a/phc-winner-argon2/src/blake2/blamka-round-ref.h b/phc-winner-argon2/src/blake2/blamka-round-ref.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/src/blake2/blamka-round-ref.h
@@ -0,0 +1,39 @@
+#ifndef BLAKE_ROUND_MKA_H
+#define BLAKE_ROUND_MKA_H
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+/*designed by the Lyra PHC team */
+static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) {
+    const uint64_t m = UINT64_C(0xFFFFFFFF);
+    const uint64_t xy = (x & m) * (y & m);
+    return x + y + 2 * xy;
+}
+
+#define G(a, b, c, d)                                                          \
+    do {                                                                       \
+        a = fBlaMka(a, b);                                                     \
+        d = rotr64(d ^ a, 32);                                                 \
+        c = fBlaMka(c, d);                                                     \
+        b = rotr64(b ^ c, 24);                                                 \
+        a = fBlaMka(a, b);                                                     \
+        d = rotr64(d ^ a, 16);                                                 \
+        c = fBlaMka(c, d);                                                     \
+        b = rotr64(b ^ c, 63);                                                 \
+    } while ((void)0, 0)
+
+#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,   \
+                           v12, v13, v14, v15)                                 \
+    do {                                                                       \
+        G(v0, v4, v8, v12);                                                    \
+        G(v1, v5, v9, v13);                                                    \
+        G(v2, v6, v10, v14);                                                   \
+        G(v3, v7, v11, v15);                                                   \
+        G(v0, v5, v10, v15);                                                   \
+        G(v1, v6, v11, v12);                                                   \
+        G(v2, v7, v8, v13);                                                    \
+        G(v3, v4, v9, v14);                                                    \
+    } while ((void)0, 0)
+
+#endif
diff --git a/phc-winner-argon2/src/core.c b/phc-winner-argon2/src/core.c
--- a/phc-winner-argon2/src/core.c
+++ b/phc-winner-argon2/src/core.c
@@ -93,7 +93,6 @@
         if (!*memory) {
             return ARGON2_MEMORY_ALLOCATION_ERROR;
         }
-
         return ARGON2_OK;
     } else {
         return ARGON2_MEMORY_ALLOCATION_ERROR;
@@ -104,7 +103,7 @@
 #if defined(_MSC_VER) && VC_GE_2005(_MSC_VER)
     SecureZeroMemory(v, n);
 #elif defined memset_s
-    memset_s(v, n);
+    memset_s(v, n, 0, n);
 #elif defined(__OpenBSD__)
     explicit_bzero(v, n);
 #else
@@ -283,6 +282,8 @@
                 if (l >= instance->threads) {
                     rc = argon2_thread_join(thread[l - instance->threads]);
                     if (rc) {
+                        free(thr_data);
+                        free(thread);
                         return ARGON2_THREAD_FAIL;
                     }
                 }
@@ -299,6 +300,8 @@
                 rc = argon2_thread_create(&thread[l], &fill_segment_thr,
                                           (void *)&thr_data[l]);
                 if (rc) {
+                    free(thr_data);
+                    free(thread);
                     return ARGON2_THREAD_FAIL;
                 }
 
@@ -417,7 +420,7 @@
         return ARGON2_MEMORY_TOO_MUCH;
     }
 
-    if (context->m_cost < 8*context->lanes) {
+    if (context->m_cost < 8 * context->lanes) {
         return ARGON2_MEMORY_TOO_LITTLE;
     }
 
@@ -505,7 +508,7 @@
     store32(&value, context->t_cost);
     blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
-    store32(&value, ARGON2_VERSION_NUMBER);
+    store32(&value, context->version);
     blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     store32(&value, (uint32_t)type);
@@ -572,7 +575,7 @@
         if (ARGON2_OK != result) {
             return result;
         }
-        memcpy(&(instance->memory), p, sizeof(instance->memory));
+        instance->memory = (block *)p;
     } else {
         result = allocate_memory(&(instance->memory), instance->memory_blocks);
         if (ARGON2_OK != result) {
@@ -602,4 +605,3 @@
 
     return ARGON2_OK;
 }
-
diff --git a/phc-winner-argon2/src/core.h b/phc-winner-argon2/src/core.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/src/core.h
@@ -0,0 +1,219 @@
+/*
+ * Argon2 source code package
+ *
+ * Written by Daniel Dinu and Dmitry Khovratovich, 2015
+ *
+ * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with
+ * this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef ARGON2_CORE_H
+#define ARGON2_CORE_H
+
+#include "argon2.h"
+
+#if defined(_MSC_VER)
+#define ALIGN(n) __declspec(align(16))
+#elif defined(__GNUC__) || defined(__clang)
+#define ALIGN(x) __attribute__((__aligned__(x)))
+#else
+#define ALIGN(x)
+#endif
+
+#define CONST_CAST(x) (x)(uintptr_t)
+
+/*************************Argon2 internal
+ * constants**************************************************/
+
+enum argon2_core_constants {
+    /* Memory block size in bytes */
+    ARGON2_BLOCK_SIZE = 1024,
+    ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
+    ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
+
+    /* Number of pseudo-random values generated by one call to Blake in Argon2i
+       to
+       generate reference block positions */
+    ARGON2_ADDRESSES_IN_BLOCK = 128,
+
+    /* Pre-hashing digest length and its extension*/
+    ARGON2_PREHASH_DIGEST_LENGTH = 64,
+    ARGON2_PREHASH_SEED_LENGTH = 72
+};
+
+/*************************Argon2 internal data
+ * types**************************************************/
+
+/*
+ * Structure for the (1KB) memory block implemented as 128 64-bit words.
+ * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
+ * bounds checking).
+ */
+typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block;
+
+/*****************Functions that work with the block******************/
+
+/* Initialize each byte of the block with @in */
+void init_block_value(block *b, uint8_t in);
+
+/* Copy block @src to block @dst */
+void copy_block(block *dst, const block *src);
+
+/* XOR @src onto @dst bytewise */
+void xor_block(block *dst, const block *src);
+
+/*
+ * Argon2 instance: memory pointer, number of passes, amount of memory, type,
+ * and derived values.
+ * Used to evaluate the number and location of blocks to construct in each
+ * thread
+ */
+typedef struct Argon2_instance_t {
+    block *memory;          /* Memory pointer */
+    uint32_t version;
+    uint32_t passes;        /* Number of passes */
+    uint32_t memory_blocks; /* Number of blocks in memory */
+    uint32_t segment_length;
+    uint32_t lane_length;
+    uint32_t lanes;
+    uint32_t threads;
+    argon2_type type;
+    int print_internals; /* whether to print the memory blocks */
+} argon2_instance_t;
+
+/*
+ * Argon2 position: where we construct the block right now. Used to distribute
+ * work between threads.
+ */
+typedef struct Argon2_position_t {
+    uint32_t pass;
+    uint32_t lane;
+    uint8_t slice;
+    uint32_t index;
+} argon2_position_t;
+
+/*Struct that holds the inputs for thread handling FillSegment*/
+typedef struct Argon2_thread_data {
+    argon2_instance_t *instance_ptr;
+    argon2_position_t pos;
+} argon2_thread_data;
+
+/*************************Argon2 core
+ * functions**************************************************/
+
+/* Allocates memory to the given pointer
+ * @param memory pointer to the pointer to the memory
+ * @param m_cost number of blocks to allocate in the memory
+ * @return ARGON2_OK if @memory is a valid pointer and memory is allocated
+ */
+int allocate_memory(block **memory, uint32_t m_cost);
+
+/* Function that securely cleans the memory
+ * @param mem Pointer to the memory
+ * @param s Memory size in bytes
+ */
+void secure_wipe_memory(void *v, size_t n);
+
+/* Clears memory
+ * @param instance pointer to the current instance
+ * @param clear_memory indicates if we clear the memory with zeros.
+ */
+void clear_memory(argon2_instance_t *instance, int clear);
+
+/* Deallocates memory
+ * @param memory pointer to the blocks
+ */
+void free_memory(block *memory);
+
+/*
+ * Computes absolute position of reference block in the lane following a skewed
+ * distribution and using a pseudo-random value as input
+ * @param instance Pointer to the current instance
+ * @param position Pointer to the current position
+ * @param pseudo_rand 32-bit pseudo-random value used to determine the position
+ * @param same_lane Indicates if the block will be taken from the current lane.
+ * If so we can reference the current segment
+ * @pre All pointers must be valid
+ */
+uint32_t index_alpha(const argon2_instance_t *instance,
+                     const argon2_position_t *position, uint32_t pseudo_rand,
+                     int same_lane);
+
+/*
+ * Function that validates all inputs against predefined restrictions and return
+ * an error code
+ * @param context Pointer to current Argon2 context
+ * @return ARGON2_OK if everything is all right, otherwise one of error codes
+ * (all defined in <argon2.h>
+ */
+int validate_inputs(const argon2_context *context);
+
+/*
+ * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears
+ * password and secret if needed
+ * @param  context  Pointer to the Argon2 internal structure containing memory
+ * pointer, and parameters for time and space requirements.
+ * @param  blockhash Buffer for pre-hashing digest
+ * @param  type Argon2 type
+ * @pre    @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes
+ * allocated
+ */
+void initial_hash(uint8_t *blockhash, argon2_context *context,
+                  argon2_type type);
+
+/*
+ * Function creates first 2 blocks per lane
+ * @param instance Pointer to the current instance
+ * @param blockhash Pointer to the pre-hashing digest
+ * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values
+ */
+void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance);
+
+/*
+ * Function allocates memory, hashes the inputs with Blake,  and creates first
+ * two blocks. Returns the pointer to the main memory with 2 blocks per lane
+ * initialized
+ * @param  context  Pointer to the Argon2 internal structure containing memory
+ * pointer, and parameters for time and space requirements.
+ * @param  instance Current Argon2 instance
+ * @return Zero if successful, -1 if memory failed to allocate. @context->state
+ * will be modified if successful.
+ */
+int initialize(argon2_instance_t *instance, argon2_context *context);
+
+/*
+ * XORing the last block of each lane, hashing it, making the tag. Deallocates
+ * the memory.
+ * @param context Pointer to current Argon2 context (use only the out parameters
+ * from it)
+ * @param instance Pointer to current instance of Argon2
+ * @pre instance->state must point to necessary amount of memory
+ * @pre context->out must point to outlen bytes of memory
+ * @pre if context->free_cbk is not NULL, it should point to a function that
+ * deallocates memory
+ */
+void finalize(const argon2_context *context, argon2_instance_t *instance);
+
+/*
+ * Function that fills the segment using previous segments also from other
+ * threads
+ * @param instance Pointer to the current instance
+ * @param position Current position
+ * @pre all block pointers must be valid
+ */
+void fill_segment(const argon2_instance_t *instance,
+                  argon2_position_t position);
+
+/*
+ * Function that fills the entire memory t_cost times based on the first two
+ * blocks in each lane
+ * @param instance Pointer to the current instance
+ * @return ARGON2_OK if successful, @context->state
+ */
+int fill_memory_blocks(argon2_instance_t *instance);
+
+#endif
diff --git a/phc-winner-argon2/src/encoding.c b/phc-winner-argon2/src/encoding.c
--- a/phc-winner-argon2/src/encoding.c
+++ b/phc-winner-argon2/src/encoding.c
@@ -3,9 +3,10 @@
 #include <string.h>
 #include <limits.h>
 #include "encoding.h"
+#include "core.h"
 
-#/*
- * Example code for a decoder and encoder of "hash strings", with Argon2i
+/*
+ * Example code for a decoder and encoder of "hash strings", with Argon2
  * parameters.
  *
  * This code comprises three sections:
@@ -17,7 +18,7 @@
  *   the relevant functions are made public (non-static) and be given
  *   reasonable names to avoid collisions with other functions.
  *
- *   -- The second section is specific to Argon2i. It encodes and decodes
+ *   -- The second section is specific to Argon2. It encodes and decodes
  *   the parameters, salts and outputs. It does not compute the hash
  *   itself.
  *
@@ -58,7 +59,7 @@
  * Some macros for constant-time comparisons. These work over values in
  * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
  */
-#define EQ(x, y) ((((0U-((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
+#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
 #define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
 #define GE(x, y) (GT(y, x) ^ 0xFF)
 #define LT(x, y) GT(y, x)
@@ -122,11 +123,11 @@
         acc_len += 8;
         while (acc_len >= 6) {
             acc_len -= 6;
-            *dst++ = (char) b64_byte_to_char((acc >> acc_len) & 0x3F);
+            *dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F);
         }
     }
     if (acc_len > 0) {
-        *dst++ = (char) b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
+        *dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
     }
     *dst++ = 0;
     return olen;
@@ -224,16 +225,17 @@
 
 /* ==================================================================== */
 /*
- * Code specific to Argon2i.
+ * Code specific to Argon2.
  *
  * The code below applies the following format:
  *
- *  $argon2i$m=<num>,t=<num>,p=<num>[,keyid=<bin>][,data=<bin>][$<bin>[$<bin>]]
+ *  $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>[,keyid=<bin>][,data=<bin>][$<bin>[$<bin>]]
  *
- * where <num> is a decimal integer (positive, fits in an 'unsigned long')
- * and <bin> is Base64-encoded data (no '=' padding characters, no newline
- * or whitespace). The "keyid" is a binary identifier for a key (up to 8
- * bytes); "data" is associated data (up to 32 bytes). When the 'keyid'
+ * where <T> is either 'd' or 'i', <num> is a decimal integer (positive, fits in
+ * an 'unsigned long'), and <bin> is Base64-encoded data (no '=' padding
+ * characters, no newline or whitespace).
+ * The "keyid" is a binary identifier for a key (up to 8 bytes);
+ * "data" is associated data (up to 32 bytes). When the 'keyid'
  * (resp. the 'data') is empty, then it is ommitted from the output.
  *
  * The last two binary chunks (encoded in Base64) are, in that order,
@@ -242,20 +244,19 @@
  * The output length is always exactly 32 bytes.
  */
 
-/*
- * Decode an Argon2i hash string into the provided structure 'ctx'.
- * Returned value is 1 on success, 0 on error.
- */
 int decode_string(argon2_context *ctx, const char *str, argon2_type type) {
+
+/* check for prefix */
 #define CC(prefix)                                                             \
     do {                                                                       \
         size_t cc_len = strlen(prefix);                                        \
         if (strncmp(str, prefix, cc_len) != 0) {                               \
-            return 0;                                                          \
+            return ARGON2_DECODING_FAIL;                                       \
         }                                                                      \
         str += cc_len;                                                         \
     } while ((void)0, 0)
 
+/* prefix checking with supplied code */
 #define CC_opt(prefix, code)                                                   \
     do {                                                                       \
         size_t cc_len = strlen(prefix);                                        \
@@ -265,12 +266,13 @@
         }                                                                      \
     } while ((void)0, 0)
 
+/* Decoding  prefix into decimal */
 #define DECIMAL(x)                                                             \
     do {                                                                       \
         unsigned long dec_x;                                                   \
         str = decode_decimal(str, &dec_x);                                     \
         if (str == NULL) {                                                     \
-            return 0;                                                          \
+            return ARGON2_DECODING_FAIL;                                       \
         }                                                                      \
         (x) = dec_x;                                                           \
     } while ((void)0, 0)
@@ -280,7 +282,7 @@
         size_t bin_len = (max_len);                                            \
         str = from_base64(buf, &bin_len, str);                                 \
         if (str == NULL || bin_len > UINT32_MAX) {                             \
-            return 0;                                                          \
+            return ARGON2_DECODING_FAIL;                                       \
         }                                                                      \
         (len) = (uint32_t)bin_len;                                             \
     } while ((void)0, 0)
@@ -288,16 +290,22 @@
     size_t maxadlen = ctx->adlen;
     size_t maxsaltlen = ctx->saltlen;
     size_t maxoutlen = ctx->outlen;
+    int validation_result;
 
     ctx->adlen = 0;
     ctx->saltlen = 0;
     ctx->outlen = 0;
+    ctx->pwdlen = 0;
+
     if (type == Argon2_i)
         CC("$argon2i");
     else if (type == Argon2_d)
         CC("$argon2d");
     else
-        return 0;
+        return ARGON2_INCORRECT_TYPE;
+    ctx->version = ARGON2_VERSION_10;
+    /* Reading the version number if the default is suppressed */
+    CC_opt("$v=", DECIMAL(ctx->version));
     CC("$m=");
     DECIMAL(ctx->m_cost);
     CC(",t=");
@@ -306,75 +314,39 @@
     DECIMAL(ctx->lanes);
     ctx->threads = ctx->lanes;
 
-    /*
-     * Both m and t must be no more than 2^32-1. The tests below
-     * use a shift by 30 bits to avoid a direct comparison with
-     * 0xFFFFFFFF, which may trigger a spurious compiler warning
-     * on machines where 'unsigned long' is a 32-bit type.
-     */
-    if (ctx->m_cost < 1 || (ctx->m_cost >> 30) > 3) {
-        return 0;
-    }
-    if (ctx->t_cost < 1 || (ctx->t_cost >> 30) > 3) {
-        return 0;
-    }
-
-    /*
-     * The parallelism p must be between 1 and 255. The memory cost
-     * parameter, expressed in kilobytes, must be at least 8 times
-     * the value of p.
-     */
-    if (ctx->lanes < 1 || ctx->lanes > 255) {
-        return 0;
-    }
-    if (ctx->m_cost < (ctx->lanes << 3)) {
-        return 0;
-    }
-
     CC_opt(",data=", BIN(ctx->ad, maxadlen, ctx->adlen));
     if (*str == 0) {
-        return 1;
+        return ARGON2_OK;
     }
     CC("$");
     BIN(ctx->salt, maxsaltlen, ctx->saltlen);
-    if (ctx->saltlen < 8) {
-        return 0;
-    }
     if (*str == 0) {
-        return 1;
+        return ARGON2_OK;
     }
     CC("$");
     BIN(ctx->out, maxoutlen, ctx->outlen);
-    if (ctx->outlen < 12) {
-        return 0;
+    validation_result = validate_inputs(ctx);
+    if (validation_result != ARGON2_OK) {
+        return validation_result;
     }
-    return *str == 0;
-
+    if (*str == 0) {
+        return ARGON2_OK;
+    } else {
+        return ARGON2_DECODING_FAIL;
+    }
 #undef CC
 #undef CC_opt
 #undef DECIMAL
 #undef BIN
 }
 
-/*
- * encode an argon2i hash string into the provided buffer. 'dst_len'
- * contains the size, in characters, of the 'dst' buffer; if 'dst_len'
- * is less than the number of required characters (including the
- * terminating 0), then this function returns 0.
- *
- * if pp->output_len is 0, then the hash string will be a salt string
- * (no output). if pp->salt_len is also 0, then the string will be a
- * parameter-only string (no salt and no output).
- *
- * on success, 1 is returned.
- */
 int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
                   argon2_type type) {
 #define SS(str)                                                                \
     do {                                                                       \
         size_t pp_len = strlen(str);                                           \
         if (pp_len >= dst_len) {                                               \
-            return 0;                                                          \
+            return ARGON2_ENCODING_FAIL;                                       \
         }                                                                      \
         memcpy(dst, str, pp_len + 1);                                          \
         dst += pp_len;                                                         \
@@ -392,18 +364,24 @@
     do {                                                                       \
         size_t sb_len = to_base64(dst, dst_len, buf, len);                     \
         if (sb_len == (size_t)-1) {                                            \
-            return 0;                                                          \
+            return ARGON2_ENCODING_FAIL;                                       \
         }                                                                      \
         dst += sb_len;                                                         \
         dst_len -= sb_len;                                                     \
     } while ((void)0, 0)
 
     if (type == Argon2_i)
-        SS("$argon2i$m=");
+        SS("$argon2i$v=");
     else if (type == Argon2_d)
-        SS("$argon2d$m=");
+        SS("$argon2d$v=");
     else
-        return 0;
+        return ARGON2_ENCODING_FAIL;
+
+    if (validate_inputs(ctx) != ARGON2_OK) {
+        return validate_inputs(ctx);
+    }
+    SX(ctx->version);
+    SS("$m=");
     SX(ctx->m_cost);
     SS(",t=");
     SX(ctx->t_cost);
@@ -416,19 +394,44 @@
     }
 
     if (ctx->saltlen == 0)
-        return 1;
+        return ARGON2_OK;
 
     SS("$");
     SB(ctx->salt, ctx->saltlen);
 
     if (ctx->outlen == 0)
-        return 1;
+        return ARGON2_OK;
 
     SS("$");
     SB(ctx->out, ctx->outlen);
-    return 1;
+    return ARGON2_OK;
 
 #undef SS
 #undef SX
 #undef SB
 }
+
+size_t b64len(uint32_t len) {
+    size_t olen = ((size_t)len / 3) << 2;
+
+    switch (len % 3) {
+    case 2:
+        olen++;
+    /* fall through */
+    case 1:
+        olen += 2;
+        break;
+    }
+
+    return olen;
+}
+
+size_t numlen(uint32_t num) {
+    size_t len = 1;
+    while (num >= 10) {
+        ++len;
+        num = num / 10;
+    }
+    return len;
+}
+
diff --git a/phc-winner-argon2/src/encoding.h b/phc-winner-argon2/src/encoding.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/src/encoding.h
@@ -0,0 +1,40 @@
+#ifndef ENCODING_H
+#define ENCODING_H
+#include "argon2.h"
+
+#define ARGON2_MAX_DECODED_LANES UINT32_C(255)
+#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8)
+#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12)
+
+/*
+* encode an Argon2 hash string into the provided buffer. 'dst_len'
+* contains the size, in characters, of the 'dst' buffer; if 'dst_len'
+* is less than the number of required characters (including the
+* terminating 0), then this function returns ARGON2_ENCODING_ERROR.
+*
+* if ctx->outlen is 0, then the hash string will be a salt string
+* (no output). if ctx->saltlen is also 0, then the string will be a
+* parameter-only string (no salt and no output).
+*
+* on success, ARGON2_OK is returned.
+*
+* No other parameters are checked
+*/
+int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
+                  argon2_type type);
+
+/*
+* Decodes an Argon2 hash string into the provided structure 'ctx'.
+* The fields ctx.saltlen, ctx.adlen, ctx.outlen set the maximal salt, ad, out
+* length values that are allowed; invalid input string causes an error.
+* Returned value is ARGON2_OK on success, other ARGON2_ codes on error.
+*/
+int decode_string(argon2_context *ctx, const char *str, argon2_type type);
+
+/* Returns the length of the encoded byte stream with length len */
+size_t b64len(uint32_t len);
+
+/* Returns the length of the encoded number num */
+size_t numlen(uint32_t num);
+
+#endif
diff --git a/phc-winner-argon2/src/ref.c b/phc-winner-argon2/src/ref.c
--- a/phc-winner-argon2/src/ref.c
+++ b/phc-winner-argon2/src/ref.c
@@ -22,15 +22,55 @@
 #include "blake2/blake2-impl.h"
 #include "blake2/blake2.h"
 
+
 void fill_block(const block *prev_block, const block *ref_block,
-                block *next_block) {
+    block *next_block) {
     block blockR, block_tmp;
     unsigned i;
 
     copy_block(&blockR, ref_block);
     xor_block(&blockR, prev_block);
     copy_block(&block_tmp, &blockR);
+            /*Now blockR = ref_block + prev_block and bloc_tmp = ref_block + prev_block */
+                /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
+                (16,17,..31)... finally (112,113,...127) */
+    for (i = 0; i < 8; ++i) {
+        BLAKE2_ROUND_NOMSG(
+            blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2],
+            blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5],
+            blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8],
+            blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11],
+            blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14],
+            blockR.v[16 * i + 15]);
+    }
 
+    /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
+    (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
+    for (i = 0; i < 8; i++) {
+        BLAKE2_ROUND_NOMSG(
+            blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16],
+            blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33],
+            blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64],
+            blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81],
+            blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112],
+            blockR.v[2 * i + 113]);
+    }
+
+    copy_block(next_block, &block_tmp);
+    xor_block(next_block, &blockR);
+}
+
+
+void fill_block_with_xor(const block *prev_block, const block *ref_block,
+                block *next_block) {
+    block blockR, block_tmp;
+    unsigned i;
+
+    copy_block(&blockR, ref_block);
+    xor_block(&blockR, prev_block);
+    copy_block(&block_tmp, &blockR);
+    xor_block(&block_tmp, next_block); /*Saving the next block contents for XOR over*/
+    /*Now blockR = ref_block + prev_block and bloc_tmp = ref_block + prev_block + next_block*/
     /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
        (16,17,..31)... finally (112,113,...127) */
     for (i = 0; i < 8; ++i) {
@@ -62,12 +102,11 @@
 void generate_addresses(const argon2_instance_t *instance,
                         const argon2_position_t *position,
                         uint64_t *pseudo_rands) {
-    block zero_block, input_block, address_block;
+    block zero_block, input_block, address_block,tmp_block;
     uint32_t i;
 
     init_block_value(&zero_block, 0);
     init_block_value(&input_block, 0);
-    init_block_value(&address_block, 0);
 
     if (instance != NULL && position != NULL) {
         input_block.v[0] = position->pass;
@@ -80,8 +119,10 @@
         for (i = 0; i < instance->segment_length; ++i) {
             if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
                 input_block.v[6]++;
-                fill_block(&zero_block, &input_block, &address_block);
-                fill_block(&zero_block, &address_block, &address_block);
+                init_block_value(&tmp_block, 0);
+                init_block_value(&address_block, 0);
+                fill_block_with_xor(&zero_block, &input_block, &tmp_block);
+                fill_block_with_xor(&zero_block, &tmp_block, &address_block);
             }
 
             pseudo_rands[i] = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
@@ -169,7 +210,18 @@
         ref_block =
             instance->memory + instance->lane_length * ref_lane + ref_index;
         curr_block = instance->memory + curr_offset;
-        fill_block(instance->memory + prev_offset, ref_block, curr_block);
+        if (ARGON2_VERSION_10 == instance->version) {
+            /* version 1.2.1 and earlier: overwrite, not XOR */
+            fill_block(instance->memory + prev_offset, ref_block, curr_block);
+        } else {
+            if(0 == position.pass) {
+                fill_block(instance->memory + prev_offset, ref_block,
+                           curr_block);
+            } else {
+                fill_block_with_xor(instance->memory + prev_offset, ref_block,
+                                    curr_block);
+            }
+        }
     }
 
     free(pseudo_rands);
diff --git a/phc-winner-argon2/src/ref.h b/phc-winner-argon2/src/ref.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/src/ref.h
@@ -0,0 +1,51 @@
+/*
+ * Argon2 source code package
+ *
+ * Written by Daniel Dinu and Dmitry Khovratovich, 2015
+ *
+ * This work is licensed under a Creative Commons CC0 1.0 License/Waiver.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with
+ * this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#ifndef ARGON2_REF_H
+#define ARGON2_REF_H
+
+#include "core.h"
+
+/*
+ * Function fills a new memory block by XORing over @next_block. @next_block must be initialized
+ * @param prev_block Pointer to the previous block
+ * @param ref_block Pointer to the reference block
+ * @param next_block Pointer to the block to be constructed
+ * @pre all block pointers must be valid
+ */
+void fill_block_with_xor(const block *prev_block, const block *ref_block,
+                block *next_block);
+
+/* LEGACY CODE: version 1.2.1 and earlier
+* Function fills a new memory block by overwriting @next_block. 
+* @param prev_block Pointer to the previous block
+* @param ref_block Pointer to the reference block
+* @param next_block Pointer to the block to be constructed
+* @pre all block pointers must be valid
+*/
+void fill_block(const block *prev_block, const block *ref_block,
+    block *next_block);
+
+/*
+ * Generate pseudo-random values to reference blocks in the segment and puts
+ * them into the array
+ * @param instance Pointer to the current instance
+ * @param position Pointer to the current position
+ * @param pseudo_rands Pointer to the array of 64-bit values
+ * @pre pseudo_rands must point to @a instance->segment_length allocated values
+ */
+void generate_addresses(const argon2_instance_t *instance,
+                        const argon2_position_t *position,
+                        uint64_t *pseudo_rands);
+
+#endif /* ARGON2_REF_H */
diff --git a/phc-winner-argon2/src/thread.c b/phc-winner-argon2/src/thread.c
--- a/phc-winner-argon2/src/thread.c
+++ b/phc-winner-argon2/src/thread.c
@@ -1,6 +1,6 @@
 #include "thread.h"
 #if defined(_WIN32)
-#include <Windows.h>
+#include <windows.h>
 #endif
 
 int argon2_thread_create(argon2_thread_handle_t *handle,
diff --git a/phc-winner-argon2/src/thread.h b/phc-winner-argon2/src/thread.h
new file mode 100644
--- /dev/null
+++ b/phc-winner-argon2/src/thread.h
@@ -0,0 +1,46 @@
+#ifndef ARGON2_THREAD_H
+#define ARGON2_THREAD_H
+/*
+        Here we implement an abstraction layer for the simpĺe requirements
+        of the Argon2 code. We only require 3 primitives---thread creation,
+        joining, and termination---so full emulation of the pthreads API
+        is unwarranted. Currently we wrap pthreads and Win32 threads.
+
+        The API defines 2 types: the function pointer type,
+   argon2_thread_func_t,
+        and the type of the thread handle---argon2_thread_handle_t.
+*/
+#if defined(_WIN32)
+#include <process.h>
+typedef unsigned(__stdcall *argon2_thread_func_t)(void *);
+typedef uintptr_t argon2_thread_handle_t;
+#else
+#include <pthread.h>
+typedef void *(*argon2_thread_func_t)(void *);
+typedef pthread_t argon2_thread_handle_t;
+#endif
+
+/* Creates a thread
+ * @param handle pointer to a thread handle, which is the output of this
+ * function. Must not be NULL.
+ * @param func A function pointer for the thread's entry point. Must not be
+ * NULL.
+ * @param args Pointer that is passed as an argument to @func. May be NULL.
+ * @return 0 if @handle and @func are valid pointers and a thread is successfuly
+ * created.
+ */
+int argon2_thread_create(argon2_thread_handle_t *handle,
+                         argon2_thread_func_t func, void *args);
+
+/* Waits for a thread to terminate
+ * @param handle Handle to a thread created with argon2_thread_create.
+ * @return 0 if @handle is a valid handle, and joining completed successfully.
+*/
+int argon2_thread_join(argon2_thread_handle_t handle);
+
+/* Terminate the current thread. Must be run inside a thread created by
+ * argon2_thread_create.
+*/
+void argon2_thread_exit(void);
+
+#endif
diff --git a/src/Crypto/Argon2.hs b/src/Crypto/Argon2.hs
--- a/src/Crypto/Argon2.hs
+++ b/src/Crypto/Argon2.hs
@@ -117,9 +117,9 @@
 -- will be throw.
 data Argon2Exception
   = -- | The length of the supplied password is outside the range supported by @libargon2@.
-    Argon2PasswordLengthOutOfRange !Word64 -- ^ The erroneous length.
+    Argon2PasswordLengthOutOfRange !CSize -- ^ The erroneous length.
   | -- | The length of the supplied salt is outside the range supported by @libargon2@.
-    Argon2SaltLengthOutOfRange !Word64 -- ^ The erroneous length.
+    Argon2SaltLengthOutOfRange !CSize -- ^ The erroneous length.
   | -- | Either too much or too little memory was requested via 'hashMemory'.
     Argon2MemoryUseOutOfRange !Word32 -- ^ The erroneous 'hashMemory' value.
   | -- | Either too few or too many iterations were requested via 'hashIterations'.
@@ -132,7 +132,7 @@
 
 instance Exception Argon2Exception
 
-type Argon2Encoded = Word32 -> Word32 -> Word32 -> CString -> Word64 -> CString -> Word64 -> Word64 -> CString -> Word64 -> IO Int32
+type Argon2Encoded = Word32 -> Word32 -> Word32 -> CString -> CSize -> CString -> CSize -> CSize -> CString -> CSize -> IO Int32
 
 hashEncoded' :: HashOptions
              -> BS.ByteString
@@ -143,10 +143,12 @@
 hashEncoded' options@HashOptions{..} password salt argon2i argon2d =
   do let saltLen = fromIntegral (BS.length salt)
          passwordLen = fromIntegral (BS.length password)
-         outLen =
-           (BS.length salt * 4 + 32 * 4 +
-            length ("$argon2x$m=,t=,p=$$" :: String) +
-            3 * 3)
+     outLen <- fmap fromIntegral $ FFI.argon2_encodedlen
+                                              hashIterations
+                                              hashMemory
+                                              hashParallelism
+                                              saltLen
+                                              hashlen
      out <- mallocBytes outLen
      res <-
        BS.useAsCString password $
@@ -159,15 +161,16 @@
                   password'
                   passwordLen
                   salt'
-                  saltLen
-                  64
+                  (fromIntegral saltLen)
+                  (fromIntegral hashlen)
                   out
                   (fromIntegral outLen)
      handleSuccessCode res options password salt
      fmap T.decodeUtf8 (BS.packCString out)
   where argon2 = variant argon2i argon2d hashVariant
+        hashlen = 64
 
-type Argon2Unencoded = Word32 -> Word32 -> Word32 -> CString -> Word64 -> CString -> Word64 -> CString -> Word64 -> IO Int32
+type Argon2Unencoded = Word32 -> Word32 -> Word32 -> CString -> CSize -> CString -> CSize -> CString -> CSize -> IO Int32
 
 hash' :: HashOptions
       -> BS.ByteString
@@ -195,7 +198,7 @@
                   out
                   (fromIntegral outLen)
      handleSuccessCode res options password salt
-     BS.packCString out
+     BS.packCStringLen (out, outLen)
   where argon2 = variant argon2i argon2d hashVariant
 
 handleSuccessCode :: Int32
diff --git a/src/Crypto/Argon2/FFI.hsc b/src/Crypto/Argon2/FFI.hsc
--- a/src/Crypto/Argon2/FFI.hsc
+++ b/src/Crypto/Argon2/FFI.hsc
@@ -9,17 +9,19 @@
 import Foreign
 import Foreign.C
 
-foreign import ccall unsafe "argon2.h argon2i_hash_encoded" argon2i_hash_encoded :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> (#type const size_t) -> Ptr b -> (# type const size_t) -> (#type const size_t) -> CString -> (#type const size_t) -> IO (#type int)
+foreign import ccall unsafe "argon2.h argon2i_hash_encoded" argon2i_hash_encoded :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> CSize -> Ptr b -> CSize -> CSize -> CString -> CSize -> IO (#type int)
 
-foreign import ccall unsafe "argon2.h argon2i_hash_raw" argon2i_hash_raw :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> (#type const size_t) -> Ptr b -> (#type size_t) -> Ptr c -> (#type const size_t) -> IO (#type int)
+foreign import ccall unsafe "argon2.h argon2i_hash_raw" argon2i_hash_raw :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> CSize -> Ptr b -> CSize -> Ptr c -> CSize -> IO (#type int)
 
-foreign import ccall unsafe "argon2.h argon2d_hash_encoded" argon2d_hash_encoded :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> (#type const size_t) -> Ptr b -> (# type const size_t) -> (#type const size_t) -> CString -> (#type const size_t) -> IO (#type int)
+foreign import ccall unsafe "argon2.h argon2d_hash_encoded" argon2d_hash_encoded :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> CSize -> Ptr b -> CSize -> CSize -> CString -> CSize -> IO (#type int)
 
-foreign import ccall unsafe "argon2.h argon2d_hash_raw" argon2d_hash_raw :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> (#type const size_t) -> Ptr b -> (#type size_t) -> Ptr c -> (#type const size_t) -> IO (#type int)
+foreign import ccall unsafe "argon2.h argon2d_hash_raw" argon2d_hash_raw :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> Ptr a -> CSize -> Ptr b -> CSize -> Ptr c -> CSize -> IO (#type int)
 
-foreign import ccall unsafe "argon2.h argon2i_verify" argon2i_verify :: CString -> Ptr a -> (#type const size_t) -> IO (#type int)
+foreign import ccall unsafe "argon2.h argon2i_verify" argon2i_verify :: CString -> Ptr a -> CSize -> IO (#type int)
 
-foreign import ccall unsafe "argon2.h argon2d_verify" argon2d_verify :: CString -> Ptr a -> (#type const size_t) -> IO (#type int)
+foreign import ccall unsafe "argon2.h argon2d_verify" argon2d_verify :: CString -> Ptr a -> CSize -> IO (#type int)
+
+foreign import ccall unsafe "argon2.h argon2_encodedlen" argon2_encodedlen :: (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> (#type const uint32_t) -> IO CSize
 
 pattern ARGON2_OK = (#const ARGON2_OK)
 pattern ARGON2_OUTPUT_PTR_NULL = (#const ARGON2_OUTPUT_PTR_NULL)
