コード例 #1
0
ファイル: Program.cs プロジェクト: MushR00m/Kerberos.NET
            protected override IKeyAgreement StartKeyAgreement()
            {
                IKeyAgreement agreement = null;

                switch (random.Next(4))
                {
                case 0:
                    agreement = new BCryptDiffieHellmanOakleyGroup14();
                    break;

                case 1:
                    agreement = new BCryptDiffieHellmanOakleyGroup2();
                    break;

                case 2:
                    agreement = new ManagedDiffieHellmanOakley14();
                    break;

                case 3:
                    agreement = new ManagedDiffieHellmanOakley2();
                    break;
                }

                W($"DH Type: {agreement.GetType()}");

                if (agreement == null)
                {
                    throw new ArgumentException("How did it get here?");
                }

                return(agreement);
            }
コード例 #2
0
        private async Task <IKeyAgreement> FromDiffieHellmanDomainParametersAsync(KrbSubjectPublicKeyInfo clientPublicValue)
        {
            var parameters = KrbDiffieHellmanDomainParameters.DecodeSpecial(clientPublicValue.Algorithm.Parameters.Value);

            IKeyAgreement agreement = null;

            switch (parameters.P.Length)
            {
            case 128:
                agreement = CryptoPal.Platform.DiffieHellmanModp2(
                    await Service.Principals.RetrieveKeyCache(KeyAgreementAlgorithm.DiffieHellmanModp2)
                    );
                break;

            case 256:
                agreement = CryptoPal.Platform.DiffieHellmanModp14(
                    await Service.Principals.RetrieveKeyCache(KeyAgreementAlgorithm.DiffieHellmanModp14)
                    );
                break;

            default:
                throw new InvalidOperationException("Unknown key agreement parameter");
            }

            var publicKey = DiffieHellmanKey.ParsePublicKey(clientPublicValue.SubjectPublicKey, agreement.PublicKey.KeyLength);

            agreement.ImportPartnerKey(publicKey);

            return(agreement);
        }
コード例 #3
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);
        }
コード例 #4
0
        /// <summary>
        /// Applies credential-specific changes to the KDC-REQ message and is what supplies the PKINIT properties to the request.
        /// </summary>
        /// <param name="req">The <see cref="KrbKdcReq"/> that will be modified.</param>
        public override void TransformKdcReq(KrbKdcReq req)
        {
            agreement = StartKeyAgreement();

            // We don't support the straight RSA mode because
            // it doesn't rely on ephemeral key agreement
            // which isn't great security-wise

            if (agreement == null)
            {
                throw OnlyKeyAgreementSupportedException();
            }

            var padata = req.PaData.ToList();

            KrbAuthPack authPack;

            if (SupportsEllipticCurveDiffieHellman)
            {
                authPack = CreateEllipticCurveDiffieHellmanAuthPack(req.Body);
            }
            else if (SupportsDiffieHellman)
            {
                authPack = CreateDiffieHellmanAuthPack(req.Body);
            }
            else
            {
                throw OnlyKeyAgreementSupportedException();
            }

            KerberosConstants.Now(out authPack.PKAuthenticator.CTime, out authPack.PKAuthenticator.CuSec);

            SignedCms signed = new SignedCms(
                new ContentInfo(
                    IdPkInitAuthData,
                    authPack.Encode().ToArray()
                    )
                );

            var signer = new CmsSigner(Certificate)
            {
                IncludeOption = IncludeOption
            };

            signed.ComputeSignature(signer, silent: true);

            var pk = new KrbPaPkAsReq {
                SignedAuthPack = signed.Encode()
            };

            padata.Add(new KrbPaData
            {
                Type  = PaDataType.PA_PK_AS_REQ,
                Value = pk.Encode()
            });

            req.PaData = padata.ToArray();
        }
