/// <summary> /// Sign /// </summary> /// <param name="privateKey">private key</param> /// <param name="data">data to be signed</param> /// <param name="signature">signature returned</param> /// <returns>true if the signing succeeded, otherwise false.</returns> public override bool Sign(byte[] privateKey, byte[] data, out byte[] signature) { // based on draft-irtf-cfrg-eddsa-08 "Edwards-curve Digital Signature Algorithm (EdDSA)" if (privateKey.Length != 32) { signature = null; return(false); } using (var sha512 = new SHA512CryptoServiceProvider()) { byte[] hash; hash = sha512.ComputeHash(privateKey); byte[] sdata = new byte[32]; Buffer.BlockCopy(hash, 0, sdata, 0, 32); sdata[0] &= (byte)((0xff << _c) & 0xff); // clear lower bits sdata[31] &= (byte)((1 << (_n % 8)) - 1); // clear higher bits sdata[31] |= (byte)(1 << (_n % 8)); // set top bit Array.Reverse(sdata); // to big endian var s = new BigInteger(sdata); var G = GetBasePoint(); byte[] A; if (!EncodePoint(PointMul(s, G), out A)) { signature = null; return(false); } sha512.Initialize(); sha512.TransformBlock(hash, 32, 32, null, 0); sha512.TransformFinalBlock(data, 0, data.Length); byte[] rdata = sha512.Hash; Array.Reverse(rdata); // to big endian var r = new BigInteger(rdata) % this._l; byte[] R; if (!EncodePoint(PointMul(r, G), out R)) { signature = null; return(false); } sha512.Initialize(); sha512.TransformBlock(R, 0, R.Length, null, 0); sha512.TransformBlock(A, 0, A.Length, null, 0); sha512.TransformFinalBlock(data, 0, data.Length); byte[] kdata = sha512.Hash; Array.Reverse(kdata); // to big endian var k = new BigInteger(kdata) % this._l; var S = (r + k * s) % this._l; byte[] sig = new byte[64]; Buffer.BlockCopy(R, 0, sig, 0, R.Length); // copy 32 bytes byte[] wS = S.GetBytes(); Array.Reverse(wS); // to little endian Buffer.BlockCopy(wS, 0, sig, 32, wS.Length); signature = sig; return(true); } }
/// <summary> /// bcrypt_pbkdf (pkcs #5 pbkdf2 implementation using the "bcrypt" hash) /// </summary> /// <param name="pass">password</param> /// <param name="salt">salt</param> /// <param name="rounds">rounds</param> /// <param name="keylen">key length</param> /// <returns>key</returns> public byte[] BcryptPbkdf(string pass, byte[] salt, uint rounds, int keylen) { // this code is based on OpenBSD's bcrypt_pbkdf.c if (rounds < 1) { return(null); } if (pass.Length == 0 || salt.Length == 0 || keylen <= 0 || keylen > 1024) { return(null); } byte[] key = new byte[keylen]; int stride = (keylen + 32 - 1) / 32; int amt = (keylen + stride - 1) / stride; var blowfish = new Blowfish(); using (var sha512 = new SHA512CryptoServiceProvider()) { // collapse password byte[] passData = Encoding.UTF8.GetBytes(pass); byte[] sha2pass = sha512.ComputeHash(passData); // generate key, sizeof(out) at a time byte[] countsalt = new byte[4]; for (int count = 1; keylen > 0; ++count) { countsalt[0] = (byte)(count >> 24); countsalt[1] = (byte)(count >> 16); countsalt[2] = (byte)(count >> 8); countsalt[3] = (byte)(count); // first round, salt is salt sha512.Initialize(); sha512.TransformBlock(salt, 0, salt.Length, null, 0); sha512.TransformFinalBlock(countsalt, 0, countsalt.Length); byte[] sha2salt = sha512.Hash; byte[] tmpout = BcryptHash(blowfish, sha2pass, sha2salt); byte[] output = (byte[])tmpout.Clone(); for (uint r = rounds; r > 1; --r) { // subsequent rounds, salt is previous output sha512.Initialize(); sha2salt = sha512.ComputeHash(tmpout); tmpout = BcryptHash(blowfish, sha2pass, sha2salt); for (int i = 0; i < output.Length; ++i) { output[i] ^= tmpout[i]; } } // pbkdf2 deviation: output the key material non-linearly. amt = Math.Min(amt, keylen); int k; for (k = 0; k < amt; ++k) { int dest = k * stride + (count - 1); if (dest >= key.Length) { break; } key[dest] = output[k]; } keylen -= k; } } return(key); }