/// <summary> /// Encode a publicKey into miniLock format. /// </summary> /// <param name="publicKey">A 32 byte publicKey.</param> /// <exception cref="Sodium.Exceptions.KeyOutOfRangeException"></exception> /// <returns>A Base58 encoded publicKey.</returns> public static string EncodeMiniLockPublicKey(byte[] publicKey) { if (publicKey == null || publicKey.Length != PublicKeyBytes) { throw new KeyOutOfRangeException("publicKey", (publicKey == null) ? 0 : publicKey.Length, string.Format("key must be {0} bytes in length.", PublicKeyBytes)); } var final = ArrayHelper.ConcatArrays(publicKey, Blake2S.Hash(publicKey, (byte[])null, 1)); return(Base58CheckEncoding.EncodePlain(final)); }
/// <summary> /// Decode a miniLock ID into a byte array. /// </summary> /// <param name="encodedPublicKey">The miniLock ID.</param> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="CorruptIdentityException"></exception> /// <returns>A 32 byte array.</returns> public static byte[] DecodeMiniLockPublicKey(string encodedPublicKey) { if (encodedPublicKey == null) { throw new ArgumentNullException("encodedPublicKey", "encodedPublicKey cannot be null"); } var raw = Base58CheckEncoding.DecodePlain(encodedPublicKey); var publicKey = ArrayHelper.SubArray(raw, 0, 32); var checksum = ArrayHelper.SubArray(raw, 32); // validate the checksum if (!checksum.SequenceEqual(Blake2S.Hash(publicKey, (byte[])null, 1))) { throw new CorruptIdentityException("the given identity seems to be an invalid miniLock ID"); } return(publicKey); }
public void Test() { var vectors = new List <TestVector> { new TestVector { Id = 1, InputHex = "", KeyHex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", ResultHex = "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49" }, new TestVector { Id = 2, InputHex = "00", KeyHex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", ResultHex = "40d15fee7c328830166ac3f918650f807e7e01e177258cdc0a39b11f598066f1" }, new TestVector { Id = 3, InputHex = "0001", KeyHex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", ResultHex = "6bb71300644cd3991b26ccd4d274acd1adeab8b1d7914546c1198bbe9fc9d803" } }; foreach (var vector in vectors) { var result = TestHelper.StringToByteArray(vector.ResultHex); var hash = Blake2S.Hash(TestHelper.StringToByteArray(vector.InputHex), TestHelper.StringToByteArray(vector.KeyHex), result.Length); CollectionAssert.AreEqual(result, hash); Console.WriteLine("Test Vector " + vector.Id + ": passed"); } }
/// <summary> /// Generate a MiniLock Keypair from an email and a password. /// </summary> /// <param name="email">A valid (format) email address.</param> /// <param name="password">A password with a minimal entropy of 100.</param> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="InvalidMailException"></exception> /// <exception cref="LowEntropyException"></exception> /// <returns>A libsodium compatible KeyPair.</returns> public static KeyPair GenerateMiniLockKeyPair(string email, string password) { const int minPasswordEntropy = 100; if (email == null) { throw new ArgumentNullException("email", "email cannot be null"); } // perform a simple email check if (!StringHelper.IsValidEmail(email)) { throw new InvalidMailException("the given email address seems to be invalid"); } if (password == null) { throw new ArgumentNullException("password", "password cannot be null"); } var passwordEntropy = Zxcvbn.Zxcvbn.MatchPassword(password).Entropy; // check the entropy if (passwordEntropy < 100) { throw new LowEntropyException( string.Format( "miniLock needs at least an entropy of {0}, the given password only has an entropy of {1}.", minPasswordEntropy, passwordEntropy)); } var passwordHash = Blake2S.Hash(Encoding.UTF8.GetBytes(password), (byte[])null, 32); var seed = SCrypt.ComputeDerivedKey(passwordHash, Encoding.UTF8.GetBytes(email), 131072, 8, 1, 1, 32); var keyPair = PublicKeyBox.GenerateKeyPair(seed); return(keyPair); }