Beispiel #1
0
        async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetInitialEndToEndEncryptionKey(string recipientId)
        {
            E2EUser user = await GetCheckedUser(recipientId);

            // user.DynamicPrivateDecryptionKeys = new Dictionary<long, byte[]>(); // don't do this. Or only the last receipt of a resent message can be decrypted
            user.LatestDynamicPublicKey   = null;
            user.LatestDynamicPublicKeyId = 0;


            long nextDynamicPublicKeyId = this._ratchetTimer.GetNextTicks(0);
            var  ecdhKeypair            = this._visualCrypt2Service.GenerateECKeyPair().Result;

            byte[] dynamicPublicKey = ecdhKeypair.PublicKey;

            user.DynamicPrivateDecryptionKeys[nextDynamicPublicKeyId] = ecdhKeypair.PrivateKey;
            RemoveExcessKeys(user);

            await this._updateUser(user);

            long privateKeyHint = 0;

            var dynamicSharedSecret          = this._visualCrypt2Service.CalculateAndHashSharedSecret(ecdhKeypair.PrivateKey, user.StaticPublicKey);
            var symmetricKeyMaterial         = ByteArrays.Concatenate(dynamicSharedSecret, user.AuthSecret);
            var symmetricKeyMaterialMetaData = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]);             // note we are not using user.AuthSecret fro the metadata

            return(new Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64>(new KeyMaterial64(symmetricKeyMaterial), dynamicPublicKey, nextDynamicPublicKeyId, privateKeyHint, new KeyMaterial64(symmetricKeyMaterialMetaData)));
        }
Beispiel #2
0
        // TODO: Review this, compare it with TLSCLient.RemovePreviousKeys and when key cleanup is done
        // This may not work correctly.
        void RemoveExcessKeys(E2EUser user)
        {
            var excess = user.DynamicPrivateDecryptionKeys.Keys.OrderByDescending(k => k).Skip(KeepLatestDynamicPrivateKeys);

            foreach (var keyId in excess)
            {
                user.DynamicPrivateDecryptionKeys.Remove(keyId);
            }
        }
Beispiel #3
0
        public async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetE2EEncryptionKeyCommon(string recipientId, bool?isInitial)
        {
            E2EUser user = await GetCheckedUser(recipientId);

            if (user.IsJustInitialized)
            {
                isInitial = true;
            }

            if (isInitial == true)             // When the contact was just added (the ratchet was just initialized, AuthSecret was null before), or we are answering for a resent request, we use this 'initial' method.
            {
                return(await GetInitialEndToEndEncryptionKey(recipientId));
            }
            return(await GetEndToEndEncryptionKey(recipientId));
        }
Beispiel #4
0
        async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetEndToEndEncryptionKey(string recipientId)
        {
            E2EUser user = await GetCheckedUser(recipientId);

            long existingMaxKeyId = 0;

            if (user.DynamicPrivateDecryptionKeys.Keys.Count > 0)             // count might be 0 initially...might be a bug or not
            {
                existingMaxKeyId = user.DynamicPrivateDecryptionKeys.Keys.Max();
            }

            long nextDynamicPublicKeyId = this._ratchetTimer.GetNextTicks(existingMaxKeyId);

            var ecdhKeypair = this._visualCrypt2Service.GenerateECKeyPair().Result;

            byte[] dynamicPublicKey = ecdhKeypair.PublicKey;

            long privateKeyHint;

            user.DynamicPrivateDecryptionKeys[nextDynamicPublicKeyId] = ecdhKeypair.PrivateKey;
            RemoveExcessKeys(user);
            await this._updateUser(user);

            byte[] dynamicOrStaticPublicKey;
            if (user.LatestDynamicPublicKey != null)
            {
                dynamicOrStaticPublicKey = user.LatestDynamicPublicKey;
                privateKeyHint           = user.LatestDynamicPublicKeyId;
            }
            else
            {
                dynamicOrStaticPublicKey = user.StaticPublicKey;
                privateKeyHint           = 0;
            }

            var dynamicSharedSecret = this._visualCrypt2Service.CalculateAndHashSharedSecret(ecdhKeypair.PrivateKey, dynamicOrStaticPublicKey);

            var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, user.AuthSecret);

            return(new Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64>(new KeyMaterial64(symmetricKeyMaterial), dynamicPublicKey, nextDynamicPublicKeyId, privateKeyHint, null));
        }
Beispiel #5
0
        async Task <E2EUser> GetCheckedUser(string userId)
        {
            E2EUser user = await this._gu(userId);

            if (user.DynamicPrivateDecryptionKeys == null)
            {
                throw new InvalidOperationException("Must be guaranteed on object creation, atm in AppRepository, line 315.");
            }
            if (user.AuthSecret == null)
            {
                user.AuthSecret = this._visualCrypt2Service.CalculateAndHashSharedSecret(this._myStaticPrivateKey,
                                                                                         user.StaticPublicKey);
                user.IsJustInitialized = true;
            }

            if (user.IsJustInitialized)
            {
                await this._updateUser(user);
            }
            return(user);
        }