コード例 #5
0
ファイル: Program.cs プロジェクト: MushR00m/Kerberos.NET
            protected override bool CacheKeyAgreementParameters(IKeyAgreement agreement)
            {
                var serializedPk = JsonConvert.SerializeObject(ConvertKey(agreement.PrivateKey as DiffieHellmanKey));

                using (var reg = Registry.CurrentUser.CreateSubKey($"SOFTWARE\\Kerberos.NET\\{UserName}"))
                {
                    reg.SetValue("DHParameter", serializedPk, RegistryValueKind.String);
                }

                return(true);
            }
コード例 #6
0
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                InitializationNonce?.Shred();
                InitializationNonce = null;

                (LocalEcdhForInit as IDisposable)?.Dispose();
                LocalEcdhForInit = null;
            }
        }
コード例 #7
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);
        }
コード例 #8
0
        private void LoadInternal(Stream memory, IKeyAgreementFactory kexFac)
        {
            var versionInt = memory.ReadByte();

            if (versionInt < 0)
            {
                throw new EndOfStreamException();
            }
            var versionByte = (byte)versionInt;

            bool isClient           = (versionByte & 0b0000_1000) != 0;
            bool hasInit            = (versionByte & 0b0001_0000) != 0;
            bool hasRatchet         = (versionByte & 0b0010_0000) != 0;
            bool hasEcdh            = (versionByte & 0b0100_0000) != 0;
            bool hasServerPublicKey = (versionByte & 0b1000_0000) != 0;

            if (!isClient)
            {
                throw new InvalidOperationException("The provided state is not client state");
            }

            if (hasInit)
            {
                if (InitializationNonce == null || InitializationNonce.Length != MicroRatchetContext.InitializationNonceSize)
                {
                    InitializationNonce = new byte[MicroRatchetContext.InitializationNonceSize];
                }

                memory.Read(InitializationNonce, 0, MicroRatchetContext.InitializationNonceSize);
            }

            if (hasEcdh)
            {
                LocalEcdhForInit = kexFac.Deserialize(memory);
            }

            if (hasServerPublicKey)
            {
                if (ServerPublicKey == null || ServerPublicKey.Length != MicroRatchetContext.ExpectedPublicKeySize)
                {
                    ServerPublicKey = new byte[MicroRatchetContext.ExpectedPublicKeySize];
                }
                memory.Read(ServerPublicKey, 0, MicroRatchetContext.ExpectedPublicKeySize);
            }

            if (hasRatchet)
            {
                ReadRatchet(memory, kexFac);
            }

            Log.Verbose($"Read {memory.Position} bytes of client state");
        }
コード例 #9
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 });
        }
コード例 #10
0
        public static EcdhRatchetStep Create(IKeyAgreement EcdhKey, byte[] NextRootKey,
                                             int receivingGeneration, byte[] receivingHeaderKey, byte[] receivingNextHeaderKey, byte[] receivingChainKey,
                                             int sendingGeneration, byte[] sendingHeaderKey, byte[] sendingNextHeaderKey, byte[] sendingChainKey)
        {
            if (NextRootKey != null && NextRootKey.Length != 32)
            {
                throw new InvalidOperationException("The next root key size needs to be 32 bytes");
            }
            if (receivingHeaderKey != null && receivingHeaderKey.Length != 32)
            {
                throw new InvalidOperationException("The receiving header key size needs to be 32 bytes");
            }
            if (receivingNextHeaderKey != null && receivingNextHeaderKey.Length != 32)
            {
                throw new InvalidOperationException("The next receiving header key size needs to be 32 bytes");
            }
            if (receivingChainKey != null && receivingChainKey.Length != 32)
            {
                throw new InvalidOperationException("The receiving chain key size needs to be 32 bytes");
            }
            if (sendingHeaderKey != null && sendingHeaderKey.Length != 32)
            {
                throw new InvalidOperationException("The sending header key size needs to be 32 bytes");
            }
            if (sendingNextHeaderKey != null && sendingNextHeaderKey.Length != 32)
            {
                throw new InvalidOperationException("The next sending header key size needs to be 32 bytes");
            }
            if (sendingChainKey != null && sendingChainKey.Length != 32)
            {
                throw new InvalidOperationException("The sending chain key size needs to be 32 bytes");
            }

            var step = new EcdhRatchetStep()
            {
                EcdhKey              = EcdhKey,
                NextRootKey          = NextRootKey,
                ReceiveHeaderKey     = receivingHeaderKey,
                NextReceiveHeaderKey = receivingNextHeaderKey,
                SendHeaderKey        = sendingHeaderKey,
                NextSendHeaderKey    = sendingNextHeaderKey
            };

            step.ReceivingChain.Initialize(receivingChainKey);
            step.ReceivingChain.Generation = receivingGeneration;
            step.SendingChain.Initialize(sendingChainKey);
            step.SendingChain.Generation = sendingGeneration;

            return(step);
        }
