summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2017-08-16 11:41:25 -0700
committerSean Whitton <spwhitton@spwhitton.name>2017-08-16 11:41:25 -0700
commit379f036b39e0f7dac360ba04b281f6ea4ce8f20e (patch)
tree01a86517d6234562088561c3c4b51365ed722807
parent680449e656820db2b899a8631060cf62359b9b74 (diff)
parent0229f026b1ae0344c4c052593564800035268d81 (diff)
downloadkeysafe-379f036b39e0f7dac360ba04b281f6ea4ce8f20e.tar.gz
Merge tag '0.20170811'
tagging package keysafe version 0.20170811 # gpg: Signature made Fri 11 Aug 2017 03:58:54 PM MST # gpg: using RSA key 28A500C35207EAB72F6C0F25DB12DB0FF05F8F38 # gpg: Good signature from "Joey Hess <joeyh@joeyh.name>" [full] # Primary key fingerprint: E85A 5F63 B31D 24C1 EBF0 D81C C910 D922 2512 E3C7 # Subkey fingerprint: 28A5 00C3 5207 EAB7 2F6C 0F25 DB12 DB0F F05F 8F38
-rw-r--r--CHANGELOG6
-rw-r--r--CmdLine.hs1
-rw-r--r--Encryption.hs6
-rw-r--r--HTTP/Client.hs25
-rw-r--r--HTTP/ProofOfWork.hs4
-rw-r--r--HTTP/Server.hs1
-rw-r--r--Makefile2
-rw-r--r--Storage.hs3
-rw-r--r--TODO100
-rw-r--r--doc/comments.mdwn7
-rw-r--r--doc/index.mdwn3
-rw-r--r--doc/news/version_0.20170303.mdwn3
-rw-r--r--doc/serverless.mdwn129
-rw-r--r--doc/servers.mdwn3
-rw-r--r--doc/templates/todolist.tmpl25
-rw-r--r--doc/todo.mdwn8
-rw-r--r--doc/todo/Fix_compiler_errors_in_HEAD.mdwn78
-rw-r--r--doc/todo/Make_the_number_of_shard_servers_configurable.mdwn3
-rw-r--r--doc/todo/Make_the_number_of_shard_servers_configurable/comment_1_4416f7495e2a34a3cdb6f5106beaf582._comment20
-rw-r--r--doc/todo/Move_the_project_to_notabug.org.mdwn9
-rw-r--r--doc/todo/Move_the_project_to_notabug.org/comment_1_a1e7094aa456b5ff6a6fb5adcc34fcab._comment13
-rw-r--r--doc/todo/add_--key-value_option.mdwn5
-rw-r--r--doc/todo/back_up_only_chosen_subkeys.mdwn2
-rw-r--r--doc/todo/custom_gui.mdwn10
-rw-r--r--doc/todo/delay_some_uploads_to_prevent_correlation.mdwn19
-rw-r--r--doc/todo/detect_bad_password_on_restore_and_re-prompt.mdwn2
-rw-r--r--doc/todo/detect_number_of_required_shares_on_restore.mdwn34
-rw-r--r--doc/todo/done.mdwn4
-rw-r--r--doc/todo/future_encryption_tunables_changes.mdwn18
-rw-r--r--doc/todo/important.mdwn4
-rw-r--r--doc/todo/improve_restore_progress_bar.mdwn1
-rw-r--r--doc/todo/need_one_more_recommended_server.mdwn8
-rw-r--r--doc/todo/padding_to_prevent_traffic_analysis.mdwn3
-rw-r--r--doc/todo/remove_gpg_key_passphrase_from_backed_up_gpg_keys.mdwn17
-rw-r--r--doc/todo/remove_jargon.mdwn4
-rw-r--r--doc/todo/set_up_--check-servers_in_cron_job.mdwn3
-rw-r--r--doc/todo/usability_testing.mdwn3
-rw-r--r--doc/todo/use_cryptohash_argon2.mdwn2
-rw-r--r--doc/todo/use_locked_memory.mdwn5
-rw-r--r--keysafe.cabal19
-rw-r--r--stack.yaml17
41 files changed, 477 insertions, 152 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 60167a0..af81330 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+keysafe (0.20170811) unstable; urgency=medium
+
+ * Updated to http-client 0.5.3, servant 0.11, and stackage lts-9.0.
+
+ -- Joey Hess <id@joeyh.name> Fri, 11 Aug 2017 18:57:15 -0400
+
keysafe (0.20170303) unstable; urgency=medium
* Updated to use raaz-0.1.1.
diff --git a/CmdLine.hs b/CmdLine.hs
index 3ba4e23..0f89fe4 100644
--- a/CmdLine.hs
+++ b/CmdLine.hs
@@ -14,6 +14,7 @@ import Storage.Network
import Tunables
import qualified Gpg
import Options.Applicative
+import Data.Monoid
import qualified Data.ByteString.UTF8 as BU8
import qualified Data.Text as T
import System.Directory
diff --git a/Encryption.hs b/Encryption.hs
index 3e085a0..3748edf 100644
--- a/Encryption.hs
+++ b/Encryption.hs
@@ -20,7 +20,6 @@ import qualified Raaz.Cipher.Internal as Raaz
import qualified Data.Text.Encoding as E
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
-import qualified Data.ByteString.UTF8 as BU8
import Text.Read
type AesKey = Raaz.KEY256
@@ -202,7 +201,10 @@ encodeEncryptableBytes tunables content = EncryptableBytes $
-- | Encoded, so that it does not contain any NULs.
sha :: B.ByteString -> B.ByteString
-sha = BU8.fromString . Raaz.showBase16 . Raaz.sha256
+sha = Raaz.toByteString . enc . Raaz.sha256
+ where
+ enc :: Raaz.Encodable a => a -> Raaz.Base16
+ enc = Raaz.encode
padBytes :: Int -> B.ByteString -> B.ByteString
padBytes n b = b <> padding
diff --git a/HTTP/Client.hs b/HTTP/Client.hs
index 25ff536..9626eab 100644
--- a/HTTP/Client.hs
+++ b/HTTP/Client.hs
@@ -15,11 +15,8 @@ import Servant.API
import Servant.Client
import Data.Proxy
import Network.HTTP.Client hiding (port, host, Proxy)
-import Network.HTTP.Client.Internal (Connection, makeConnection)
-import Control.Monad.Trans.Except (ExceptT, runExceptT)
+import Network.HTTP.Client.Internal (Connection)
import Control.Exception
-import qualified Network.Socket
-import Network.Socket.ByteString (sendAll, recv)
import Network.Socks5
import qualified Data.ByteString.UTF8 as BU8
import Data.List
@@ -28,10 +25,10 @@ import Data.Char
httpAPI :: Proxy HttpAPI
httpAPI = Proxy
-motd :: Manager -> BaseUrl -> ClientM Motd
-getObject :: StorableObjectIdent -> Maybe ProofOfWork -> Manager -> BaseUrl -> ClientM (POWGuarded StorableObject)
-putObject :: StorableObjectIdent -> Maybe ProofOfWork -> StorableObject -> Manager -> BaseUrl -> ClientM (POWGuarded StoreResult)
-countObjects :: Maybe ProofOfWork -> Manager -> BaseUrl -> ClientM (POWGuarded CountResult)
+motd :: ClientM Motd
+getObject :: StorableObjectIdent -> Maybe ProofOfWork -> ClientM (POWGuarded StorableObject)
+putObject :: StorableObjectIdent -> Maybe ProofOfWork -> StorableObject -> ClientM (POWGuarded StoreResult)
+countObjects :: Maybe ProofOfWork -> ClientM (POWGuarded CountResult)
motd :<|> getObject :<|> putObject :<|> countObjects = client httpAPI
tryA :: IO a -> IO (Either SomeException a)
@@ -43,7 +40,7 @@ serverRequest
-> (String -> a)
-> (r -> a)
-> p
- -> (Maybe ProofOfWork -> Manager -> BaseUrl -> ExceptT ServantError IO (POWGuarded r))
+ -> (Maybe ProofOfWork -> ClientM (POWGuarded r))
-> IO a
serverRequest srv onerr onsuccess p a = do
r <- tryA $ go Nothing maxProofOfWork
@@ -69,7 +66,7 @@ serverRequest srv onerr onsuccess p a = do
-- comparing IP addresses (which are masked somewhat by using tor).
serverRequest'
:: Server
- -> (Manager -> BaseUrl -> ExceptT ServantError IO r)
+ -> (ClientM r)
-> IO (Either String r)
serverRequest' srv a = go Nothing (serverUrls srv)
where
@@ -77,7 +74,7 @@ serverRequest' srv a = go Nothing (serverUrls srv)
maybe "no known address" (\err -> "server failure: " ++ show err) lasterr
go _ (url:urls) = do
manager <- torableManager
- res <- runExceptT $ a manager url
+ res <- runClientM a (ClientEnv manager url)
case res of
Left err -> go (Just err) urls
Right r -> return (Right r)
@@ -104,12 +101,6 @@ torConnection onionaddress p = do
socksdomain = SocksAddrDomainName (BU8.fromString onionaddress)
socksaddr = SocksAddress socksdomain (fromIntegral p)
-socketConnection :: Network.Socket.Socket -> Int -> IO Connection
-socketConnection socket chunksize = makeConnection
- (recv socket chunksize)
- (sendAll socket)
- (Network.Socket.close socket)
-
serverUrls :: Server -> [BaseUrl]
serverUrls srv = map go (serverAddress srv)
where
diff --git a/HTTP/ProofOfWork.hs b/HTTP/ProofOfWork.hs
index 61fea20..54e2223 100644
--- a/HTTP/ProofOfWork.hs
+++ b/HTTP/ProofOfWork.hs
@@ -16,7 +16,7 @@ import ByteStrings
import GHC.Generics
import qualified Data.Text as T
import qualified Data.ByteString as B
-import Data.Text.Encoding (encodeUtf8)
+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Raaz.Core.Encode
import qualified Raaz
import Data.BloomFilter.Hash
@@ -109,7 +109,7 @@ mkRequestID secret = mkRequeestID' secret <$> mkRandomSalt
mkRequeestID' :: RequestIDSecret -> RandomSalt -> RequestID
mkRequeestID' (RequestIDSecret key) salt =
let hmac = Raaz.hmacSha256 key (encodeUtf8 $ fromRandomSalt salt)
- in RequestID salt (T.pack (showBase16 hmac))
+ in RequestID salt $ decodeUtf8 $ Raaz.toByteString (Raaz.encode hmac :: Base16)
validRequestID :: RequestIDSecret -> RequestID -> Bool
validRequestID secret rid =
diff --git a/HTTP/Server.hs b/HTTP/Server.hs
index 61bdbfd..1abcc56 100644
--- a/HTTP/Server.hs
+++ b/HTTP/Server.hs
@@ -18,6 +18,7 @@ import CmdLine (ServerConfig(..))
import Storage.Local
import Serialization ()
import Servant
+import Network.Wai (Application)
import Network.Wai.Handler.Warp
import Control.Monad.IO.Class
import Control.Concurrent
diff --git a/Makefile b/Makefile
index 58def76..8ac9a1f 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,7 @@ keysafe:
$(BUILDER) build $(BUILDEROPTIONS); \
fi
if [ "$(BUILDER)" = stack ]; then \
- ln -sf $$(find .stack-work/ -name keysafe -type f | grep build/keysafe/keysafe | tail -n 1) keysafe; \
+ ln -sf $$(stack path --dist-dir)/build/keysafe/keysafe keysafe; \
else \
ln -sf dist/build/keysafe/keysafe keysafe; \
fi
diff --git a/Storage.hs b/Storage.hs
index feb5791..88e6ae5 100644
--- a/Storage.hs
+++ b/Storage.hs
@@ -73,9 +73,6 @@ problemStoringIn (StorageLocations locs) tunables
--
-- If a server is not currently accessible, it will be queued locally.
-- If any uploads are queued, returns True.
---
--- TODO: Add shuffling and queueing/chaffing to prevent
--- correlation of related shares.
storeShares :: StorageLocations -> ShareIdents -> [S.Set Share] -> UpdateProgress -> IO (StoreResult, Bool, [Storage])
storeShares (StorageLocations locs) allsis shares updateprogress = do
((r, anyqueued), usedlocs) <- go allsis shares [] False
diff --git a/TODO b/TODO
deleted file mode 100644
index 7b56c90..0000000
--- a/TODO
+++ /dev/null
@@ -1,100 +0,0 @@
-Soon:
-
-* Finish vetting 2 servers to Recommended.
-* Set up --check-servers in a cron job, so I know when servers are down.
-* Remove gpg key passohrase from gpg keys that keysafe backs up.
- The reason for this is that the user may well forget their gpg key
- passphrase, and it's *weird* to restore a key with keysafe's password
- and then have it passphrase protected.
- The gpg key passphrase is intended only to keep a key from being used
- for a short period of time (a week or so) when the device holding it
- is known to have been compromised, so the key can be revoked.
- This doesn't really apply to keys backed up with keysafe -- if they get
- compromised somehow, the user won't know, and cracking the gpg passphrase
- should be almost trivial to an attacker who was able to break keysafe's
- password.
- paperkey can remove gpg key passphrases. Is there any better way?
- It might make sense for keysafe to prompt for a new gpg passphrase
- when restoring.
-
-Later:
-
-* The attack cost display can lead to a false sense of security if the user
- takes it as gospel. It needs to be clear that it's an estimate. This and
- other parts of the keysafe UI need usability testing.
-* Make --gui password entry fields longer, so user does not feel they need
- to make password short. (zenity may not allow configuring this)
-* improve restore progress bar points (update after every hash try)
-* If we retrieved enough shares successfully, but decrypt failed, must
- be a wrong password, so prompt for re-entry and retry with those shares.
-* --no-jargon which makes the UI avoid terms like "secret key" and "crack
- password". Do usability testing!
-* --key-value=$N which eliminates the question about password value,
- and rejects passwords that would cost less than $N to crack at current
- rates. This should add a combo box to the password entry form in the
- GUI to let the user adjust the $N there.
-* In backup, only upload to N-1 servers immediately, and delay the rest
- for up to several days, with some uploads of chaff, to prevent
- collaborating evil servers from correlating related shards.
-* Add some random padding to http requests and responses, to make it
- harder for traffic analysis to tell that given TOR traffic is
- keysafe traffic.
-
-Wishlist:
-
-* Custom GUI, instead of zenity. Allows:
- - Fewer screens by consolidating multiple prompts.
- - Check same password entered second time and don't allow continuing
- if not.
- - Password strengh display, and don't allow continuing if password is too
- weak.
-* Keep secret keys in locked memory until they're encrypted.
- (Raaz makes this possible to do.)
- Would be nice, but not super-important, since gpg secret keys
- are passphrase protected anyway..
-* Don't require --totalshares and --neededshares on restore when unusual
- values were used for backup.
-
- The difficulty is that the number of needed shares cannot be determined by
- looking at shares, and guessing it wrong will result in combining
- too few shares yielding garbage, which it will take up to an hour to
- try to decrypt, before it can tell that more shares are needed.
-
- This could be dealt with by including the number of needed shares in the
- serialization of Share, but then an attacker could use it to partition
- shares from servers. If only one person uses --neededshares=5,
- the attacker can guess that all their shares go together.
-
- What about including the number of needed shares in the name? Since that's
- hashed, it's not visible to an attacker. Keysafe would need to try names
- with 2 shares, then 3, etc, and once it found shares, it would know the
- number needed. It should also be possible to avoid breaking backwards
- compatability, by only including the number of shares in the name when
- it's not the standard number. To avoid needing to re-run argon2 for each
- try, the argon2 hash of the name could be calculated first, and then the
- number of needed shares appended before the final sha256 hash is
- generated.
-
- If an attacker is able to guess the name, and a nonstandard number of
- shares was used, the attacker could upload other objects where they would
- be found before the real objects. This could be used to prevent
- restore from working. (It also makes a malicious data attack (as described
- in https://keysafe.branchable.com/details/) possible by attackers who do not
- control the servers.
-
-Encryption tunables changes:
-
-* Argon2d is more resistent to GPU/ASIC attack optimisation.
- Switching from Argon2i would require new tunables, and delay restores
- (of keys backed up using the old tunables, and when the user provides the
- wrong name) by ~10 minutes, so deferred for now
- until there's some other reason to change the tunables.
-* The ShareIdents derivation currently appends a number and sha256 hashes
- to generate a stream of values. Ben M points out that HMAC is a more
- typical way to do such a thing. Even better, a HKDF-Expand
- (RFC5869) can generate a stream which can then be chunked up into values.
- Either of these would avoid a full pre-image attack on SHA-2 breaking
- keysafe. Of course, such an SHA-2 attack would be a general security
- disaster. HKDF may prove more robust in the face of partial SHA-2 breaks.
- Deferred for now until tthere's some other reason to change keysafe's
- tunables.
diff --git a/doc/comments.mdwn b/doc/comments.mdwn
new file mode 100644
index 0000000..3da643b
--- /dev/null
+++ b/doc/comments.mdwn
@@ -0,0 +1,7 @@
+[[!inline pages="comment_pending(*)" feedfile=pendingmoderation
+description="comments pending moderation" show=-1]]
+Comments in the [[!commentmoderation desc="moderation queue"]]:
+[[!pagecount pages="comment_pending(*)"]]
+
+Recent comments posted to this site:
+[[!inline pages="comment(*)" template="comment"]]
diff --git a/doc/index.mdwn b/doc/index.mdwn
index 873eb35..5c0c670 100644
--- a/doc/index.mdwn
+++ b/doc/index.mdwn
@@ -57,7 +57,8 @@ Install it from there, or from source.
## Git repository
-`git clone git://keysafe.branchable.com/ keysafe`
+`git clone git://keysafe.branchable.com/ keysafe` or
+`git clone https://git.joeyh.name/git/keysafe.git/`
All tags and commits in this repository are gpg signed, and you should
verify the signature before using it.
diff --git a/doc/news/version_0.20170303.mdwn b/doc/news/version_0.20170303.mdwn
new file mode 100644
index 0000000..6d412d4
--- /dev/null
+++ b/doc/news/version_0.20170303.mdwn
@@ -0,0 +1,3 @@
+keysafe 0.20170303 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+ * Updated to use raaz-0.1.1."""]] \ No newline at end of file
diff --git a/doc/serverless.mdwn b/doc/serverless.mdwn
new file mode 100644
index 0000000..555bbfb
--- /dev/null
+++ b/doc/serverless.mdwn
@@ -0,0 +1,129 @@
+idea, unfinished and unreviewed
+
+Storage:
+
+* Input password and name from user. The name is a combination of their
+ own name and a more obscure name (such as the name of their high-school
+ sweetheart).
+* Generate N by argon2(name), tuned to take 10 minutes.
+* Generate list I by sha256 of N+1,2,3.. , hashes truncated to 10 bytes.
+* Generate P, a byte chosen at random.
+* Generate K by argon2(password+name, salt=P), tuned to take 0.195 (50/256)
+ minutes.
+* Generate padding to obscure the length of the data. For example,
+ pad all keys to be as long as a rsa4096 gpg secret key as exported by
+ paperkey (2724 bytes), to prevent targeting types of keys by size.
+* AES encrypt (length + checksum + data + padding) with K as the key,
+ generating D.
+* Split D into 70 byte chunks, call this list C.
+* Generate 36 item list T of 80 byte values
+ (size of bitcoin's `OP_RETURN`), by concating I[x] with D[x].
+* Generate 36 bitcoin transactions storing list T in `OP_RETURN`
+* Store the transactions in bitcoin blockchain.
+
+Retrieval:
+
+* Input password and name from user.
+* Generate N by argon2(name) (takes 10 minutes)
+* Generate list I by sha256 of N+1,2,3.. , hashes truncated to 10 bytes.
+* Begin retrieving transactions from bitcoin blockchain, looking for ones
+ with `OP_RETURN` starting with values from I. Extract the 70 byte
+ chunks from the `OP_RETURN`, generating ordered list C.
+* At the same time, generate K by argon2(password+name, salt=p), guessing
+ values for P until the right one is found (takes 25 minutes on average)
+* Once K and C are available, decrypt (concat C) with K and verify
+ checksum.
+
+Password guessing:
+
+Password guessing cost is unchanged from original keysafe. Eg, 25 CPU-years
+for a super-weak 19 entropy password.
+
+Before password guessing can begin, the keysafe transactions for a key
+need to be found in the blockchain.
+
+The attacker can assume that transactions from around the same time are
+grouped together. But, the attacker needs to order the transactions
+correctly to recover D. If they get the order wrong, their password
+cracking will never succeed. With 36 transactions, and 36 factorial possible
+combinations, guessing the right order is infeasible. So, grouping
+transactions does not benefit an attacker. The attacker needs to guess the
+name and generate I to find the right ordering.
+
+Each guess of a name takes 10 minutes CPU work. This is the same attack
+cost as guessing names to download shares from keysafe servers, and a
+name that attackers are unlikely to guess prevents this attack, and so
+prevents password cracking from starting.
+
+(keysafe servers also impose a proof of work when lots of requests for
+shares are being made, which adds to the cost. That PoW is capped at 8
+minutes per request. This new design doesn't have that so is somewhat weaker.
+To get equal strength as original keysafe, could increase the
+work needed to guess a name to 18 minutes?)
+
+Storage servers:
+
+A storage server can be sent the bitcoin transactions,
+and broadcast them to the bitcoin network. This way keysafe does not
+need to run a full bitcoin node. It may be possible to use eg, electrum
+servers as keysafe storage servers.
+
+Keysafe would need to verify that the transactions reached the
+network (by querying retrieval servers).
+
+Retrieval servers:
+
+Retrieval could download the entire bitcoin blockchain and scan it, but
+that would be gigabytes of download and take probably days. So instead,
+there can be retrieval servers, which the user connects to via tor and
+requests keysafe transactions tagged with I.
+
+Retrieval servers learn which transactions contain keys with still active
+users who have needed to restore their key. These might be more interesting
+transations to try to guess the passwords of. For example, if people are
+using keysafe to safely transit borders with their gpg keys, a government
+might focus on transactions retrieved soon after a person of interest
+crossed a border.
+
+Retrieval servers could provide innaccurate or slow results, or too many
+transactions, to try to prevent key restore. But keysafe can know about a
+lot of retrieval servers, and query several at once. Get a list of
+transaction ids from each, and then go through all the lists in parallel
+and request transactions from each server until it has retrieved enough.
+
+Storage cost:
+
+Current cost per bitcoin transaction is $1, so storing a key in keysafe
+would cost $36.
+
+Of course, keysafe could also use a blockchain other than bitcoin, eg
+lightcoin or dodgecoin have much smaller transaction fees. Using those
+would allow storing the whole gpg public+private key, not only the paperkey
+minimised private key.
+
+The risk is that all copies of a less widely used blockchain could be lost,
+preventing restore of a gpg key. Litecoin is probably big enough and
+dodgecoin amusing enough to avoid such a fate for long enough for keysafe
+backups to be useful.
+
+Name collisions:
+
+If an attacker can guess the name that a user used with keysafe, and their
+password, then as well as decrypting their gpg key, they can add new
+transactions using the same name and encrypred with the same password.
+Keysafe won't be able to tell which to use, and so the user might restore
+a gpg key controlled by the attacker.
+
+Keysafe could prevent this by preferring earlier bitcoin transactions to
+later ones.
+
+On the other hand, a user might reuse a name and password when storing an
+updated/new gpg key. Then they might expect keysafe to restore the newest
+version of the key they stored.
+
+Keysafe might want to try to retrieve old transactions when storing a key,
+to detect if the user is doing that. It could tell the user they need to
+pick a different name if it finds any transactions using the name they
+chose. This would also guard against several of users picking the same name,
+which could otherwise make keysafe restore need to download a lot of
+transactions.
diff --git a/doc/servers.mdwn b/doc/servers.mdwn
index 3e82c7e..d190603 100644
--- a/doc/servers.mdwn
+++ b/doc/servers.mdwn
@@ -178,7 +178,8 @@ source of disk IO.
It's early days still, but keysafe's server code works well enough.
-* `git clone git://keysafe.branchable.com/ keysafe`
+* `git clone git://keysafe.branchable.com/ keysafe` or
+ `git clone https://git.joeyh.name/git/keysafe.git/`
* Be sure to verify the gpg signature of the git repository!
* You will need to install keysafe from source; see its INSTALL file.
Use `make install` to install it, including a systemd service file.
diff --git a/doc/templates/todolist.tmpl b/doc/templates/todolist.tmpl
new file mode 100644
index 0000000..a5d93e9
--- /dev/null
+++ b/doc/templates/todolist.tmpl
@@ -0,0 +1,25 @@
+<div class="archivepage">
+<TMPL_IF PERMALINK>
+<a href="<TMPL_VAR PERMALINK>"><TMPL_VAR TITLE></a>
+<TMPL_ELSE>
+<a href="<TMPL_VAR PAGEURL>"><TMPL_VAR TITLE></a>
+</TMPL_IF>
+<TMPL_IF TAGS>
+<TMPL_LOOP TAGS>
+ [<TMPL_VAR LINK>]
+</TMPL_LOOP>
+</TMPL_IF>
+<br />
+<span class="archivepagedate">
+Posted <TMPL_VAR CTIME>
+<TMPL_IF AUTHOR>
+by <span class="author">
+<TMPL_IF AUTHORURL>
+<a href="<TMPL_VAR AUTHORURL>"><TMPL_VAR AUTHOR></a>
+<TMPL_ELSE>
+<TMPL_VAR AUTHOR>
+</TMPL_IF>
+</span>
+</TMPL_IF>
+</span>
+</div>
diff --git a/doc/todo.mdwn b/doc/todo.mdwn
index 6fa6e18..bc643b4 100644
--- a/doc/todo.mdwn
+++ b/doc/todo.mdwn
@@ -1,4 +1,6 @@
-This is keysafe's todo list. Link items to [[todo/done]] when done.
+This is keysafe's todo list. Please feel free to post ideas here.
+Link items to [[todo/done]] when done.
-[[!inline pages="./todo/* and !./todo/done and !link(done)
-and !*/Discussion" actions=yes postform=yes show=0 archive=yes]]
+[[!inline pages="./todo/* and !./todo/done and !link(done) and
+!./todo/important
+and !*/Discussion" actions=yes postform=yes show=0 archive=yes template=todolist]]
diff --git a/doc/todo/Fix_compiler_errors_in_HEAD.mdwn b/doc/todo/Fix_compiler_errors_in_HEAD.mdwn
new file mode 100644
index 0000000..08dbdc8
--- /dev/null
+++ b/doc/todo/Fix_compiler_errors_in_HEAD.mdwn
@@ -0,0 +1,78 @@
+```
+From e1fee7bb9dd508b9bef420fd76292f91bb151c8c Mon Sep 17 00:00:00 2001
+From: Mitchell Rosen <mitchellwrosen@gmail.com>
+Date: Sat, 1 Apr 2017 14:39:41 -0400
+Subject: [PATCH 1/1] Fix compiler errors
+
+---
+ HTTP/ProofOfWork.hs | 8 ++++----
+ HTTP/Server.hs | 3 ++-
+ 2 files changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/HTTP/ProofOfWork.hs b/HTTP/ProofOfWork.hs
+index 0237347..9a10d39 100644
+--- a/HTTP/ProofOfWork.hs
++++ b/HTTP/ProofOfWork.hs
+@@ -16,7 +16,7 @@ import ByteStrings
+ import GHC.Generics
+ import qualified Data.Text as T
+ import qualified Data.ByteString as B
+-import Data.Text.Encoding (encodeUtf8)
++import Data.Text.Encoding (decodeUtf8, encodeUtf8)
+ import Raaz.Core.Encode
+ import qualified Raaz
+ import Data.BloomFilter.Hash
+@@ -55,7 +55,7 @@ instance Hashable RequestID where
+ hashIO64 = hashIO64 . hashRequestID
+
+ hashRequestID :: RequestID -> B.ByteString
+-hashRequestID rid = encodeUtf8 (fromRandomSalt (randomSalt rid))
++hashRequestID rid = encodeUtf8 (fromRandomSalt (randomSalt rid))
+ <> ":" <> encodeUtf8 (requestHMAC rid)
+
+ -- | Using Text and not ByteString so that ProofOfWorkRequirement can have a
+@@ -80,7 +80,7 @@ maxProofOfWork = Seconds (16*60)
+ -- on the very first try. On average, the client will need to work for half
+ -- as long as the returned number of Seconds.
+ generationTime :: ProofOfWorkRequirement -> Seconds
+-generationTime req =
++generationTime req =
+ let UseArgon2 (CPUCost (Seconds s) _) _ = proofOfWorkHashTunable (addedArgon2Iterations req)
+ in Seconds ((2^(leadingZeros req)) * s)
+
+@@ -109,7 +109,7 @@ mkRequestID secret = mkRequeestID' secret <$> mkRandomSalt
+ mkRequeestID' :: RequestIDSecret -> RandomSalt -> RequestID
+ mkRequeestID' (RequestIDSecret key) salt =
+ let hmac = Raaz.hmacSha256 key (encodeUtf8 $ fromRandomSalt salt)
+- in RequestID salt $ T.toByteString (Raaz.encode hmac :: Base16)
++ in RequestID salt $ decodeUtf8 $ Raaz.toByteString (Raaz.encode hmac :: Base16)
+
+ validRequestID :: RequestIDSecret -> RequestID -> Bool
+ validRequestID secret rid =
+diff --git a/HTTP/Server.hs b/HTTP/Server.hs
+index 61bdbfd..12a8f26 100644
+--- a/HTTP/Server.hs
++++ b/HTTP/Server.hs
+@@ -18,6 +18,7 @@ import CmdLine (ServerConfig(..))
+ import Storage.Local
+ import Serialization ()
+ import Servant
++import Network.Wai (Application)
+ import Network.Wai.Handler.Warp
+ import Control.Monad.IO.Class
+ import Control.Concurrent
+@@ -51,7 +52,7 @@ runServer d cfg = do
+ _ <- forkIO $ obscurerThread st
+ runSettings settings (app st)
+ where
+- settings = setHost host $ setPort (serverPort cfg) $ defaultSettings
++ settings = setHost host $ setPort (serverPort cfg) $ defaultSettings
+ host = fromString (serverAddress cfg)
+
+ serverStorage :: Maybe LocalStorageDirectory -> Storage
+--
+2.12.1
+```
+
+> Thanks for the patch; I applied essentially these fixes although
+> untangled the unrelated issues. [[done]] --[[Joey]]
diff --git a/doc/todo/Make_the_number_of_shard_servers_configurable.mdwn b/doc/todo/Make_the_number_of_shard_servers_configurable.mdwn
new file mode 100644
index 0000000..d9ebe32
--- /dev/null
+++ b/doc/todo/Make_the_number_of_shard_servers_configurable.mdwn
@@ -0,0 +1,3 @@
+Currently 3 shard servers are used for saving the key and either two of them are required to reconstruct the key.
+
+In my opinion it's too easy for any two subjects to cooperate when it comes to key reconstruction. I'd like to therefore have the number of shard servers configurable (with possibly hundreds of shard servers). This implies also having configurable also the minimum number of servers needed for key reconstruction (with possibly hundreds of required pieces of the key).
diff --git a/doc/todo/Make_the_number_of_shard_servers_configurable/comment_1_4416f7495e2a34a3cdb6f5106beaf582._comment b/doc/todo/Make_the_number_of_shard_servers_configurable/comment_1_4416f7495e2a34a3cdb6f5106beaf582._comment
new file mode 100644
index 0000000..1a2871f
--- /dev/null
+++ b/doc/todo/Make_the_number_of_shard_servers_configurable/comment_1_4416f7495e2a34a3cdb6f5106beaf582._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-04-04T16:19:10Z"
+ content="""
+You can use --totalshares and --neededshares to configure how many shares
+keysafe splits the key into. See also
+[[detect_number_of_required_shares_on_restore]].
+
+Bear in mind that colluding servers still have to guess the name used to
+find the shares to combine, and even then they still have the expensive
+work of cracking the password ahead. Splitting the secret across servers is
+only an initial line of defense.
+
+It's debatable whether having a lot of servers would add much security.
+
+But, keysafe needs more servers in any case. With more than 3 servers,
+splits like 3-of-4 and 2-of-4 become usable; these and other parameters are
+probably useful in some cases.
+"""]]
diff --git a/doc/todo/Move_the_project_to_notabug.org.mdwn b/doc/todo/Move_the_project_to_notabug.org.mdwn
new file mode 100644
index 0000000..faec65a
--- /dev/null
+++ b/doc/todo/Move_the_project_to_notabug.org.mdwn
@@ -0,0 +1,9 @@
+Currently it's difficult to discuss anything publicly, refer to solved issues, get a fresh overview of the project's progress, etc.
+
+So I'd like to propose moving the Keysafe project to [https://notabug.org](https://notabug.org ). They're very open, they respect (in fact require) Free Software and they're pretty stable (I'm following them for about 2 years and they're meticulously publicly documenting what is going on) and legally OK.
+
+[https://notabug.org](https://notabug.org ) is running [Gogs](https://gogs.io), which is a SW providing a centralized web interface for a great amount of git projects [https://notabug.org](https://notabug.org ) can be seen as an alternative GitHub, but with good Terms of Service and not the GitHub's ToS which basically say among other things that "you're fully responsible for anything we do in your country/land/region").
+
+If [https://notabug.org](https://notabug.org ) won't suit our needs, could find another alternative allowing easy public discussion of enhancements/bugs of Keysafe and a nicely visible progress overview?
+
+[[done]]
diff --git a/doc/todo/Move_the_project_to_notabug.org/comment_1_a1e7094aa456b5ff6a6fb5adcc34fcab._comment b/doc/todo/Move_the_project_to_notabug.org/comment_1_a1e7094aa456b5ff6a6fb5adcc34fcab._comment
new file mode 100644
index 0000000..02bc5ca
--- /dev/null
+++ b/doc/todo/Move_the_project_to_notabug.org/comment_1_a1e7094aa456b5ff6a6fb5adcc34fcab._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-04-04T16:58:17Z"
+ content="""
+I have nothing against notabug, but I prefer to use my own tools and keep
+everything in a git repository (not some sql-based wrapper around a git
+repository). So I don't think I'll be doing that.
+
+People generally don't seem to find it impossible to use ikiwiki,
+which is what we're using here, for public discussion, to refer to solved
+issues, to see what's going on, etc.
+"""]]
diff --git a/doc/todo/add_--key-value_option.mdwn b/doc/todo/add_--key-value_option.mdwn
new file mode 100644
index 0000000..bd6e918
--- /dev/null
+++ b/doc/todo/add_--key-value_option.mdwn
@@ -0,0 +1,5 @@
+--key-value=$N which eliminates the question about password value,
+and rejects passwords that would cost less than $N to crack at current
+rates. This should add a combo box to the password entry form in the
+GUI to let the user adjust the $N there.
+
diff --git a/doc/todo/back_up_only_chosen_subkeys.mdwn b/doc/todo/back_up_only_chosen_subkeys.mdwn
new file mode 100644
index 0000000..17a4b4b
--- /dev/null
+++ b/doc/todo/back_up_only_chosen_subkeys.mdwn
@@ -0,0 +1,2 @@
+I'm a DD, so I don't want keysafe to ever back up my signing subkey nor my master key. But I'd like it to back up my encryption subkey, so that I don't lose access to (e.g.) git-annex special remotes encrypted against that key. It would be good if keysafe asked me which subkeys I want to back up. --spwhitton
+
diff --git a/doc/todo/custom_gui.mdwn b/doc/todo/custom_gui.mdwn
new file mode 100644
index 0000000..c2863f6
--- /dev/null
+++ b/doc/todo/custom_gui.mdwn
@@ -0,0 +1,10 @@
+Custom GUI, instead of zenity. Allows:
+
+* Fewer screens by consolidating multiple prompts.
+* Check same password entered second time and don't allow continuing
+ if not.
+* Password strengh display, and don't allow continuing if password is too
+ weak.
+* Make --gui password entry fields longer, so user does not feel they
+ need to make password short. (zenity does not seem to allow configuring
+ this.)
diff --git a/doc/todo/delay_some_uploads_to_prevent_correlation.mdwn b/doc/todo/delay_some_uploads_to_prevent_correlation.mdwn
new file mode 100644
index 0000000..5b9e324
--- /dev/null
+++ b/doc/todo/delay_some_uploads_to_prevent_correlation.mdwn
@@ -0,0 +1,19 @@
+In backup, only upload to some servers immediately, and delay the rest
+for up to several days, with some uploads of chaff, to prevent
+collaborating evil servers from correlating related shards.
+
+How many servers should be uploaded to immediately? The safe answer is at least
+M (--neededshares); that way the secret key does get backed up immediately.
+
+Uploading to less would be more secure, but risks the user thinking it
+finished backing up the key, and eg, wiping their laptop. So careful
+messaging would be needed in this case.
+
+Might just upload M-1 shares immediatly, and show a dialog saying, the
+backup will be completed next Wednesday, or click here to finish it now.
+
+----
+
+Also, when there are multiple chunks, they are currently uploaded in order.
+That could easily be shuffled, with server A getting its share of chunk 2
+first, server B its share of chunk 3 first, etc.
diff --git a/doc/todo/detect_bad_password_on_restore_and_re-prompt.mdwn b/doc/todo/detect_bad_password_on_restore_and_re-prompt.mdwn
new file mode 100644
index 0000000..f010676
--- /dev/null
+++ b/doc/todo/detect_bad_password_on_restore_and_re-prompt.mdwn
@@ -0,0 +1,2 @@
+If we retrieved enough shares successfully, but decrypt failed, must
+be a wrong password, so prompt for re-entry and retry with those shares.
diff --git a/doc/todo/detect_number_of_required_shares_on_restore.mdwn b/doc/todo/detect_number_of_required_shares_on_restore.mdwn
new file mode 100644
index 0000000..4bfa080
--- /dev/null
+++ b/doc/todo/detect_number_of_required_shares_on_restore.mdwn
@@ -0,0 +1,34 @@
+When --totalshares and --neededshares were used to back up a key,
+those options (well at least --neededshares)
+also have to be provided at restore time to make it try to find
+enough shares to restore.
+
+It would be good to detect the number of required shares so the user does
+not need to remember to do that.
+
+The difficulty is that the number of needed shares cannot be determined by
+looking at shares, and guessing it wrong will result in combining
+too few shares yielding garbage, which it will take up to an hour to
+try to decrypt, before it can tell that more shares are needed.
+
+This could be dealt with by including the number of needed shares in the
+serialization of Share, but then an attacker could use it to partition
+shares from servers. If only one person uses --neededshares=5,
+the attacker can guess that all their shares go together.
+
+What about including the number of needed shares in the name? Since that's
+hashed, it's not visible to an attacker. Keysafe would need to try names
+with 2 shares, then 3, etc, and once it found shares, it would know the
+number needed. It should also be possible to avoid breaking backwards
+compatability, by only including the number of shares in the name when
+it's not the standard number. To avoid needing to re-run argon2 for each
+try, the argon2 hash of the name could be calculated first, and then the
+number of needed shares appended before the final sha256 hash is
+generated.
+
+Problem with this: If an attacker is able to guess the name, and a
+nonstandard number of shares was used, the attacker could upload other
+objects where they would be found before the real objects. This could be
+used to prevent restore from working. (It also makes a malicious data
+attack (as described in https://keysafe.branchable.com/details/) possible
+by attackers who do not control the servers.
diff --git a/doc/todo/done.mdwn b/doc/todo/done.mdwn
new file mode 100644
index 0000000..e7c9808
--- /dev/null
+++ b/doc/todo/done.mdwn
@@ -0,0 +1,4 @@
+recently fixed [[todo]] items.
+
+[[!inline pages="./* and link(./done) and !*/Discussion" sort=mtime show=10
+archive=yes]]
diff --git a/doc/todo/future_encryption_tunables_changes.mdwn b/doc/todo/future_encryption_tunables_changes.mdwn
new file mode 100644
index 0000000..8a9b29d
--- /dev/null
+++ b/doc/todo/future_encryption_tunables_changes.mdwn
@@ -0,0 +1,18 @@
+If switching any of the encryption tunables for some reason,
+consider making these changes all at once:
+
+* Argon2d is more resistent to GPU/ASIC attack optimisation.
+ Switching from Argon2i would require new tunables, and delay restores
+ (of keys backed up using the old tunables, and when the user provides the
+ wrong name) by ~10 minutes, so deferred for now
+ until there's some other reason to change the tunables.
+* The ShareIdents derivation currently appends a number and sha256 hashes
+ to generate a stream of values. Ben M points out that HMAC is a more
+ typical way to do such a thing. Even better, a HKDF-Expand
+ (RFC5869) can generate a stream which can then be chunked up into values.
+ Either of these would avoid a full pre-image attack on SHA-2 breaking
+ keysafe. Of course, such an SHA-2 attack would be a general security
+ disaster. HKDF may prove more robust in the face of partial SHA-2 breaks.
+ Deferred for now until tthere's some other reason to change keysafe's
+ tunables.
+* Perhaps use CHACHA2 instead of AES?
diff --git a/doc/todo/important.mdwn b/doc/todo/important.mdwn
new file mode 100644
index 0000000..46d2b49
--- /dev/null
+++ b/doc/todo/important.mdwn
@@ -0,0 +1,4 @@
+important [[todo]] items.
+
+[[!inline pages="./* and link(./important) and !*/Discussion" sort=mtime show=10
+archive=yes]]
diff --git a/doc/todo/improve_restore_progress_bar.mdwn b/doc/todo/improve_restore_progress_bar.mdwn
new file mode 100644
index 0000000..5a6af76
--- /dev/null
+++ b/doc/todo/improve_restore_progress_bar.mdwn
@@ -0,0 +1 @@
+improve restore progress bar points (update after every hash try)
diff --git a/doc/todo/need_one_more_recommended_server.mdwn b/doc/todo/need_one_more_recommended_server.mdwn
new file mode 100644
index 0000000..a699643
--- /dev/null
+++ b/doc/todo/need_one_more_recommended_server.mdwn
@@ -0,0 +1,8 @@
+There is currently only 1 recommended server; keysafe needs one more before
+it can be used without warning that it's uploading 2 shares to
+not-recommended servers.
+
+Probably need a new recommended server. The existing non-recommended
+servers don't have warrant canaries for various reasons.
+
+[[!tag important]]
diff --git a/doc/todo/padding_to_prevent_traffic_analysis.mdwn b/doc/todo/padding_to_prevent_traffic_analysis.mdwn
new file mode 100644
index 0000000..c2aa3c1
--- /dev/null
+++ b/doc/todo/padding_to_prevent_traffic_analysis.mdwn
@@ -0,0 +1,3 @@
+Add some random padding to http requests and responses, to make it
+harder for traffic analysis to tell that given TOR traffic is
+keysafe traffic.
diff --git a/doc/todo/remove_gpg_key_passphrase_from_backed_up_gpg_keys.mdwn b/doc/todo/remove_gpg_key_passphrase_from_backed_up_gpg_keys.mdwn
new file mode 100644
index 0000000..5a370be
--- /dev/null
+++ b/doc/todo/remove_gpg_key_passphrase_from_backed_up_gpg_keys.mdwn
@@ -0,0 +1,17 @@
+Remove gpg key passohrase from gpg keys that keysafe backs up.
+
+The reason for this is that the user may well forget their gpg key
+passphrase, and it's *weird* to restore a key with keysafe's password
+and then have it passphrase protected.
+
+The gpg key passphrase is intended only to keep a key from being used
+for a short period of time (a week or so) when the device holding it
+is known to have been compromised, so the key can be revoked.
+This doesn't really apply to keys backed up with keysafe -- if they get
+compromised somehow, the user won't know, and cracking the gpg passphrase
+should be almost trivial to an attacker who was able to break keysafe's
+password.
+
+paperkey can remove gpg key passphrases. Is there any better way?
+It might make sense for keysafe to prompt for a new gpg passphrase
+when restoring.
diff --git a/doc/todo/remove_jargon.mdwn b/doc/todo/remove_jargon.mdwn
new file mode 100644
index 0000000..8706010
--- /dev/null
+++ b/doc/todo/remove_jargon.mdwn
@@ -0,0 +1,4 @@
+Add --no-jargon which makes the UI avoid terms like "secret key" and "crack
+password". Do usability testing!
+
+Should perhaps be default?
diff --git a/doc/todo/set_up_--check-servers_in_cron_job.mdwn b/doc/todo/set_up_--check-servers_in_cron_job.mdwn
new file mode 100644
index 0000000..311ae71
--- /dev/null
+++ b/doc/todo/set_up_--check-servers_in_cron_job.mdwn
@@ -0,0 +1,3 @@
+So I know when servers go down. --[[Joey]]
+
+[[!tag important]]
diff --git a/doc/todo/usability_testing.mdwn b/doc/todo/usability_testing.mdwn
new file mode 100644
index 0000000..de8e801
--- /dev/null
+++ b/doc/todo/usability_testing.mdwn
@@ -0,0 +1,3 @@
+The attack cost display can lead to a false sense of security if the user
+takes it as gospel. It needs to be clear that it's an estimate. Is it?
+This and other parts of the keysafe UI need usability testing.
diff --git a/doc/todo/use_cryptohash_argon2.mdwn b/doc/todo/use_cryptohash_argon2.mdwn
new file mode 100644
index 0000000..61670a2
--- /dev/null
+++ b/doc/todo/use_cryptohash_argon2.mdwn
@@ -0,0 +1,2 @@
+cryptohash includes argon2 now, so it could be used instead of the separate
+argon2 library. --[[Joey]]
diff --git a/doc/todo/use_locked_memory.mdwn b/doc/todo/use_locked_memory.mdwn
new file mode 100644
index 0000000..2d139a3
--- /dev/null
+++ b/doc/todo/use_locked_memory.mdwn
@@ -0,0 +1,5 @@
+Keep secret keys in locked memory until they're encrypted.
+(Raaz makes this possible to do.)
+Would be nice, but not super-important, since gpg secret keys
+are passphrase protected anyway.. But, see [[remove_gpg_key_passphrase_from_backed_up_gpg_keys]].
+
diff --git a/keysafe.cabal b/keysafe.cabal
index 42e95fd..188d02a 100644
--- a/keysafe.cabal
+++ b/keysafe.cabal
@@ -1,5 +1,5 @@
Name: keysafe
-Version: 0.20170303
+Version: 0.20170811
Cabal-Version: >= 1.8
Maintainer: Joey Hess <joey@kitenet.net>
Author: Joey Hess
@@ -19,7 +19,6 @@ Description:
License-File: AGPL
Extra-Source-Files:
CHANGELOG
- TODO
INSTALL
keysafe.1
keysafe.service
@@ -52,18 +51,18 @@ Executable keysafe
, unix == 2.7.*
, filepath == 1.4.*
, split == 0.2.*
- , directory == 1.2.*
+ , directory (>= 1.2 && < 1.4)
, process (>= 1.2 && < 1.5)
- , optparse-applicative == 0.12.*
+ , optparse-applicative (>= 0.12 && < 0.14)
, readline == 1.0.*
, zxcvbn-c == 1.0.*
- , servant (>= 0.7 && < 0.9)
- , servant-server (>= 0.7 && < 0.9)
- , servant-client (>= 0.7 && < 0.9)
- , aeson == 0.11.*
+ , servant (>= 0.7 && < 0.12)
+ , servant-server (>= 0.7 && < 0.12)
+ , servant-client (>= 0.7 && < 0.12)
+ , aeson (>= 0.11 && < 1.2)
, wai == 3.2.*
, warp == 3.2.*
- , http-client == 0.4.*
+ , http-client (>= 0.5.3 && < 0.6)
, transformers (>= 0.4 && < 0.6)
, stm == 2.4.*
, socks == 0.5.*
@@ -79,7 +78,7 @@ Executable keysafe
, unix-compat == 0.4.*
, exceptions == 0.8.*
, random-shuffle == 0.0.*
- , MonadRandom == 0.4.*
+ , MonadRandom (>= 0.4 && < 0.6)
Other-Modules:
AutoStart
BackupLog
diff --git a/stack.yaml b/stack.yaml
index 0deb662..6704d6b 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,16 +1,9 @@
packages:
- '.'
-resolver: lts-6.12
+resolver: lts-9.0
extra-deps:
- - secret-sharing-1.0.0.3
- - dice-entropy-conduit-1.0.0.1
- - polynomial-0.7.2
- - finite-field-0.8.0
- - raaz-0.1.1
- - zxcvbn-c-1.0.0
- - servant-0.7.1
- - servant-server-0.7.1
- - servant-client-0.7.1
- - token-bucket-0.1.0.1
- - argon2-1.2.0
+- argon2-1.2.0
+- secret-sharing-1.0.0.3
+- dice-entropy-conduit-1.0.0.1
+- polynomial-0.7.3
explicit-setup-deps: