From 769e02e4c7e66230f327f90841981c5744d6e3b9 Mon Sep 17 00:00:00 2001 From: Didactic Drunk <1479616+didactic-drunk@users.noreply.github.com> Date: Mon, 1 Jul 2019 10:37:45 -0700 Subject: [PATCH] Add Pwhash test vectors from PyNaCl and RbNaCl. Pwhash::Algorithm renamed to Mode to be more like other libsodium bindings. Cryptobox#encrypt now returns {encrypted, nonce} identical to SecretBox. --- README.md | 2 +- spec/data/box_from_seed.txt | 6 ++ spec/data/modular_crypt_argon2i_hashes.json | 92 +++++++++++++++++ spec/data/modular_crypt_argon2id_hashes.json | 102 +++++++++++++++++++ spec/data/raw_argon2i_hashes.json | 92 +++++++++++++++++ spec/data/raw_argon2id_hashes.json | 102 +++++++++++++++++++ spec/data/sealed_box_ref.txt | 6 ++ spec/sodium/crypto_box/secret_key_spec.cr | 2 +- spec/sodium/pwhash_spec.cr | 77 +++++++++++++- src/sodium/crypto_box.cr | 3 +- src/sodium/pwhash.cr | 39 +++---- src/sodium/secret_box.cr | 1 + 12 files changed, 500 insertions(+), 24 deletions(-) create mode 100644 spec/data/box_from_seed.txt create mode 100644 spec/data/modular_crypt_argon2i_hashes.json create mode 100644 spec/data/modular_crypt_argon2id_hashes.json create mode 100644 spec/data/raw_argon2i_hashes.json create mode 100644 spec/data/raw_argon2id_hashes.json create mode 100644 spec/data/sealed_box_ref.txt diff --git a/README.md b/README.md index e8cea8f..2a413ef 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Crystal bindings for the [libsodium API](https://libsodium.gitbook.io/doc/) - [x] Complete libsodium implementation including `key`, `salt`, `personal` and fully selectable output sizes. - [ ] [SipHash](https://libsodium.gitbook.io/doc/hashing/short-input_hashing) - [Password Hashing](https://libsodium.gitbook.io/doc/password_hashing) - - [x] [Argon2](https://libsodium.gitbook.io/doc/password_hashing/the_argon2i_function) (Use for new applications) + - [x] [x] [Argon2](https://libsodium.gitbook.io/doc/password_hashing/the_argon2i_function) (Use for new applications) - [ ] [Scrypt](https://libsodium.gitbook.io/doc/advanced/scrypt) (For compatibility with older applications) - Other - [x] [Key Derivation](https://libsodium.gitbook.io/doc/key_derivation) diff --git a/spec/data/box_from_seed.txt b/spec/data/box_from_seed.txt new file mode 100644 index 0000000..7547613 --- /dev/null +++ b/spec/data/box_from_seed.txt @@ -0,0 +1,6 @@ +# Reference data taken from libsodium's test/default/box_seed.c +# and test/default/box_seed.exp. +# The seed comes from box_seed.c, the public and secret keys +# come from box_seed.exp where they are directly concatenated. +# Fmt: || +77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a ed7749b4d989f6957f3bfde6c56767e988e21c9f8784d91d610011cd553f9b06accd44eb8e93319c0570bc11005c0e0189d34ff02f6c17773411ad191293c98f diff --git a/spec/data/modular_crypt_argon2i_hashes.json b/spec/data/modular_crypt_argon2i_hashes.json new file mode 100644 index 0000000..910dc2d --- /dev/null +++ b/spec/data/modular_crypt_argon2i_hashes.json @@ -0,0 +1,92 @@ +[ + { + "passwd": "gCb0SBnpxkdLZdWe", + "mode": "crypt", + "dgst_len": 20, + "iters": 4, + "salt": "kSmmBSy5uyC4", + "pwhash": "$argon2i$v=19$m=131072,t=4,p=1$a1NtbUJTeTV1eUM0$gNxxt8jHCHaKDUcbdP3ugX9i+BA", + "maxmem": 131072 + }, + { + "passwd": "mu3KbBv3", + "mode": "crypt", + "dgst_len": 17, + "iters": 4, + "salt": "kvPMDFk4yT9sZI", + "pwhash": "$argon2i$v=19$m=32768,t=4,p=1$a3ZQTURGazR5VDlzWkk$V+L2XhLr+F/EevADgyfo+bM", + "maxmem": 32768 + }, + { + "passwd": "Bxrsg5L0A0", + "mode": "crypt", + "dgst_len": 17, + "iters": 3, + "salt": "Z5c5VUWXSJcSbr", + "pwhash": "$argon2i$v=19$m=65536,t=3,p=1$WjVjNVZVV1hTSmNTYnI$BqyrFpvLJUCFaf9cQAFH1Ms", + "maxmem": 65536 + }, + { + "passwd": "EQmTLQ2ET", + "mode": "crypt", + "dgst_len": 30, + "iters": 3, + "salt": "8aWdz2IL", + "pwhash": "$argon2i$v=19$m=32768,t=3,p=1$OGFXZHoySUw$U6F0Tm/c/gyjJyMi4SWjLS+8dpuiKFK0OZje6iEZ", + "maxmem": 32768 + }, + { + "passwd": "9wQdHVSks3tbXx", + "mode": "crypt", + "dgst_len": 19, + "iters": 5, + "salt": "HW4AgInQoU", + "pwhash": "$argon2i$v=19$m=65536,t=5,p=1$SFc0QWdJblFvVQ$IwHIJ6VP1vIV7xrKm+mlI1nupA", + "maxmem": 65536 + }, + { + "passwd": "XRGHrq53wJ1LWgV", + "mode": "crypt", + "dgst_len": 27, + "iters": 5, + "salt": "Dr57DsF3", + "pwhash": "$argon2i$v=19$m=131072,t=5,p=1$RHI1N0RzRjM$shLAeDv4NbeHN9Sce+jaoEHIjUTP6OnGOtZ1", + "maxmem": 131072 + }, + { + "passwd": "JVoDvUkiJgtm", + "mode": "crypt", + "dgst_len": 17, + "iters": 5, + "salt": "C5jzAUY8D", + "pwhash": "$argon2i$v=19$m=131072,t=5,p=1$QzVqekFVWThE$SqZ2XYLggynRZgYlsWFiXGI", + "maxmem": 131072 + }, + { + "passwd": "eZf6zOck", + "mode": "crypt", + "dgst_len": 16, + "iters": 3, + "salt": "ID0CNHQR", + "pwhash": "$argon2i$v=19$m=65536,t=3,p=1$SUQwQ05IUVI$ShibIsTX2z5mclt4TKyAkQ", + "maxmem": 65536 + }, + { + "passwd": "nPeXb5WW", + "mode": "crypt", + "dgst_len": 26, + "iters": 5, + "salt": "DibJwvJWarySIIic", + "pwhash": "$argon2i$v=19$m=16384,t=5,p=1$RGliSnd2SldhcnlTSUlpYw$3n/DLofidmz8LPnM925hbm+4He/iKjAlvG4", + "maxmem": 16384 + }, + { + "passwd": "Vgv3CCRnqkgTO2co", + "mode": "crypt", + "dgst_len": 17, + "iters": 5, + "salt": "MfmTsCzom", + "pwhash": "$argon2i$v=19$m=131072,t=5,p=1$TWZtVHNDem9t$1W2xtNO3w3BylGxOHuX/IZ0", + "maxmem": 131072 + } +] diff --git a/spec/data/modular_crypt_argon2id_hashes.json b/spec/data/modular_crypt_argon2id_hashes.json new file mode 100644 index 0000000..5b001eb --- /dev/null +++ b/spec/data/modular_crypt_argon2id_hashes.json @@ -0,0 +1,102 @@ +[ + { + "iters": 1, + "maxmem": 131072, + "dgst_len": 30, + "passwd": "6GaBkxnU", + "salt": "SIWAuVklLU", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=131072,t=1,p=1$U0lXQXVWa2xMVQ$+EaGmstgtSas9elKIXLVqcybaYJIBWifLeLWkFAZ", + "mode": "crypt" + }, + { + "iters": 3, + "maxmem": 65536, + "dgst_len": 31, + "passwd": "JU1K5W8rzP1K", + "salt": "9Jve1X5OQ2tp", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=65536,t=3,p=1$OUp2ZTFYNU9RMnRw$7yApdAtgdaPQZWisUa58lb328fR+kgznHqQqcafaCg", + "mode": "crypt" + }, + { + "iters": 2, + "maxmem": 131072, + "dgst_len": 20, + "passwd": "zBqdT3A3WReQFO", + "salt": "ahTuAAVQ6", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=131072,t=2,p=1$YWhUdUFBVlE2$jLdOXTu7R5XVuLKyHoEKVojIYsM", + "mode": "crypt" + }, + { + "iters": 5, + "maxmem": 262144, + "dgst_len": 30, + "passwd": "FF3gKe0hL", + "salt": "XYpBw246J", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=262144,t=5,p=1$WFlwQncyNDZK$8D7TbKnOi9PMuhooK9HUJLTKdPBXD9CZ5/dE4p0r", + "mode": "crypt" + }, + { + "iters": 4, + "maxmem": 131072, + "dgst_len": 20, + "passwd": "tUhcdnSMH", + "salt": "qGQAKPomg08", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=131072,t=4,p=1$cUdRQUtQb21nMDg$7kj3MDpz4aOZazen9BrazcIiMQE", + "mode": "crypt" + }, + { + "iters": 5, + "maxmem": 131072, + "dgst_len": 19, + "passwd": "oxx700QV8RVHQA2l", + "salt": "r8R7NqsNyb", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=131072,t=5,p=1$cjhSN05xc055Yg$yHz0aPvCukbkMw1oXaKejEkG6Q", + "mode": "crypt" + }, + { + "iters": 4, + "maxmem": 262144, + "dgst_len": 27, + "passwd": "zTKzEfuKI4", + "salt": "ajbUgkhOX9bxaD", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=262144,t=4,p=1$YWpiVWdraE9YOWJ4YUQ$h0NTYkqWF6qEpVnLP4Bqsk7oYjG4DNyn6+q0", + "mode": "crypt" + }, + { + "iters": 2, + "maxmem": 65536, + "dgst_len": 21, + "passwd": "DicnLJsOvUTGBSAx", + "salt": "djp2efSTgmrz", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=65536,t=2,p=1$ZGpwMmVmU1RnbXJ6$Ar5DxZsxCAjUrGrBZPsa0vOVhjvk", + "mode": "crypt" + }, + { + "iters": 1, + "maxmem": 32768, + "dgst_len": 30, + "passwd": "HyURt4Ofuwivg8", + "salt": "9tArBEgW8GU", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=32768,t=1,p=1$OXRBckJFZ1c4R1U$l/6y/kKeMPSk3WQb+bM4ir3bGj5yxj+/r6Wi/D8A", + "mode": "crypt" + }, + { + "iters": 2, + "maxmem": 65536, + "dgst_len": 31, + "passwd": "ikt3keViPK0S", + "salt": "4Wh4LpHrUy", + "construct": "argon2id", + "pwhash": "$argon2id$v=19$m=65536,t=2,p=1$NFdoNExwSHJVeQ$MrbDWj1XHpotj2+n6vs5KF+2E0oUGt9Ge/mo0IJ0tA", + "mode": "crypt" + } +] \ No newline at end of file diff --git a/spec/data/raw_argon2i_hashes.json b/spec/data/raw_argon2i_hashes.json new file mode 100644 index 0000000..6a028f5 --- /dev/null +++ b/spec/data/raw_argon2i_hashes.json @@ -0,0 +1,92 @@ +[ + { + "passwd": "5qflFfSWpOvQ", + "mode": "raw", + "dgst_len": 20, + "iters": 5, + "salt": "QIA9x2qmhRVmdRCU", + "pwhash": "3eb202f3e4b0e2c078031f724103ea526fe2a065", + "maxmem": 16384 + }, + { + "passwd": "pf1SMoxVb54r", + "mode": "raw", + "dgst_len": 26, + "iters": 4, + "salt": "NPBA55CsQp9wsWKy", + "pwhash": "b7173ea20f6eb6946d3044542fc75ef5dda0d9c94acf86d834ca", + "maxmem": 131072 + }, + { + "passwd": "bg9Laxjhrk8MUyhM", + "mode": "raw", + "dgst_len": 18, + "iters": 4, + "salt": "exKErRXWGdCDRSop", + "pwhash": "9c7957beb49f1c5e868d6a04f34d1da821f6", + "maxmem": 65536 + }, + { + "passwd": "n95X5vWQncMXlP", + "mode": "raw", + "dgst_len": 17, + "iters": 3, + "salt": "gxSD9VHiGF0oou1k", + "pwhash": "b605e0557e9f57455f246e474e93648595", + "maxmem": 32768 + }, + { + "passwd": "5G8iiXVYwI", + "mode": "raw", + "dgst_len": 29, + "iters": 4, + "salt": "GcjAUSZ4pLAZsDHL", + "pwhash": "5fa4d3b86c8ca981dbc3ea4cbbd015030bda8bd444d4039309e88b23c7", + "maxmem": 32768 + }, + { + "passwd": "cUu3ACKqAHnI54Eh", + "mode": "raw", + "dgst_len": 23, + "iters": 3, + "salt": "6H99IAjhJQ2WFgom", + "pwhash": "6202e1b58e65a658d1adcc0584fafd6373e565d6fd8348", + "maxmem": 32768 + }, + { + "passwd": "jBB3PBZ8v1H1aga", + "mode": "raw", + "dgst_len": 20, + "iters": 4, + "salt": "L3vcnvwJbLJTdvKx", + "pwhash": "f3d792b3edbf49441c269c22c2641e724cb21ef0", + "maxmem": 16384 + }, + { + "passwd": "WGA58n0DVW9FT", + "mode": "raw", + "dgst_len": 18, + "iters": 3, + "salt": "qN6Q3h3lzJtXcMkH", + "pwhash": "ec9dd7f5503cc5b74ab1fc8ac6cad426327f", + "maxmem": 65536 + }, + { + "passwd": "rxB2Op3fo17s6", + "mode": "raw", + "dgst_len": 27, + "iters": 5, + "salt": "xw9IrU6uf4E4hBo3", + "pwhash": "ffe9c857e686ab40f8758415acd6d3409acfe80d54bac24bcacfa3", + "maxmem": 65536 + }, + { + "passwd": "JyjdXkjHxGiGjg", + "mode": "raw", + "dgst_len": 30, + "iters": 5, + "salt": "rN0D8NoVbNBnlEex", + "pwhash": "fab6d8b5e1849c99f5261c15ff502b1bb245f8076efa5e01cbbdc1903512", + "maxmem": 32768 + } +] diff --git a/spec/data/raw_argon2id_hashes.json b/spec/data/raw_argon2id_hashes.json new file mode 100644 index 0000000..882ea31 --- /dev/null +++ b/spec/data/raw_argon2id_hashes.json @@ -0,0 +1,102 @@ +[ + { + "iters": 1, + "mode": "raw", + "salt": "RPgjpKWKmH7otL58", + "dgst_len": 25, + "construct": "argon2id", + "passwd": "jfuT8oJu2Fg", + "pwhash": "959f27c6ac7a7f9d4cebb7bb1cc05833810079e4e384f687a7", + "maxmem": 131072 + }, + { + "iters": 4, + "mode": "raw", + "salt": "hsvjdI3ckgfMke6x", + "dgst_len": 19, + "construct": "argon2id", + "passwd": "J2TU89T68L", + "pwhash": "f45619dcd98553c38a4f311904a06e888dd5dd", + "maxmem": 32768 + }, + { + "iters": 3, + "mode": "raw", + "salt": "oCSdi7b6I3UYftj6", + "dgst_len": 23, + "construct": "argon2id", + "passwd": "ZTgoKeVg", + "pwhash": "823cd743a9378b0013a61b275d8a8d61b4abf465e3c758", + "maxmem": 65536 + }, + { + "iters": 3, + "mode": "raw", + "salt": "BcZv9BmKSL1BZXBD", + "dgst_len": 25, + "construct": "argon2id", + "passwd": "3bDtmljuB86", + "pwhash": "a667a360356b3658656cc51feda5abc3c9932d1e3b4a398633", + "maxmem": 16384 + }, + { + "iters": 5, + "mode": "raw", + "salt": "hqiuuNWIOLHft77r", + "dgst_len": 20, + "construct": "argon2id", + "passwd": "410nLopRL2Pt6yT", + "pwhash": "7274d6a9be2b52feab9b346df1abee1a68ec5c79", + "maxmem": 65536 + }, + { + "iters": 1, + "mode": "raw", + "salt": "ZP3u5yVZTAl0F33z", + "dgst_len": 31, + "construct": "argon2id", + "passwd": "0pXnCEpVlR", + "pwhash": "d14d136cae8ca735e0fe03d2c618f904cff07a525579d0dc67d813b223ac7a", + "maxmem": 32768 + }, + { + "iters": 3, + "mode": "raw", + "salt": "QWWo5rUeD3nBv0dF", + "dgst_len": 28, + "construct": "argon2id", + "passwd": "vl50xKRCK0EHNRY", + "pwhash": "23560c5771bb56d02c8129effd4ff2c70d2f28e1912636ff1a4f11a6", + "maxmem": 262144 + }, + { + "iters": 5, + "mode": "raw", + "salt": "OADldcmnqJHY6tkc", + "dgst_len": 24, + "construct": "argon2id", + "passwd": "EuXIuKCdFq", + "pwhash": "bae0f9ff8143a141bfb8b8629414eba2c51f122b2524e73d", + "maxmem": 65536 + }, + { + "iters": 2, + "mode": "raw", + "salt": "cxYQAFWsKbji4Y0e", + "dgst_len": 30, + "construct": "argon2id", + "passwd": "2su5ROWRMZLOOWbv", + "pwhash": "6341329de527593dbd479e24ca02b7f6bfcc29a6c74def655db74808e199", + "maxmem": 32768 + }, + { + "iters": 1, + "mode": "raw", + "salt": "Zk6vLoBZZA8lgzKL", + "dgst_len": 19, + "construct": "argon2id", + "passwd": "7mPYiXjrwLw", + "pwhash": "84dc96e740c84cc52855c3f7c1b5b05070d14a", + "maxmem": 65536 + } +] diff --git a/spec/data/sealed_box_ref.txt b/spec/data/sealed_box_ref.txt new file mode 100644 index 0000000..2ad538e --- /dev/null +++ b/spec/data/sealed_box_ref.txt @@ -0,0 +1,6 @@ +# generated reference vectors for crypto_seal_box +# Format: : <tab> <cr_len>:<ciphertext> [<tab> ...] +# all fields are hex encoded +77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a 8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a 131:be075fc53c81f2d5cf141316ebeb0c7b5228c52a4c62cbd44b66849b64244ffce5ecbaaf33bd751a1ac728d45e6c61296cdc3c01233561f41db66cce314adb310e3be8250c46f06dceea3a7fa1348057e2f6556ad6b1318a024a838f21af1fde048977eb48f59ffd4924ca1c60902e52f0a089bc76897040e082f937763848645e0705 179:199c808806a62c1be56951023ad3396b0ce0ef2c5b9ca688ac96d2d06ca43f65d314400cc9bbefb23dd26f824c9cb553f81e8c894ea9a553f4d777c77b66d5a6925da3f5961c5f7147172db5597ac14210066ee3ee13e6230a9a9610e9cddbf24094f54fbbf6694c08a436cec39ff5a373656d9584f4df9dd8c817e2e597540628d0ee21a6524a3fcf3eabdc0968390bd260a47366ead7f71cc2e774d411d96f3497c7e10291937bc16dc46a111686b85a8c86 +7fdc652e0f714cc6f6d5f7bf1aab9da7fcaa5b27f1f7763f31a659cc6ddd60d5 695ec87c37afbb70771a1a048e08d7c70a25b7d710e23a5814248128f3db035d 0: 48:017f873a1536b35eac54f729e0f05ea5c5a96f9c221327d70bf46497b3b33007a84d0b93667ea189ad4dfcc016f727d1 +1bcdb90ac2996d3e5598a404fd8753bae482ec821a64e306e15416228bae7212 6415b1fe7bb4dc602097f607249df89eb59cf2e75be1cb74abfc0664dc0f0a3d 1:eb 49:dbe788125ef86d2d4049feb371cf25e2a52a0d71505eeb2ebff526e859e6495c3c278fa7ba88e41c92c711a0ddbd3bdf09 diff --git a/spec/sodium/crypto_box/secret_key_spec.cr b/spec/sodium/crypto_box/secret_key_spec.cr index cdb7348..3fada64 100644 --- a/spec/sodium/crypto_box/secret_key_spec.cr +++ b/spec/sodium/crypto_box/secret_key_spec.cr @@ -40,7 +40,7 @@ describe Sodium::CryptoBox::SecretKey do # Encrypt a message for Bob using his public key, signing it with Alice's # secret key box = alice.box bob.public_key - nonce, encrypted = box.encrypt_easy data + encrypted, nonce = box.encrypt_easy data # Decrypt the message using Bob's secret key, and verify its signature against # Alice's public key diff --git a/spec/sodium/pwhash_spec.cr b/spec/sodium/pwhash_spec.cr index 8c7079f..2df53dc 100644 --- a/spec/sodium/pwhash_spec.cr +++ b/spec/sodium/pwhash_spec.cr @@ -1,5 +1,47 @@ require "../spec_helper" require "../../src/sodium/pwhash" +require "json" + +def test_vectors(filename, pwmode) + pwhash = Sodium::Pwhash.new + + buf = File.read Path[__DIR__].join("..", "data", filename) + vectors = Array(Hash(String, String | Int32)).from_json(buf).map do |h| + { + salt: h["salt"].to_s, + pass: h["passwd"].to_s, + mode: h["mode"].to_s, + ops: h["iters"].to_i, + mem: h["maxmem"].to_i * 1024, + dgst_len: h["dgst_len"].to_i, + hash: h["pwhash"].to_s, + # h: h, + } + end + + vectors.each do |h| + case h[:mode] + when "argon2i" + pwhash.mode = Sodium::Pwhash::Mode::Argon2i13 + pwhash.verify h[:hash], h[:pass] + when "argon2id" + pwhash.mode = Sodium::Pwhash::Mode::Argon2id13 + pwhash.verify h[:hash], h[:pass] + when "raw" + pwhash.opslimit = h[:ops].to_u64 + pwhash.memlimit = h[:mem].to_u64 + pwhash.mode = pwmode + # p pwhash, h + key = pwhash.key_derive salt: h[:salt].to_slice, pass: h[:pass], key_bytes: h[:dgst_len] + key.should eq h[:hash].hexbytes + else + # p h + puts "unhandled mode #{h[:mode]}" + next + # raise "unhandled mode #{h[:mode]}" + end + end +end private def pw_min pwhash = Sodium::Pwhash.new @@ -26,7 +68,7 @@ describe Sodium::Pwhash do pwhash.needs_rehash?(hash).should be_true end - it "key_derive fails without an algorithm" do + it "key_derive fails without a mode" do pwhash = pw_min expect_raises(ArgumentError) do pwhash.key_derive pwhash.salt, "foo", 16 @@ -35,7 +77,7 @@ describe Sodium::Pwhash do it "derives a key from a password" do pwhash = pw_min - pwhash.algorithm = Sodium::Pwhash::Algorithm::Argon2id13 + pwhash.mode = Sodium::Pwhash::Mode::Argon2id13 salt = pwhash.salt key1 = pwhash.key_derive salt, "foo", 16 key2 = pwhash.key_derive salt, "foo", 16 @@ -46,6 +88,35 @@ describe Sodium::Pwhash do key1.should eq key2 key1.should_not eq key3 key1.should_not eq key4 - # BUG: validate against known passwords + end + + it "PyNaCl key vectors" do + test_vectors "modular_crypt_argon2i_hashes.json", Sodium::Pwhash::Mode::Argon2i13 + test_vectors "modular_crypt_argon2id_hashes.json", Sodium::Pwhash::Mode::Argon2id13 + test_vectors "raw_argon2i_hashes.json", Sodium::Pwhash::Mode::Argon2i13 + test_vectors "raw_argon2id_hashes.json", Sodium::Pwhash::Mode::Argon2id13 + end + + # from libsodium/test/default/pwhash_argon2id.c + it "RbNaCl key vectors" do + pwhash = Sodium::Pwhash.new + pwhash.mode = Sodium::Pwhash::Mode::Argon2id13 + pwhash.opslimit = 5_u64 + pwhash.memlimit = 7_256_678_u64 + key_len = 155 + + pass = "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0" \ + "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d" \ + "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5" \ + "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6".hexbytes + salt = "5541fbc995d5c197ba290346d2c559de".hexbytes + expected = "18acec5d6507739f203d1f5d9f1d862f7c2cdac4f19d2bdff64487e60d969e3ced6" \ + "15337b9eec6ac4461c6ca07f0939741e57c24d0005c7ea171a0ee1e7348249d135b" \ + "38f222e4dad7b9a033ed83f5ca27277393e316582033c74affe2566a2bea47f91f0" \ + "fd9fe49ece7e1f79f3ad6e9b23e0277c8ecc4b313225748dd2a80f5679534a0700e" \ + "246a79a49b3f74eb89ec6205fe1eeb941c73b1fcf1".hexbytes + + key = pwhash.key_derive salt, pass, key_len + key.should eq expected end end diff --git a/src/sodium/crypto_box.cr b/src/sodium/crypto_box.cr index 167a789..1d49607 100644 --- a/src/sodium/crypto_box.cr +++ b/src/sodium/crypto_box.cr @@ -1,6 +1,7 @@ require "./lib_sodium" require "./wipe" require "./crypto_box/secret_key" +require "./nonce" module Sodium class CryptoBox @@ -24,7 +25,7 @@ module Sodium if LibSodium.crypto_box_easy(dst, src, src.bytesize, nonce.to_slice, @public_key.to_slice, @secret_key.to_slice) != 0 raise Error.new("crypto_box_easy") end - {nonce, dst} + {dst, nonce} end def decrypt_easy(src) diff --git a/src/sodium/pwhash.cr b/src/sodium/pwhash.cr index 7b61df0..01ac764 100644 --- a/src/sodium/pwhash.cr +++ b/src/sodium/pwhash.cr @@ -20,10 +20,11 @@ module Sodium MEMLIMIT_INTERACTIVE = LibSodium.crypto_pwhash_memlimit_interactive MEMLIMIT_MAX = LibSodium.crypto_pwhash_memlimit_max # Don't use this. Maximum of the library which is more ram than any computer. - STR_SIZE = LibSodium.crypto_pwhash_strbytes + SALT_SIZE = LibSodium.crypto_pwhash_saltbytes + STR_SIZE = LibSodium.crypto_pwhash_strbytes # Use the most recent algorithm Argon2id13 for new applications. - enum Algorithm + enum Mode Argon2i13 = 1 Argon2id13 = 2 end @@ -33,7 +34,7 @@ module Sodium property memlimit = MEMLIMIT_INTERACTIVE # Used by and must be set before calling #key_derive - property algorithm : Algorithm? + property mode : Mode? # Apply the most recent password hashing algorithm agains a password. # Returns a opaque String which includes: @@ -73,28 +74,30 @@ module Sodium end end - # Returns a consistent key based on [salt, pass, key_bytes, algorithm, ops_limit, mem_limit] + # Returns a consistent key based on [salt, pass, key_bytes, mode, ops_limit, mem_limit] # - # Must set an algorithm before calling. - def key_derive(salt : Bytes, pass : Bytes, key_bytes) : Bytes - if alg = algorithm - key = Bytes.new key_bytes - if LibSodium.crypto_pwhash(key, key.bytesize, pass, pass.bytesize, salt, @opslimit, @memlimit, alg) != 0 - raise Sodium::Error.new("crypto_pwhash_str") - end - key - else - raise ArgumentError.new("algorithm not set") - end - end - + # Must set a mode before calling. def key_derive(salt, pass, key_bytes) key_derive salt.to_slice, pass.to_slice, key_bytes end + def key_derive(salt : Bytes, pass : Bytes, key_bytes) : Bytes + raise "salt expected #{SALT_SIZE} bytes, got #{salt.bytesize} " if salt.bytesize != SALT_SIZE + + if m = mode + key = Bytes.new key_bytes + if LibSodium.crypto_pwhash(key, key.bytesize, pass, pass.bytesize, salt, @opslimit, @memlimit, m) != 0 + raise Sodium::Error.new("crypto_pwhash_str") + end + key + else + raise ArgumentError.new("mode not set") + end + end + # Returns a random salt for use with #key_derive def salt - Random::Secure.random_bytes LibSodium.crypto_pwhash_saltbytes + Random::Secure.random_bytes SALT_SIZE end end end diff --git a/src/sodium/secret_box.cr b/src/sodium/secret_box.cr index 2f59b4b..27187a8 100644 --- a/src/sodium/secret_box.cr +++ b/src/sodium/secret_box.cr @@ -1,5 +1,6 @@ require "./lib_sodium" require "./key" +require "./nonce" module Sodium # [https://libsodium.gitbook.io/doc/secret-key_cryptography](https://libsodium.gitbook.io/doc/secret-key_cryptography)