Exemple #1
0
        async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetInitialEndToEndEncryptionKeyAsync(string recipientId)
        {
            E2EUser user = await GetCheckedUserAsync(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  random      = this.ixdsCryptoService.GetRandom(32).Result.X;
            var  ecdhKeyPair = this.ixdsCryptoService.GenerateCurve25519KeyPairExact(random).Result;

            byte[] dynamicPublicKey = ecdhKeyPair.PublicKey;

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

            await this.parameters.UpdateUser(user);

            long privateKeyHint = 0;

            var dynamicSharedSecret          = this.ixdsCryptoService.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)));
        }
Exemple #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);
            }
        }
Exemple #3
0
        public async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetE2EEncryptionKeyCommonAsync(string recipientId, bool?isInitial)
        {
            EnsureInitialized();

            E2EUser user = await GetCheckedUserAsync(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 GetInitialEndToEndEncryptionKeyAsync(recipientId));
            }
            return(await GetEndToEndEncryptionKeyAsync(recipientId));
        }
Exemple #4
0
        async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetEndToEndEncryptionKeyAsync(string recipientId)
        {
            E2EUser user = await GetCheckedUserAsync(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 random      = this.ixdsCryptoService.GetRandom(32).Result.X;
            var ecdhKeypair = this.ixdsCryptoService.GenerateCurve25519KeyPairExact(random).Result;

            byte[] dynamicPublicKey = ecdhKeypair.PublicKey;

            long privateKeyHint;

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

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

            var dynamicSharedSecret = this.ixdsCryptoService.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));
        }
Exemple #5
0
        async Task <E2EUser> GetCheckedUserAsync(string userId)
        {
            E2EUser user = await this.parameters.GetUser(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.ixdsCryptoService.CalculateAndHashSharedSecret(this.parameters.OwnStaticPrivateKey,
                                                                                      user.StaticPublicKey);
                user.IsJustInitialized = true;
            }

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