コード例 #11
0
        internal static KrbDiffieHellmanDomainParameters FromKeyAgreement(IKeyAgreement agreement)
        {
            if (!(agreement.PublicKey is DiffieHellmanKey pk))
            {
                throw new ArgumentException("Not a DH key agreement");
            }

            return(new KrbDiffieHellmanDomainParameters
            {
                P = Pad(pk.Modulus),
                G = DepadRight(pk.Generator),
                Q = pk.Factor
            });
        }
コード例 #12
0
        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);
        }
コード例 #13
0
        private static void AssertKeysAgree(IKeyAgreement alice, IKeyAgreement bob)
        {
            var aliceDerived = alice.GenerateAgreement();
            var bobDerived   = bob.GenerateAgreement();

            var match = aliceDerived.Span.SequenceEqual(bobDerived.Span);

            if (!match)
            {
                Hex.Debug(aliceDerived.ToArray());
                Hex.Debug(bobDerived.ToArray());
            }

            Assert.IsTrue(match);

            var empty = new byte[aliceDerived.Length];

            Assert.IsFalse(aliceDerived.Span.SequenceEqual(empty));
        }
コード例 #14
0
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                ClientInitializationNonce?.Shred();
                ClientInitializationNonce = null;
                RootKey?.Shred();
                RootKey = null;
                FirstSendHeaderKey?.Shred();
                FirstSendHeaderKey = null;
                FirstReceiveHeaderKey?.Shred();
                FirstReceiveHeaderKey = null;
                ClientPublicKey?.Shred();
                ClientPublicKey = null;

                (LocalEcdhRatchetStep0 as IDisposable)?.Dispose();
                LocalEcdhRatchetStep0 = null;
                (LocalEcdhRatchetStep1 as IDisposable)?.Dispose();
                LocalEcdhRatchetStep1 = null;
            }
        }
