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))); }
// 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); } }
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)); }
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)); }
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); }