예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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 });
        }
예제 #4
0
        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);
        }
예제 #5
0
 public byte[] GetPublicKey() => EcdhKey.GetPublicKey();