コード例 #15
0
        private void LoadInternal(Stream memory, IKeyAgreementFactory kexFac)
        {
            var versionInt = memory.ReadByte();

            if (versionInt < 0)
            {
                throw new EndOfStreamException();
            }
            var versionByte = (byte)versionInt;

            bool isClient           = (versionByte & 0b0000_1000) != 0;
            bool hasInit            = (versionByte & 0b0001_0000) != 0;
            bool hasRatchet         = (versionByte & 0b0010_0000) != 0;
            bool hasEcdh            = (versionByte & 0b0100_0000) != 0;
            bool hasClientPublicKey = (versionByte & 0b1000_0000) != 0;

            if (isClient)
            {
                throw new InvalidOperationException("The provided state is not server state");
            }

            if (hasInit)
            {
                if (ClientInitializationNonce == null || ClientInitializationNonce.Length != MicroRatchetContext.InitializationNonceSize)
                {
                    ClientInitializationNonce = new byte[MicroRatchetContext.InitializationNonceSize];
                }
                if (RootKey == null || RootKey.Length != KeySizeInBytes)
                {
                    RootKey = new byte[KeySizeInBytes];
                }
                if (FirstSendHeaderKey == null || FirstSendHeaderKey.Length != KeySizeInBytes)
                {
                    FirstSendHeaderKey = new byte[KeySizeInBytes];
                }
                if (FirstReceiveHeaderKey == null || FirstReceiveHeaderKey.Length != KeySizeInBytes)
                {
                    FirstReceiveHeaderKey = new byte[KeySizeInBytes];
                }
                if (NextInitializationNonce == null || NextInitializationNonce.Length != MicroRatchetContext.InitializationNonceSize)
                {
                    NextInitializationNonce = new byte[MicroRatchetContext.InitializationNonceSize];
                }

                memory.Read(ClientInitializationNonce, 0, MicroRatchetContext.InitializationNonceSize);
                memory.Read(RootKey, 0, KeySizeInBytes);
                memory.Read(FirstSendHeaderKey, 0, KeySizeInBytes);
                memory.Read(FirstReceiveHeaderKey, 0, KeySizeInBytes);
                memory.Read(NextInitializationNonce, 0, MicroRatchetContext.InitializationNonceSize);
            }

            if (hasEcdh)
            {
                LocalEcdhRatchetStep0 = kexFac.Deserialize(memory);
                LocalEcdhRatchetStep1 = kexFac.Deserialize(memory);
            }

            if (hasClientPublicKey)
            {
                if (ClientPublicKey == null || ClientPublicKey.Length != KeySizeInBytes)
                {
                    ClientPublicKey = new byte[32];
                }
                memory.Read(ClientPublicKey, 0, 32);
            }

            if (hasRatchet)
            {
                ReadRatchet(memory, kexFac);
            }

            Log.Verbose($"Read {memory.Position} bytes of server state");
        }
コード例 #16
0
 /// <summary>
 /// If overridden this method will cache the key agreement private keys to reduce key generation time.
 /// Note that caching Key Agreement private keys is not recommended as these keys should be ephemeral.
 /// </summary>
 /// <param name="agreement">The agreement private key to cache.</param>
 /// <returns>Returns true if the key was cached, otherwise it will return false.</returns>
 protected virtual bool CacheKeyAgreementParameters(IKeyAgreement agreement) => false;
コード例 #17
0
        /// <summary>
        /// Applies credential-specific changes to the KDC-REQ message and is what supplies the PKINIT properties to the request.
        /// </summary>
        /// <param name="req">The <see cref="KrbKdcReq"/> that will be modified.</param>
        public override void TransformKdcReq(KrbKdcReq req)
        {
            if (req == null)
            {
                throw new ArgumentNullException(nameof(req));
            }

            this.agreement = this.StartKeyAgreement();

            // We don't support the straight RSA mode because
            // it doesn't rely on ephemeral key agreement
            // which isn't great security-wise

            if (this.agreement == null)
            {
                throw OnlyKeyAgreementSupportedException();
            }

            var padata = req.PaData.ToList();

            KrbAuthPack authPack;

            if (this.SupportsEllipticCurveDiffieHellman)
            {
                authPack = this.CreateEllipticCurveDiffieHellmanAuthPack(req.Body);
            }
            else if (this.SupportsDiffieHellman)
            {
                authPack = this.CreateDiffieHellmanAuthPack(req.Body);
            }
            else
            {
                throw OnlyKeyAgreementSupportedException();
            }

            Now(out DateTimeOffset ctime, out int usec);

            authPack.PKAuthenticator.CTime = ctime;
            authPack.PKAuthenticator.CuSec = usec;

            SignedCms signed = new SignedCms(
                new ContentInfo(
                    IdPkInitAuthData,
                    authPack.Encode().ToArray()
                    )
                );

            var signer = new CmsSigner(this.Certificate)
            {
                IncludeOption = this.IncludeOption
            };

            signed.ComputeSignature(signer, silent: !CanPrompt);

            var pk = new KrbPaPkAsReq {
                SignedAuthPack = signed.Encode()
            };

            padata.Add(new KrbPaData
            {
                Type  = PaDataType.PA_PK_AS_REQ,
                Value = pk.Encode()
            });

            req.PaData = padata.ToArray();
        }
