public void StretchedSecret() { var cipher = "AES-256"; var hash = "SHA256"; var secret = new byte[] { 195, 191, 209, 165, 209, 201, 127, 122, 136, 111, 31, 66, 111, 68, 38, 155, 216, 204, 46, 181, 200, 188, 170, 204, 104, 74, 239, 251, 173, 114, 222, 234 }; StretchedKey.Generate(cipher, hash, secret, out StretchedKey k1, out StretchedKey k2); Assert.IsNotNull(k1); CollectionAssert.AreEqual(new byte[] { 208, 132, 203, 169, 253, 52, 40, 83, 161, 91, 17, 71, 33, 136, 67, 96 }, k1.IV); CollectionAssert.AreEqual(new byte[] { 156, 48, 241, 157, 92, 248, 153, 186, 114, 127, 195, 114, 106, 104, 215, 133, 35, 11, 131, 137, 123, 70, 74, 26, 15, 60, 189, 32, 67, 221, 115, 137 }, k1.CipherKey); CollectionAssert.AreEqual(new byte[] { 6, 179, 91, 245, 224, 56, 153, 120, 77, 140, 29, 5, 15, 213, 187, 65, 137, 230, 202, 120 }, k1.MacKey); Assert.IsNotNull(k2); CollectionAssert.AreEqual(new byte[] { 236, 17, 34, 141, 90, 106, 197, 56, 197, 184, 157, 135, 91, 88, 112, 19 }, k2.IV); CollectionAssert.AreEqual(new byte[] { 151, 145, 195, 219, 76, 195, 102, 109, 187, 231, 100, 150, 132, 245, 251, 130, 254, 37, 178, 55, 227, 34, 114, 39, 238, 34, 2, 193, 107, 130, 32, 87 }, k2.CipherKey); CollectionAssert.AreEqual(new byte[] { 3, 229, 77, 212, 241, 217, 23, 113, 220, 126, 38, 255, 18, 117, 108, 205, 198, 89, 1, 236 }, k2.MacKey); }
/// <summary> /// Create two streched keys from the shared secret. /// </summary> /// <remarks> /// The is no spec for this. Copied https://github.com/libp2p/go-libp2p-crypto/blob/0f79fbebcb64f746a636aba79ece0635ec5919e9/key.go#L183 /// </remarks> public static void Generate(string cipherName, string hashName, byte[] secret, out StretchedKey k1, out StretchedKey k2) { int cipherKeySize; int ivSize; switch (cipherName) { case "AES-128": ivSize = 16; cipherKeySize = 16; break; case "AES-256": ivSize = 16; cipherKeySize = 32; break; case "Blowfish": ivSize = 8; cipherKeySize = 32; break; default: throw new NotSupportedException($"Cipher '{cipherName}' is not supported."); } var hmacKeySize = 20; var need = 2 * (ivSize + cipherKeySize + hmacKeySize); var hmac = new HMac(DigestUtilities.GetDigest(hashName)); var kp = new KeyParameter(secret); var seed = Encoding.ASCII.GetBytes("key expansion"); hmac.Init(kp); var a = new byte[hmac.GetMacSize()]; var b = new byte[hmac.GetMacSize()]; hmac.BlockUpdate(seed, 0, seed.Length); hmac.DoFinal(a, 0); int j = 0; var result = new byte[need]; while (j < need) { hmac.Reset(); hmac.BlockUpdate(a, 0, a.Length); hmac.BlockUpdate(seed, 0, seed.Length); hmac.DoFinal(b, 0); var todo = b.Length; if (j + todo > need) { todo = need - j; } Buffer.BlockCopy(b, 0, result, j, todo); j += todo; hmac.Reset(); hmac.BlockUpdate(a, 0, a.Length); hmac.DoFinal(a, 0); } int half = need / 2; k1 = new StretchedKey { IV = new byte[ivSize], CipherKey = new byte[cipherKeySize], MacKey = new byte[hmacKeySize] }; Buffer.BlockCopy(result, 0, k1.IV, 0, ivSize); Buffer.BlockCopy(result, ivSize, k1.CipherKey, 0, cipherKeySize); Buffer.BlockCopy(result, ivSize + cipherKeySize, k1.MacKey, 0, hmacKeySize); k2 = new StretchedKey { IV = new byte[ivSize], CipherKey = new byte[cipherKeySize], MacKey = new byte[hmacKeySize] }; Buffer.BlockCopy(result, half, k2.IV, 0, ivSize); Buffer.BlockCopy(result, half + ivSize, k2.CipherKey, 0, cipherKeySize); Buffer.BlockCopy(result, half + ivSize + cipherKeySize, k2.MacKey, 0, hmacKeySize); }