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); }
private byte[] SendInitializationRequest(State state) { // message format: // nonce(16), pubkey(32), ecdh(32), padding(...), signature(64), mac(12) if (!(state is ClientState clientState)) { throw new InvalidOperationException("Only the client can send init request."); } // 16 bytes nonce clientState.InitializationNonce = RandomNumberGenerator.Generate(InitializationNonceSize); // get the public key var pubkey = Signature.GetPublicKey(); // generate new ECDH keypair for init message and root key IKeyAgreement clientEcdh = KeyAgreementFactory.GenerateNew(); clientState.LocalEcdhForInit = clientEcdh; // nonce(16), <pubkey(32), ecdh(32), signature(64)>, mac(12) var initializationMessageSize = InitializationNonceSize + EcNumSize * 4 + MacSize; var messageSize = Math.Max(Configuration.MinimumMessageSize, initializationMessageSize); var initializationMessageSizeWithSignature = messageSize - MacSize; var initializationMessageSizeWithoutSignature = messageSize - MacSize - SignatureSize; var signatureOffset = messageSize - MacSize - SignatureSize; var message = new byte[messageSize]; Array.Copy(clientState.InitializationNonce, 0, message, 0, InitializationNonceSize); Array.Copy(pubkey, 0, message, InitializationNonceSize, EcNumSize); Array.Copy(clientEcdh.GetPublicKey(), 0, message, InitializationNonceSize + EcNumSize, EcNumSize); // sign the message var digest = Digest.ComputeDigest(message, 0, initializationMessageSizeWithoutSignature); Array.Copy(Signature.Sign(digest), 0, message, signatureOffset, SignatureSize); // encrypt the message with the application key var cipher = new AesCtrMode(AesFactory.GetAes(true, Configuration.ApplicationKey), clientState.InitializationNonce); var encryptedPayload = cipher.Process(message, InitializationNonceSize, initializationMessageSizeWithSignature - InitializationNonceSize); Array.Copy(encryptedPayload, 0, message, InitializationNonceSize, initializationMessageSizeWithSignature - InitializationNonceSize); // calculate mac var Mac = new Poly(AesFactory); Mac.Init(Configuration.ApplicationKey, clientState.InitializationNonce, MacSize); Mac.Process(message, 0, initializationMessageSizeWithSignature); var mac = Mac.Compute(); Array.Copy(mac, 0, message, initializationMessageSizeWithSignature, MacSize); return(message); }
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 }); }
private byte[] SendInitializationResponse(State state, ArraySegment <byte> initializationNonce, ArraySegment <byte> remoteEcdhForInit) { // message format: // new nonce(16), ecdh pubkey(32), // <nonce from init request(16), server pubkey(32), // new ecdh pubkey(32) x2, Padding(...), signature(64)>, mac(12) = 236 bytes if (!(state is ServerState serverState)) { throw new InvalidOperationException("Only the server can send init response."); } // generate a nonce and new ecdh parms var serverNonce = RandomNumberGenerator.Generate(InitializationNonceSize); serverState.NextInitializationNonce = serverNonce; IKeyAgreement rootPreEcdh = KeyAgreementFactory.GenerateNew(); var rootPreEcdhPubkey = rootPreEcdh.GetPublicKey(); // generate server ECDH for root key and root key var rootPreKey = rootPreEcdh.DeriveKey(remoteEcdhForInit); rootPreKey = Digest.ComputeDigest(rootPreKey); var genKeys = KeyDerivation.GenerateKeys(rootPreKey, serverNonce, 3, EcNumSize); serverState.RootKey = genKeys[0]; serverState.FirstSendHeaderKey = genKeys[1]; serverState.FirstReceiveHeaderKey = genKeys[2]; // generate two server ECDH. One for ratchet 0 sending key and one for the next // this is enough for the server to generate a receiving chain key and sending // chain key as soon as the client sends a sending chain key IKeyAgreement serverEcdhRatchet0 = KeyAgreementFactory.GenerateNew(); serverState.LocalEcdhRatchetStep0 = serverEcdhRatchet0; IKeyAgreement serverEcdhRatchet1 = KeyAgreementFactory.GenerateNew(); serverState.LocalEcdhRatchetStep1 = serverEcdhRatchet1; var minimumMessageSize = InitializationNonceSize * 2 + EcNumSize * 6 + MacSize; var entireMessageSize = Math.Max(Configuration.MinimumMessageSize, minimumMessageSize); var macOffset = entireMessageSize - MacSize; var entireMessageWithoutMacOrSignatureSize = macOffset - SignatureSize; var encryptedPayloadOffset = InitializationNonceSize + EcNumSize; var encryptedPayloadSize = macOffset - encryptedPayloadOffset; // construct the message var message = new byte[entireMessageSize]; Array.Copy(serverNonce, 0, message, 0, InitializationNonceSize); Array.Copy(rootPreEcdhPubkey, 0, message, InitializationNonceSize, EcNumSize); // construct the to-be-encrypted part var rre0 = serverEcdhRatchet0.GetPublicKey(); var rre1 = serverEcdhRatchet1.GetPublicKey(); Array.Copy(initializationNonce.Array, initializationNonce.Offset, message, encryptedPayloadOffset, InitializationNonceSize); Array.Copy(Signature.GetPublicKey(), 0, message, encryptedPayloadOffset + InitializationNonceSize, EcNumSize); Array.Copy(rre0, 0, message, encryptedPayloadOffset + InitializationNonceSize + EcNumSize, EcNumSize); Array.Copy(rre1, 0, message, encryptedPayloadOffset + InitializationNonceSize + EcNumSize * 2, EcNumSize); // sign the message var digest = Digest.ComputeDigest(message, 0, entireMessageWithoutMacOrSignatureSize); Array.Copy(Signature.Sign(digest), 0, message, entireMessageWithoutMacOrSignatureSize, SignatureSize); // encrypt the message var cipher = new AesCtrMode(AesFactory.GetAes(true, rootPreKey), serverNonce); var encryptedPayload = cipher.Process(message, encryptedPayloadOffset, encryptedPayloadSize); Array.Copy(encryptedPayload, 0, message, encryptedPayloadOffset, encryptedPayloadSize); // encrypt the header cipher = new AesCtrMode(AesFactory.GetAes(true, Configuration.ApplicationKey), encryptedPayload, encryptedPayloadSize - HeaderIVSize, HeaderIVSize); var encryptedHeader = cipher.Process(message, 0, encryptedPayloadOffset); Array.Copy(encryptedHeader, 0, message, 0, encryptedPayloadOffset); // calculate mac var Mac = new Poly(AesFactory); Mac.Init(Configuration.ApplicationKey, encryptedHeader, 0, InitializationNonceSize, MacSize); Mac.Process(message, 0, macOffset); var mac = Mac.Compute(); Array.Copy(mac, 0, message, macOffset, MacSize); return(message); }
public byte[] GetPublicKey() => EcdhKey.GetPublicKey();