コード例 #18
0
ファイル: Program.cs プロジェクト: MushR00m/Kerberos.NET
 protected override bool CacheKeyAgreementParameters(IKeyAgreement agreement)
 {
     return(false);
 }
コード例 #19
0
        private byte[] DeconstructMessage(State state, byte[] payload, byte[] headerKey, EcdhRatchetStep ratchetUsed, bool usedNextHeaderKey)
        {
            if (state == null)
            {
                throw new ArgumentNullException(nameof(state));
            }
            if (payload == null)
            {
                throw new ArgumentNullException(nameof(payload));
            }
            if (headerKey == null)
            {
                throw new ArgumentNullException(nameof(headerKey));
            }

            var messageSize    = payload.Length;
            var encryptedNonce = new ArraySegment <byte>(payload, 0, NonceSize);

            // decrypt the nonce
            var headerEncryptionNonce = new ArraySegment <byte>(payload, payload.Length - MacSize - HeaderIVSize, HeaderIVSize);
            var hcipher        = new AesCtrMode(GetHeaderKeyCipher(headerKey), headerEncryptionNonce);
            var decryptedNonce = hcipher.Process(encryptedNonce);

            CheckNonce(headerEncryptionNonce);

            // get the ecdh bit
            var hasEcdh = (decryptedNonce[0] & 0b1000_0000) != 0;

            decryptedNonce[0] &= 0b0111_1111;

            // extract ecdh if needed
            var step = BigEndianBitConverter.ToInt32(decryptedNonce);

            if (hasEcdh)
            {
                var clientEcdhPublic = new ArraySegment <byte>(hcipher.Process(new ArraySegment <byte>(payload, NonceSize, EcNumSize)));

                if (ratchetUsed == null)
                {
                    // an override header key was used.
                    // this means we have to initialize the ratchet
                    if (!(state is ServerState serverState))
                    {
                        throw new InvalidOperationException("Only the server can initialize a ratchet.");
                    }
                    ratchetUsed = EcdhRatchetStep.InitializeServer(KeyDerivation, Digest,
                                                                   serverState.LocalEcdhRatchetStep0,
                                                                   serverState.RootKey, clientEcdhPublic,
                                                                   serverState.LocalEcdhRatchetStep1,
                                                                   serverState.FirstReceiveHeaderKey,
                                                                   serverState.FirstSendHeaderKey);
                    serverState.Ratchets.Add(ratchetUsed);
                }
                else
                {
                    if (usedNextHeaderKey)
                    {
                        // perform ecdh ratchet
                        IKeyAgreement newEcdh = KeyAgreementFactory.GenerateNew();

                        // this is the hottest line in the deconstruct process:
                        EcdhRatchetStep newRatchet = ratchetUsed.Ratchet(KeyDerivation, Digest, clientEcdhPublic, newEcdh);
                        state.Ratchets.Add(newRatchet);
                        ratchetUsed = newRatchet;
                    }
                }
            }


            // get the inner payload key from the receive chain
            if (ratchetUsed == null)
            {
                throw new InvalidOperationException("An override header key was used but the message did not contain ECDH parameters");
            }
            (var key, var _) = ratchetUsed.ReceivingChain.RatchetForReceiving(KeyDerivation, step);
            CheckNonce(key);

            // get the encrypted payload
            var payloadOffset    = hasEcdh ? NonceSize + EcNumSize : NonceSize;
            var encryptedPayload = new ArraySegment <byte>(payload, payloadOffset, messageSize - payloadOffset - MacSize);

            // decrypt the inner payload
            var icipher = new AesCtrMode(AesFactory.GetAes(true, key), decryptedNonce);
            var decryptedInnerPayload = icipher.Process(encryptedPayload);

            return(decryptedInnerPayload);
        }
