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