public static EcdhRatchetStep InitializeServer(IKeyDerivation kdf, IDigest digest, IKeyAgreement previousKeyPair, byte[] rootKey, ArraySegment <byte> remotePublicKey, IKeyAgreement keyPair, byte[] receiveHeaderKey, byte[] sendHeaderKey) { if (receiveHeaderKey.Length != 32 || sendHeaderKey.Length != 32) { throw new InvalidOperationException("Keys need to be 32 bytes."); } Log.Verbose($"--Initialize ECDH Ratchet"); Log.Verbose($"Root Key: {Log.ShowBytes(rootKey)}"); Log.Verbose($"Prev ECDH Private: ({Log.ShowBytes(previousKeyPair.GetPublicKey())})"); Log.Verbose($"ECDH Public: {Log.ShowBytes(remotePublicKey)}"); Log.Verbose($"Curr ECDH Private: ({Log.ShowBytes(keyPair.GetPublicKey())})"); var e = new EcdhRatchetStep { EcdhKey = keyPair, ReceiveHeaderKey = receiveHeaderKey, SendHeaderKey = sendHeaderKey }; // receive chain Log.Verbose(" --Receiving Chain"); var rcderived = previousKeyPair.DeriveKey(remotePublicKey); rcderived = digest.ComputeDigest(rcderived); Log.Verbose($" C Input Key: {Log.ShowBytes(rootKey)}"); Log.Verbose($" C Key Info: {Log.ShowBytes(rcderived)}"); var rckeys = kdf.GenerateKeys(rcderived, rootKey, 3, 32); Log.Verbose($" C Key Out 0: {Log.ShowBytes(rckeys[0])}"); Log.Verbose($" C Key Out 1: {Log.ShowBytes(rckeys[1])}"); Log.Verbose($" C Key Out 2: {Log.ShowBytes(rckeys[2])}"); rootKey = rckeys[0]; e.ReceivingChain.Initialize(rckeys[1]); e.NextReceiveHeaderKey = rckeys[2]; // send chain Log.Verbose(" --Sending Chain"); var scderived = keyPair.DeriveKey(remotePublicKey); scderived = digest.ComputeDigest(scderived); Log.Verbose($" C Input Key: {Log.ShowBytes(rootKey)}"); Log.Verbose($" C Key Info: {Log.ShowBytes(scderived)}"); var sckeys = kdf.GenerateKeys(scderived, rootKey, 3, 32); Log.Verbose($" C Key Out 0: {Log.ShowBytes(sckeys[0])}"); Log.Verbose($" C Key Out 1: {Log.ShowBytes(sckeys[1])}"); Log.Verbose($" C Key Out 2: {Log.ShowBytes(sckeys[2])}"); rootKey = sckeys[0]; e.SendingChain.Initialize(sckeys[1]); e.NextSendHeaderKey = sckeys[2]; // next root key Log.Verbose($"Next Root Key: ({Log.ShowBytes(rootKey)})"); e.NextRootKey = rootKey; return(e); }
public static byte[][] GenerateKeys(this IKeyDerivation kdf, byte[] key, byte[] info, int numKeys, int keySize) { byte[] totalKeyBytes = kdf.GenerateBytes(key, info, keySize * numKeys); byte[][] keys = new byte[numKeys][]; for (int i = 0; i < numKeys; i++) { keys[i] = new byte[keySize]; Array.Copy(totalKeyBytes, i * keySize, keys[i], 0, keySize); } return(keys); }
public static EcdhRatchetStep[] InitializeClient(IKeyDerivation kdf, IDigest digest, byte[] rootKey, ArraySegment <byte> remotePublicKey0, ArraySegment <byte> remotePublicKey1, IKeyAgreement keyPair, byte[] receiveHeaderKey, byte[] sendHeaderKey, IKeyAgreement nextKeyPair) { if (receiveHeaderKey.Length != 32 || sendHeaderKey.Length != 32) { throw new InvalidOperationException("Keys need to be 32 bytes."); } Log.Verbose($"--Initialize ECDH Ratchet CLIENT"); Log.Verbose($"Root Key: {Log.ShowBytes(rootKey)}"); Log.Verbose($"ECDH Public 0: {Log.ShowBytes(remotePublicKey0)}"); Log.Verbose($"ECDH Public 1: {Log.ShowBytes(remotePublicKey1)}"); Log.Verbose($"ECDH Private: ({Log.ShowBytes(keyPair.GetPublicKey())})"); var e0 = new EcdhRatchetStep { EcdhKey = keyPair, SendHeaderKey = sendHeaderKey }; // receive chain doesn't exist Log.Verbose(" --Receiving Chain"); // send chain Log.Verbose(" --Sending Chain"); var scderived = keyPair.DeriveKey(remotePublicKey0); scderived = digest.ComputeDigest(scderived); Log.Verbose($" C Input Key: {Log.ShowBytes(rootKey)}"); Log.Verbose($" C Key Info: {Log.ShowBytes(scderived)}"); var sckeys = kdf.GenerateKeys(scderived, rootKey, 3, 32); Log.Verbose($" C Key Out 0: {Log.ShowBytes(sckeys[0])}"); Log.Verbose($" C Key Out 1: {Log.ShowBytes(sckeys[1])}"); Log.Verbose($" C Key Out 2: {Log.ShowBytes(sckeys[2])}"); rootKey = sckeys[0]; e0.SendingChain.Initialize(sckeys[1]); var nextSendHeaderKey = sckeys[2]; var e1 = InitializeServer(kdf, digest, keyPair, rootKey, remotePublicKey1, nextKeyPair, receiveHeaderKey, nextSendHeaderKey); return(new[] { e0, e1 }); }
public EcdhRatchetStep Ratchet(IKeyDerivation kdf, IDigest digest, ArraySegment <byte> remotePublicKey, IKeyAgreement keyPair) { var nextStep = InitializeServer(kdf, digest, EcdhKey, NextRootKey, remotePublicKey, keyPair, NextReceiveHeaderKey, NextSendHeaderKey); NextRootKey = null; EcdhKey = null; NextSendHeaderKey = null; NextReceiveHeaderKey = null; return(nextStep); }
public (byte[] key, int generation) RatchetForSending(IKeyDerivation kdf) { // message keys are 128 bit var nextKeyBytes = kdf.GenerateBytes(ChainKey, ChainContext, 32 + 16); byte[] nextChainKey = new byte[32]; Array.Copy(nextKeyBytes, nextChainKey, 32); byte[] messageKey = new byte[16]; Array.Copy(nextKeyBytes, 32, messageKey, 0, 16); Log.Verbose($" RTC #: {Generation + 1}"); Log.Verbose($" RTC IN: {Log.ShowBytes(ChainKey)}"); Log.Verbose($" RTC CK: {Log.ShowBytes(nextChainKey)}"); Log.Verbose($" RTC OK: {Log.ShowBytes(messageKey)}"); Generation++; ChainKey = nextChainKey; return(messageKey, Generation); }
public (byte[] key, int generation) RatchetForReceiving(IKeyDerivation kdf, int toGeneration) { int gen; byte[] chain; if (toGeneration > Generation) { gen = Generation; chain = ChainKey; } else if (toGeneration > OldGeneration && OldChainKey != null) { gen = OldGeneration; chain = OldChainKey; } else { throw new InvalidOperationException("Could not ratchet to the generation because the old keys have been lost"); } bool mustSkip = toGeneration > Generation && toGeneration - Generation > 1; bool incrementOld = toGeneration > OldGeneration && OldChainKey != null && toGeneration == OldGeneration + 1; byte[] key = null; while (gen < toGeneration) { Log.Verbose($" RTC #: {gen + 1}"); Log.Verbose($" RTC IN: {Log.ShowBytes(chain)}"); // message keys are 128 bit var nextKeyBytes = kdf.GenerateBytes(chain, ChainContext, 32 + 16); byte[] nextChainKey = new byte[32]; Array.Copy(nextKeyBytes, nextChainKey, 32); byte[] messageKey = new byte[16]; Array.Copy(nextKeyBytes, 32, messageKey, 0, 16); gen++; chain = nextChainKey; key = messageKey; Log.Verbose($" RTC CK: {Log.ShowBytes(nextChainKey)}"); Log.Verbose($" RTC OK: {Log.ShowBytes(messageKey)}"); } if (mustSkip && OldChainKey == null) { OldChainKey = ChainKey; OldGeneration = Generation; } if (toGeneration > Generation) { Generation = gen; ChainKey = chain; } if (incrementOld) { OldGeneration++; OldChainKey = chain; } return(key, gen); }