コード例 #20
0
        private void ReceiveInitializationResponse(State state, byte[] data)
        {
            if (!(state is ClientState clientState))
            {
                throw new InvalidOperationException("Only the client can receive an init response.");
            }

            var messageSize    = data.Length;
            var macOffset      = messageSize - MacSize;
            var headerIvOffset = macOffset - HeaderIVSize;
            var headerSize     = InitializationNonceSize + EcNumSize;
            var payloadSize    = messageSize - headerSize - MacSize;

            // decrypt header
            var cipher          = new AesCtrMode(AesFactory.GetAes(true, Configuration.ApplicationKey), data, headerIvOffset, HeaderIVSize);
            var decryptedHeader = cipher.Process(data, 0, headerSize);

            Array.Copy(decryptedHeader, 0, data, 0, headerSize);

            // new nonce(16), ecdh pubkey(32), <nonce(16), server pubkey(32),
            // new ecdh pubkey(32) x2, signature(64)>, mac(12)
            var nonce            = new ArraySegment <byte>(data, 0, InitializationNonceSize);
            var rootEcdhKey      = new ArraySegment <byte>(data, InitializationNonceSize, EcNumSize);
            var encryptedPayload = new ArraySegment <byte>(data, headerSize, payloadSize);

            CheckNonce(nonce);

            // decrypt payload
            IKeyAgreement rootEcdh   = clientState.LocalEcdhForInit;
            var           rootPreKey = rootEcdh.DeriveKey(rootEcdhKey);

            rootPreKey = Digest.ComputeDigest(rootPreKey);
            cipher     = new AesCtrMode(AesFactory.GetAes(true, rootPreKey), nonce);
            var decryptedPayload = cipher.Process(encryptedPayload);

            Array.Copy(decryptedPayload, 0, data, headerSize, payloadSize);

            // extract some goodies
            var oldNonce           = new ArraySegment <byte>(data, headerSize, InitializationNonceSize);
            var serverPubKey       = new ArraySegment <byte>(data, headerSize + InitializationNonceSize, EcNumSize);
            var remoteRatchetEcdh0 = new ArraySegment <byte>(data, headerSize + InitializationNonceSize + EcNumSize, EcNumSize);
            var remoteRatchetEcdh1 = new ArraySegment <byte>(data, headerSize + InitializationNonceSize + EcNumSize * 2, EcNumSize);

            // make sure the nonce sent back by the server (which is encrypted and signed)
            // matches the nonce we sent previously
            if (!oldNonce.Matches(clientState.InitializationNonce))
            {
                throw new InvalidOperationException("Nonce did not match");
            }

            // verify that the signature matches
            IVerifier verifier = VerifierFactory.Create(serverPubKey);

            if (!verifier.VerifySignedMessage(Digest, new ArraySegment <byte>(data, 0, payloadSize + headerSize)))
            {
                throw new InvalidOperationException("The signature was invalid");
            }

            // keep the server public key around
            clientState.ServerPublicKey = serverPubKey.ToArray();

            // store the new nonce we got from the server
            clientState.InitializationNonce = nonce.ToArray();
            Log.Verbose($"storing iniitlizaionta nonce: {Log.ShowBytes(nonce)}");

            // we now have enough information to construct our double ratchet
            IKeyAgreement localStep0EcdhRatchet = KeyAgreementFactory.GenerateNew();
            IKeyAgreement localStep1EcdhRatchet = KeyAgreementFactory.GenerateNew();

            // initialize client root key and ecdh ratchet
            var genKeys          = KeyDerivation.GenerateKeys(rootPreKey, clientState.InitializationNonce, 3, 32);
            var rootKey          = genKeys[0];
            var receiveHeaderKey = genKeys[1];
            var sendHeaderKey    = genKeys[2];

            clientState.Ratchets.Add(EcdhRatchetStep.InitializeClient(KeyDerivation, Digest, rootKey,
                                                                      remoteRatchetEcdh0, remoteRatchetEcdh1, localStep0EcdhRatchet,
                                                                      receiveHeaderKey, sendHeaderKey,
                                                                      localStep1EcdhRatchet));

            clientState.LocalEcdhForInit = null;
        }
コード例 #21
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);
        }