InputDerivedKey32 CreatePasswordDerivedKeyWithBCrypt(IV16 iv, KeyMaterial64 keyMaterial64, RoundsExponent roundsExponent, LongRunningOperationContext context) { var leftSHA512 = new byte[32]; var rightSHA512 = new byte[32]; Buffer.BlockCopy(keyMaterial64.GetBytes(), 0, leftSHA512, 0, 32); Buffer.BlockCopy(keyMaterial64.GetBytes(), 32, rightSHA512, 0, 32); context.EncryptionProgress.Message = LocalizableStrings.MsgProcessingKey; // Compute the left side on a ThreadPool thread var task = Task.Run(() => BCrypt.CreateHash(iv, leftSHA512, roundsExponent.Value, context)); // Compute the right side after dispatching the work for the right side BCrypt24 rightBCrypt = BCrypt.CreateHash(iv, rightSHA512, roundsExponent.Value, context); // Wait for the left side result task.Wait(context.CancellationToken); // Use the results var combinedHashes = ByteArrays.Concatenate(keyMaterial64.GetBytes(), task.Result.GetBytes(), rightBCrypt.GetBytes()); Debug.Assert(combinedHashes.Length == 64 + 24 + 24); var condensedHash = this._platform.ComputeSHA256(combinedHashes); return(new InputDerivedKey32(condensedHash)); }
public static void ValidateWitnessCommitment(this SlimBlock slimBlock) { var ser = slimBlock.Serialize(); Block block = Block.Load(ser, C.Network.Consensus.ConsensusFactory); if (!ByteArrays.AreAllBytesEqual(ser, block.ToBytes(C.ProtocolVersion))) { throw new InvalidOperationException("Serialization issue."); } // Validation for witness commitments. // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the // coinbase (where 0x0000....0000 is used instead). // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are // multiple, the last one is used. bool fHaveWitness = false; Script commitment = GetWitnessCommitment(block); if (commitment != null) { uint256 hashWitness = BlockWitnessMerkleRoot(block, out bool _); // The malleation check is ignored; as the transaction tree itself // already does not permit it, it is impossible to trigger in the // witness tree. WitScript witness = block.Transactions[0].Inputs[0].WitScript; if ((witness.PushCount != 1) || (witness.Pushes.First().Length != 32)) { throw new InvalidOperationException("ConsensusErrors.BadWitnessNonceSize"); } var hashed = new byte[64]; Buffer.BlockCopy(hashWitness.ToBytes(), 0, hashed, 0, 32); Buffer.BlockCopy(witness.Pushes.First(), 0, hashed, 32, 32); hashWitness = Hashes.DoubleSHA256(hashed); // todo: not nice code to reuse tha variable if (!ByteArrays.AreAllBytesEqual(hashWitness.ToBytes(), commitment.ToBytes(true).Skip(6).ToArray())) { throw new InvalidOperationException("ConsensusErrors.BadWitnessMerkleMatch"); } fHaveWitness = true; } if (!fHaveWitness) { for (int i = 0; i < block.Transactions.Count; i++) { if (block.Transactions[i].HasWitness) { throw new InvalidOperationException("Unexpected witness."); } } } }
public static byte[] GetRandom(int length) { if (length != 32 && length != 64 && length != 20) { throw new ArgumentException(nameof(length)); } using var sha = SHA512.Create(); using var rng = new RNGCryptoServiceProvider(); var rngBytes = new byte[512]; var tickBytes = BitConverter.GetBytes(DateTime.Now.Ticks); var startedTickBytes = BitConverter.GetBytes(Started * Started); var guidBytes = Guid.NewGuid().ToByteArray(); rng.GetBytes(rngBytes); var sources = new[] { startedTickBytes, tickBytes, guidBytes, rngBytes }; byte[] entropy = ByteArrays.Concatenate(rngBytes, startedTickBytes, tickBytes, guidBytes, rngBytes); foreach (var src in sources) { entropy = sha.ComputeHash(ByteArrays.Concatenate(entropy, src)); } var ret = new byte[length]; Buffer.BlockCopy(entropy, 0, ret, 0, length); return(ret); }
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))); }
InputDerivedKey32 CreateDerivedKeyWithSHA256(IV16 iv, KeyMaterial64 keyMaterial64) { var keyMaterial = ByteArrays.Concatenate(iv.GetBytes(), keyMaterial64.GetBytes()); var derivedKey = this._platform.ComputeSHA256(keyMaterial); return(new InputDerivedKey32(derivedKey)); }
public TLSEnvelope TLSServerEncryptRequest(byte[] clearPacket, string recipientId) { byte[] authSecret = GetAuthSecret(recipientId); DynamicSecret dynamicSecret = GetDynamicSecretForEncryption(recipientId); Debug.WriteLine($"{this.ServerId}: TLSEncrypt: DynamicPublicKeyID: {dynamicSecret.DynamicPublicKeyId}, PrivateKeyHint: {dynamicSecret.PrivateKeyHint}."); // Concatenate = 'TLSAuthMode.Combined' byte[] symmetricKeyMaterial64 = ByteArrays.Concatenate(dynamicSecret.DynamicSharedSecret, authSecret); var lro = new LongRunningOperation(progress => { }, () => { }); var clearBytes = new Clearbytes(clearPacket); var sha512PW64 = new KeyMaterial64(symmetricKeyMaterial64); var method = new RoundsExponent(0xff); var encryptResponse = this.ixdsCryptoService.BinaryEncrypt(clearBytes, sha512PW64, method, lro.Context); if (!encryptResponse.IsSuccess) { throw new Exception(encryptResponse.Error); } var encodeResponse = this.ixdsCryptoService.BinaryEncodeXDSSec(encryptResponse.Result, lro.Context); if (!encodeResponse.IsSuccess) { throw new Exception(encodeResponse.Error); } var tlsEnvelope = new TLSEnvelope(dynamicSecret.PrivateKeyHint, dynamicSecret.DynamicPublicKeyId, dynamicSecret.DynamicPublicKey, encodeResponse.Result); return(tlsEnvelope); }
public Response <QualifiedRandom> GetRandom(int randomLenght, byte[] seed = null) { var response = new Response <QualifiedRandom>(); try { EnsurePlatform(); var randomBytes = this._platform.GenerateRandomBytes(randomLenght); randomBytes = this._platform.ComputeSHA512(randomBytes); if (seed != null) { seed = this._platform.ComputeSHA512(seed); var combinedBytes = ByteArrays.Concatenate(randomBytes, seed); randomBytes = this._platform.ComputeSHA512(combinedBytes); } response.Result = new QualifiedRandom { X = randomBytes.Take(randomLenght).ToArray() }; response.SetSuccess(); } catch (Exception e) { response.SetError(e); } return(response); }
static (byte[] seed, string sentence) CreateHdSecrets(byte[] sourceKeyMaterial, int startIndex) { WordList wordList = WordList.English; var usedKeyMaterial = new byte[32]; Buffer.BlockCopy(sourceKeyMaterial, startIndex, usedKeyMaterial, 0, 32); var mnemonic = new Mnemonic(wordList, usedKeyMaterial); if (!mnemonic.IsValidChecksum) { throw new InvalidOperationException("Invalid checksum."); } var seed = mnemonic.DeriveSeed(); seed.Check(64); var sentence = mnemonic.ToString(); CheckNumberOfWords(sentence, WordListLength); var testRecovery = new Mnemonic(sentence, WordList.English); if (testRecovery.ToString() != sentence || !ByteArrays.AreAllBytesEqual(testRecovery.DeriveSeed(), seed)) { throw new InvalidOperationException("This seed cannot be recovered."); } return(seed, sentence); }
public KeyMaterial64 GetInitialE2EDecryptionKey(byte[] dynamicPublicKey) { var dynamicSharedSecret = this._visualCrypt2Service.CalculateAndHashSharedSecret(this._myStaticPrivateKey, dynamicPublicKey); var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]); return(new KeyMaterial64(symmetricKeyMaterial)); }
public static byte[] Build(IList <byte[]> merkleLeaves) { if (merkleLeaves == null || merkleLeaves.Count == 0) { throw new ArgumentOutOfRangeException(nameof(merkleLeaves)); } while (true) { if (merkleLeaves.Count == 1) { return(merkleLeaves[0]); } if (merkleLeaves.Count % 2 > 0) { merkleLeaves.Add(merkleLeaves[merkleLeaves.Count - 1]); // merkleLeaves[^1] last element } var merkleBranches = new List <byte[]>(); for (int i = 0; i < merkleLeaves.Count; i += 2) { var leafBytePair = ByteArrays.Concatenate(merkleLeaves[i], merkleLeaves[i + 1]); var newMerkleBranch = DoubleSha256(leafBytePair); merkleBranches.Add(newMerkleBranch); } merkleLeaves = merkleBranches; } }
public static byte[] Serialize(this SlimBlock slimBlock) { // These buffers will be concatenated to create the full block; List <byte[]> parts = new List <byte[]>(); // Block header parts.Add(slimBlock.SlimBlockHeader.Data); // Number of tx in the block byte[] nTransactions = new byte[1]; // this must be VarInt, todo: use real varint var txCount = slimBlock.IsProofOfStake ? slimBlock.PayloadTransactions.Count + 2 : slimBlock.PayloadTransactions.Count + 1; if (txCount > 252) { throw new InvalidOperationException($"Value of {txCount} needs a real VarInt"); } nTransactions[0] = (byte)txCount; parts.Add(nTransactions); // Transactions var coinbaseTxBytes = slimBlock.CoinbaseTransaction.ToBytes(); parts.Add(coinbaseTxBytes); if (slimBlock.IsProofOfStake) { Debug.Assert(slimBlock.CoinstakeTransaction.IsCoinstake()); var coinstakeTxBytes = slimBlock.CoinstakeTransaction.ToBytes(); parts.Add(coinstakeTxBytes); } foreach (var payloadTransaction in slimBlock.PayloadTransactions) { parts.Add(payloadTransaction.ToBytes()); } // Block signature as VarString. First byte of VarString is length as VarInt if (slimBlock.IsProofOfStake) { // 252 (0xfc) is the max for a 1-byte VarInt, and we assume the signature is not longer Debug.Assert(slimBlock.SignatureBytes.Length <= 252); parts.Add(slimBlock.SignatureBytes); // SignatureBytes is written by the BlockSignature ReadWrite method. This should already be in VarString format. //byte[] signature = new byte[1 + slimBlock.SignatureBytes.Length]; //signature[0] = (byte)slimBlock.SignatureBytes.Length; //Buffer.BlockCopy(slimBlock.SignatureBytes, 0, signature, 1, slimBlock.SignatureBytes.Length); //parts.Add(signature); } else { parts.Add(slimBlock.SignatureBytes); } var blockBytes = ByteArrays.Concatenate(parts.ToArray()); return(blockBytes); }
public KeyMaterial64 GetInitialE2EDecryptionKey(byte[] dynamicPublicKey) { EnsureInitialized(); var dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(this.parameters.OwnStaticPrivateKey, dynamicPublicKey); var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]); return(new KeyMaterial64(symmetricKeyMaterial)); }
public static byte[] SerializeTo80Bytes(this SlimBlockHeader slimBlockHeader) { var versionBytes = BitConverter.GetBytes(slimBlockHeader.Version); var timeBytes = BitConverter.GetBytes(slimBlockHeader.Timestamp); var bitsBytes = BitConverter.GetBytes(slimBlockHeader.Bits); var nonceBytes = BitConverter.GetBytes(slimBlockHeader.Nonce); return(ByteArrays.Concatenate(versionBytes, slimBlockHeader.HashPrevBlock, slimBlockHeader.MerkleRoot, timeBytes, bitsBytes, nonceBytes)); }
public async Task <string> AddIdentity(XIdentity identity, Action <string, byte[]> initTlsUser) { await SemaphoreSlim.WaitAsync(); try { if (identity == null) { throw new ArgumentNullException(nameof(XIdentity)); } if (identity.Id == null) { throw new ArgumentNullException(nameof(XIdentity.Id)); } if (identity.PublicIdentityKey == null) { throw new ArgumentNullException(nameof(XIdentity.PublicIdentityKey)); } if (ChatId.GenerateChatId(identity.PublicIdentityKey) != identity.Id) { throw new Exception("Id and public key are unrelated."); } identity.ContactState = ContactState.Valid; XIdentity existing = await this.identitiesRepository.Get(identity.Id); if (existing == null) { await this.identitiesRepository.Add(identity); } else { if (!ByteArrays.AreAllBytesEqual(identity.PublicIdentityKey, existing.PublicIdentityKey)) { throw new Exception($"Different new PublicKey for {identity.Id}. Ignoring request!"); } existing.LastSeenUTC = DateTime.UtcNow; await this.identitiesRepository.Update(existing); } if (initTlsUser != null) // TLS { initTlsUser(identity.Id, identity.PublicIdentityKey); } this.logger.LogInformation($"Identity {identity.Id} was published.", nameof(MessageNodeRepository)); return(identity.Id); } finally { SemaphoreSlim.Release(); } }
public static byte[] AddCheckSum(byte[] data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } byte[] checkSum = GetCheckSum(data); byte[] dataWithCheckSum = ByteArrays.Concatenate(data, checkSum); Debug.Assert(data.Length + CheckSumSizeInBytes == dataWithCheckSum.Length); return(dataWithCheckSum); }
public static VisualCryptText CreateVisualCryptText(CipherV2 cipherV2) { Guard.NotNull(cipherV2); var visualCryptTextV2Bytes = ByteArrays.Concatenate( // len Sum(len) Start Index new[] { CipherV2.Version }, // 1 1 0 new[] { cipherV2.RoundsExponent.Value }, // 1 2 1 new[] { cipherV2.PlaintextPadding.Value }, // 1 3 2 cipherV2.IV16.GetBytes(), // 16 19 3 cipherV2.MACCipher16.GetBytes(), // 16 35 19 cipherV2.RandomKeyCipher32.GetBytes(), // 32 67 35 cipherV2.MessageCipher.GetBytes() // len 67 + len 67 ); if (visualCryptTextV2Bytes.Length < HeaderLenght) { throw new Exception("Data cannot be shorter than the required header."); } var visualCryptTextV2Base64 = Base64Encoder.EncodeDataToBase64CharArray(visualCryptTextV2Bytes); var sb = new StringBuilder(); const int breakAfter = 74; var charsInLine = 0; foreach (var c in VisualCryptSlashText) { sb.Append(c); if (++charsInLine != breakAfter) { continue; } sb.Append(new[] { '\r', '\n' }); charsInLine = 0; } foreach (var c in visualCryptTextV2Base64) { sb.Append(c == '/' ? '$' : c); if (++charsInLine != breakAfter) { continue; } sb.Append(new[] { '\r', '\n' }); charsInLine = 0; } return(new VisualCryptText(sb.ToString())); }
public static bool EqualDeep(this XMessage m1, XMessage m2) { if (ReferenceEquals(m1, m2)) { return(true); } if (m1 == null || m2 == null) { return(false); } if (m1.Id != m2.Id) { return(false); } if (m1.DynamicPublicKeyId != m2.DynamicPublicKeyId) { return(false); } if (m1.PrivateKeyHint != m2.PrivateKeyHint) { return(false); } if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.DynamicPublicKey, m2.DynamicPublicKey)) { return(false); } if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.MetaCipher, m2.MetaCipher)) { return(false); } if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.TextCipher, m2.TextCipher)) { return(false); } if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.ImageCipher, m2.ImageCipher)) { return(false); } return(true); }
public static byte[] CreateBinary(CipherV2 cipherV2) { Guard.NotNull(cipherV2); var visualCrypt2Bytes = ByteArrays.Concatenate( // len Sum(len) Start Index new[] { CipherV2.Version }, // 1 1 0 new[] { cipherV2.RoundsExponent.Value }, // 1 2 1 new[] { cipherV2.PlaintextPadding.Value }, // 1 3 2 cipherV2.IV16.GetBytes(), // 16 19 3 cipherV2.MACCipher16.GetBytes(), // 16 35 19 cipherV2.RandomKeyCipher32.GetBytes(), // 32 67 35 cipherV2.MessageCipher.GetBytes() // len 67 + len 67 ); return(visualCrypt2Bytes); }
public TLSEnvelope TLSServerEncryptRequestAnonymous(byte[] clearPacket, byte[] dynamicPublicKey, long dynamicPublicKeyID) { byte[] authSecret = new byte[32]; // we cannot create an authSecret based on the client's public key when we don't know who the client is. // we use the dynamicPublicKey and the server private key. var dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(this._serverPrivateKey, dynamicPublicKey); // the hint to the clients privk for pubkey he sent long privateKeyHint = dynamicPublicKeyID; // and now we create a dynamic public key, just to fit the protocol, but not intended for use. var random = this.ixdsCryptoService.GetRandom(32).Result.X; var throwAwayPubKey = this.ixdsCryptoService.GenerateCurve25519KeyPairExact(random).Result.PublicKey; // and a fake id long fakeDynamicPublicKeyID = 9999; // use a realistic value, not 9999! Debug.WriteLine($"{this.ServerId}: TLSServerEncryptRequestAnonymous: FakeDynamicPublicKeyID: {fakeDynamicPublicKeyID}, PrivateKeyHint: {privateKeyHint}."); // Concatenate = 'TLSAuthMode.Dynamic' - THIS is anothe case! byte[] symmetricKeyMaterial64 = ByteArrays.Concatenate(dynamicSharedSecret, authSecret); // same as normally var lro = new LongRunningOperation(progress => { }, () => { }); var clearBytes = new Clearbytes(clearPacket); var sha512PW64 = new KeyMaterial64(symmetricKeyMaterial64); var method = new RoundsExponent(0xff); var encryptResponse = this.ixdsCryptoService.BinaryEncrypt(clearBytes, sha512PW64, method, lro.Context); if (!encryptResponse.IsSuccess) { throw new Exception(encryptResponse.Error); } var encodeResponse = this.ixdsCryptoService.BinaryEncodeXDSSec(encryptResponse.Result, lro.Context); if (!encodeResponse.IsSuccess) { throw new Exception(encodeResponse.Error); } var tlsEnvelope = new TLSEnvelope(privateKeyHint, fakeDynamicPublicKeyID, throwAwayPubKey, encodeResponse.Result); return(tlsEnvelope); }
/// <summary> /// Helper to append any number of byte arrays /// </summary> /// <param name="ByteArrays">The byte arrays to append</param> /// <returns>A joined byte array</returns> public static byte[] JoinByteArrays(params byte[][] ByteArrays) { // Count the total number of bytes int TotalBytes = ByteArrays.Sum(ByteArray => ByteArray.Length); // Copy all the bytes into the new array byte[] JoinedArray = new byte[TotalBytes]; int NumBytesCopied = 0; foreach (var ByteArray in ByteArrays) { ByteArray.CopyTo(JoinedArray, NumBytesCopied); NumBytesCopied += ByteArray.Length; } return(JoinedArray); }
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)); }
public XDSPubKeyAddress CreateHdAddress(byte[] walletSeed, int accountIndex, int changePath, int addressIndex) { var keyPath = new KeyPath($"m/44'/{XDSPrincipal.XDSCoinType}'/{accountIndex}'/{changePath}/{addressIndex}"); var seedExtKey = new ExtKey(walletSeed); var derivedKey = seedExtKey.Derive(keyPath); CompressedPubKey compressedPubKey = derivedKey.PrivateKey.CompressedPubKey; var hash = compressedPubKey.GetHash160(); var bech = new Bech32Encoder("xds"); var address = bech.Encode(0, hash); return(new XDSPubKeyAddress { PrivateKey = derivedKey.PrivateKey.RawBytes, PublicKey = derivedKey.PrivateKey.CompressedPubKey.ToBytes(), Hash = hash, KeyPath = keyPath.ToString(), Address = address, ScriptPubKey = ByteArrays.Concatenate(new byte[1], hash) }); }
MAC16 CreateMAC(CipherV2 cipherV2, LongRunningOperationContext context) { // Create the MAC only for items that, while decrypting, have not been used up to this point but do include the version. var securables = ByteArrays.Concatenate(cipherV2.MessageCipher.GetBytes(), new[] { cipherV2.PlaintextPadding.Value }, new[] { CipherV2.Version }); // See e.g. http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf Chapter 7 for hash truncation. byte[] truncatedHash = new byte[16]; if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds) { var sha256 = this._platform.ComputeSHA256(securables); Buffer.BlockCopy(sha256, 0, truncatedHash, 0, 16); } else { context.EncryptionProgress.Message = LocalizableStrings.MsgCalculatingMAC; var bCrypt24 = BCrypt.CreateHash(cipherV2.IV16, securables, cipherV2.RoundsExponent.Value, context); Buffer.BlockCopy(bCrypt24.GetBytes(), 0, truncatedHash, 0, 16); } return(new MAC16(truncatedHash)); }
public static byte[] CreateBinaryDH(CipherV2 cipherV2) { Guard.NotNull(cipherV2); var randomKeyCipher32Placeholder = new byte[32]; randomKeyCipher32Placeholder[0] = 1; // insert a bit so that the SecureBytes validation does not throw. cipherV2.RandomKeyCipher32 = new RandomKeyCipher32(randomKeyCipher32Placeholder); var visualCrypt2Bytes = ByteArrays.Concatenate( // len Sum(len) Start Index new[] { CipherV2.Version }, // 1 1 0 new[] { cipherV2.RoundsExponent.Value }, // 1 2 1 new[] { cipherV2.PlaintextPadding.Value }, // 1 3 2 cipherV2.IV16.GetBytes(), // 16 19 3 cipherV2.MACCipher16.GetBytes(), // 16 35 19 cipherV2.RandomKeyCipher32.GetBytes(), // 32 67 35 cipherV2.MessageCipher.GetBytes() // len 67 + len 67 ); return(visualCrypt2Bytes); }
public async Task <TLSEnvelope> EncryptRequest(byte[] clearPacket) { await this._publicMemberLock.WaitAsync(); try { DynamicSecret dynamicSecret = GetDynamicSecretForEncryption(); Debug.WriteLine( $"{this.MyId}: TLSEncrypt: DynamicPublicKeyID: {dynamicSecret.DynamicPublicKeyId}, PrivateKeyHint: {dynamicSecret.PrivateKeyHint}."); byte[] authSecret = this._server.AuthSecret; var securable = ByteArrays.Concatenate(authSecret, this._myIdBytes, clearPacket); var symmetricKeyMaterial64 = ByteArrays.Concatenate(dynamicSecret.DynamicSharedSecret, new byte[32]); var lro = new LongRunningOperation(progress => { }, () => { }); var clearBytes = new Clearbytes(securable); var keyMaterial64 = new KeyMaterial64(symmetricKeyMaterial64); var method = new RoundsExponent(0xff); var encryptResponse = this._visualCrypt2Service.BinaryEncrypt(clearBytes, keyMaterial64, method, lro.Context); if (!encryptResponse.IsSuccess) { throw new Exception(encryptResponse.Error); } var encodeResponse = this._visualCrypt2Service.BinaryEncodeVisualCrypt(encryptResponse.Result, lro.Context); if (!encodeResponse.IsSuccess) { throw new Exception(encodeResponse.Error); } return(new TLSEnvelope(dynamicSecret.PrivateKeyHint, dynamicSecret.DynamicPublicKeyId, dynamicSecret.DynamicPublicKey, encodeResponse.Result)); } finally { this._publicMemberLock.Release(); } }
public async Task <GetE2EDecryptionKeyResult> GetEndToEndDecryptionKeyAsync(string senderId, byte[] dynamicPublicKey, long privateKeyHint) { EnsureInitialized(); var user = await GetCheckedUserAsync(senderId); var result = new GetE2EDecryptionKeyResult(); byte[] dynamicPrivateKeyOrStaticPrivateKey; if (privateKeyHint == 0) { dynamicPrivateKeyOrStaticPrivateKey = this.parameters.OwnStaticPrivateKey; result.E2EDecryptionKeyType = E2EDecryptionKeyType.MyStaticPrivateKey; } else { if (user.DynamicPrivateDecryptionKeys.TryGetValue(privateKeyHint, out dynamicPrivateKeyOrStaticPrivateKey)) { result.E2EDecryptionKeyType = E2EDecryptionKeyType.DynamicPrivateKey; } else { // Possible reasons: // - we did not look into the right user's ratchet while trying to determine the sender // - The ratchets are not in sync, resend/new dynamic key exchange required result.E2EDecryptionKeyType = E2EDecryptionKeyType.UnavailableDynamicPrivateKey; return(result); } } var dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(dynamicPrivateKeyOrStaticPrivateKey, dynamicPublicKey); var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, user.AuthSecret); result.E2EDecryptionKeyMaterial = new KeyMaterial64(symmetricKeyMaterial); return(result); }
public static bool EqualDeep(this XIdentity id1, XIdentity id2) { if (ReferenceEquals(id1, id2)) { return(true); } if (id1 == null || id2 == null) { return(false); } if (id1.Id != id2.Id) { return(false); } if (!ByteArrays.AreAllBytesEqualOrBothNull(id1.PublicIdentityKey, id2.PublicIdentityKey)) { return(false); } return(true); }
public async Task <TLSRequest> DecryptRequest(TLSEnvelope tlsEnvelope) { Guard.NotNull(tlsEnvelope); await this._publicMemberLock.WaitAsync(); try { var ar = new TLSRequest(); byte[] clientDynamicPrivateKey; this._server.DynamicPrivateDecryptionKeys.TryGetValue(tlsEnvelope.PrivateKeyHint, out clientDynamicPrivateKey); if (clientDynamicPrivateKey == null) { throw new Exception( "This should rarely happen. It means, the server has hinted me to private Key I no longer have."); } RemovePreviousKeys(tlsEnvelope.PrivateKeyHint); var dynamicSharedSecret = this._visualCrypt2Service.CalculateAndHashSharedSecret(clientDynamicPrivateKey, tlsEnvelope.DynamicPublicKey); Debug.WriteLine($"{this.MyId}: TLSDecrypt: PrivateKeyHint: {tlsEnvelope.PrivateKeyHint}"); // TLSAuthMode Combined byte[] authSecretBytes = this._server.AuthSecret; var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, authSecretBytes); // End TLSAuthMode Combined // TODO: make LRO optional! var lro = new LongRunningOperation(progress => { }, () => { }); var cipherV2 = VisualCrypt2Formatter.DissectVisualCryptBytes(tlsEnvelope.EncipheredPayload, lro.Context); var decryptResponse = this._visualCrypt2Service.BinaryDecrypt(cipherV2, new KeyMaterial64(symmetricKeyMaterial), lro.Context); var isIncomingKeyUnusable = false; if (!decryptResponse.IsSuccess) { // has the server just lost the dynamic keys? this._server.DynamicPrivateDecryptionKeys.TryGetValue(tlsEnvelope.PrivateKeyHint, out clientDynamicPrivateKey); if (clientDynamicPrivateKey == null) { throw new Exception( "This should rarely happen. It means, the server has hinted me to private Key I no longer have."); } dynamicSharedSecret = this._visualCrypt2Service.CalculateAndHashSharedSecret(clientDynamicPrivateKey, this._server.StaticPublicKey); authSecretBytes = new byte[32]; symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, authSecretBytes); var decryptResponse2 = this._visualCrypt2Service.BinaryDecrypt(cipherV2, new KeyMaterial64(symmetricKeyMaterial), lro.Context); if (!decryptResponse2.IsSuccess) { throw new Exception("Decryption failed in all ways!"); } decryptResponse = decryptResponse2; Debug.WriteLine("Decryption succeded in Anonymous mode."); DoReset(); isIncomingKeyUnusable = true; } byte[] tlsDecryptedRequest = decryptResponse.Result.GetBytes(); ar.IsAuthenticated = true; ar.UserId = this._server.UserId; ar.CommandData = tlsDecryptedRequest; if (!isIncomingKeyUnusable) { Guard.NotNull(tlsEnvelope.DynamicPublicKey); if (tlsEnvelope.DynamicPublicKeyId == 0) { throw new ArgumentException("A dynamic public key must never have an ID of 0."); } this._server.LatestDynamicPublicKey = tlsEnvelope.DynamicPublicKey; this._server.LatestDynamicPublicKeyId = tlsEnvelope.DynamicPublicKeyId; } return(ar); } finally { this._publicMemberLock.Release(); } }
public TLSRequest TLSServerDecryptRequest(TLSEnvelope tlsEnvelope) { Guard.NotNull(tlsEnvelope); var ar = new TLSRequest(); byte[] serverPrivateKey; // static or dynamic if (tlsEnvelope.PrivateKeyHint == 0) // The client has used server's static public key to encrypt... { serverPrivateKey = this._serverPrivateKey; } // ... so we simple use the servers static private key else // tlsEnvelope.PrivateKeyHint is the number the server generated earlier { serverPrivateKey = GetServerDynamicPrivateKeyForUserByPrivateKeyHint(tlsEnvelope.PrivateKeyHint); } if (serverPrivateKey == null) { // we lost our dynamic private key b/c of a disk failure and we don't know who sent us what :-( return(null); } byte[] dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(serverPrivateKey, tlsEnvelope.DynamicPublicKey); Debug.WriteLine($"{this.ServerId}: TLSDecrypt: PrivateKeyHint: {tlsEnvelope.PrivateKeyHint}"); // 'TLSAuthMode.Separate' byte[] symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]); // no auth, we dont know who the user is // End 'TLSAuthMode.Separate' var lro = new LongRunningOperation(progress => { }, () => { }); var cipherV2 = XDSSecFormatter.DissectXDSSecBytes(tlsEnvelope.EncipheredPayload, lro.Context); var decryptResponse = this.ixdsCryptoService.BinaryDecrypt(cipherV2, new KeyMaterial64(symmetricKeyMaterial), lro.Context); if (!decryptResponse.IsSuccess) { throw new Exception(decryptResponse.Error); } byte[] tlsDecryptedRequest = decryptResponse.Result.GetBytes(); // 'TLSAuthMode.Separate' var authSecretFromMessage = new byte[32]; Buffer.BlockCopy(tlsDecryptedRequest, 0, authSecretFromMessage, 0, authSecretFromMessage.Length); // must be set even if the user is not authenticated for the case where a new identity is published right now. ar.UserId = Encoding.UTF8.GetString(tlsDecryptedRequest, 32, 10); // try authenticate var authSecret = GetAuthSecret(ar.UserId); if (authSecret != null) { if (ByteArrays.AreAllBytesEqual(authSecretFromMessage, authSecret)) { ar.IsAuthenticated = true; } } else { ar.IsAuthenticated = false; } var requestData = new byte[tlsDecryptedRequest.Length - 32 - 10]; Buffer.BlockCopy(tlsDecryptedRequest, 32 + 10, requestData, 0, requestData.Length); ar.CommandData = requestData; // End 'TLSAuthMode.Separate' if (ar.IsAuthenticated) { SaveIncomingDynamicPublicKey(ar.UserId, tlsEnvelope.DynamicPublicKey, tlsEnvelope.DynamicPublicKeyId); } return(ar); }
/// <summary> /// Fully decrypts a message of all types, both control and content messages (including images), if an end-to-en-encryption key can be determined. /// </summary> async Task <Message> TryDecryptMessageToFindSenderEnryptDecryptionKeyAndSaveIt(XMessage xmessage) { try { IReadOnlyList <Identity> contacts = await this.repo.GetAllContacts(); var decryptedMessage = new Message { // XMessage fields RecipientId = "1", // drop my Id TextCipher = xmessage.TextCipher, ImageCipher = xmessage.ImageCipher, DynamicPublicKey = xmessage.DynamicPublicKey, DynamicPublicKeyId = xmessage.DynamicPublicKeyId, PrivateKeyHint = xmessage.PrivateKeyHint, LocalMessageState = LocalMessageState.JustReceived, SendMessageState = SendMessageState.None, Side = MessageSide.You, // This is what we try to decrypt here MessageType = MessageType.None, SenderId = null, }; Response <CipherV2> decodeMetaResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.MetaCipher, null); if (!decodeMetaResponse.IsSuccess) { return(null); // garbled, no chance, ignore! } foreach (Identity identity in contacts) { GetE2EDecryptionKeyResult getE2EDecryptionKeyResult = await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(identity.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint); if (getE2EDecryptionKeyResult.E2EDecryptionKeyType == E2EDecryptionKeyType.UnavailableDynamicPrivateKey) { continue; // there was a privateKeyHint != 0 in the message, but the dynamic private key was not in the ratchet for user in the loop } KeyMaterial64 keyMaterial64 = getE2EDecryptionKeyResult.E2EDecryptionKeyMaterial; var decryptMetaResponse = this.ixdsCryptoService.BinaryDecrypt(decodeMetaResponse.Result, keyMaterial64, null); if (!decryptMetaResponse.IsSuccess) { continue; } await this.e2eRatchet.SaveIncomingDynamicPublicKeyOnSuccessfulDecryptionAsync(identity.Id, xmessage.DynamicPublicKey, xmessage.DynamicPublicKeyId); XMessageMetaData metadata = decryptMetaResponse.Result.GetBytes().DeserializeMessageMetadata(); decryptedMessage.SenderId = identity.Id; decryptedMessage.MessageType = metadata.MessageType; decryptedMessage.SenderLocalMessageId = metadata.SenderLocalMessageId.ToString(); if (decryptedMessage.MessageType.IsReceipt()) { return(decryptedMessage); } if (decryptedMessage.MessageType.IsContent()) { if (decryptedMessage.MessageType == MessageType.Text || decryptedMessage.MessageType == MessageType.TextAndMedia || decryptedMessage.MessageType == MessageType.File) { // if the message has text, decrypt all text var decodeTextResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.TextCipher, null); if (!decodeTextResponse.IsSuccess) { return(null); // something is wrong, should have worked } var decrpytTextResponse = this.ixdsCryptoService.Decrypt(decodeTextResponse.Result, keyMaterial64, null); if (!decrpytTextResponse.IsSuccess) { return(null); // something is wrong, should have worked } decryptedMessage.ThreadText = decrpytTextResponse.Result.Text; } if (decryptedMessage.MessageType == MessageType.Media || decryptedMessage.MessageType == MessageType.TextAndMedia || decryptedMessage.MessageType == MessageType.File) { // if the message has image content, decrypt the image content var decodeImageResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.ImageCipher, null); if (!decodeImageResponse.IsSuccess) { return(null); // something is wrong, should have worked } var decrpytImageResponse = this.ixdsCryptoService.BinaryDecrypt(decodeImageResponse.Result, keyMaterial64, null); if (!decrpytImageResponse.IsSuccess) { return(null); // something is wrong, should have worked } decryptedMessage.ThreadMedia = decrpytImageResponse.Result.GetBytes(); } decryptedMessage.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial64.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey()); decryptedMessage.LocalMessageState = LocalMessageState.Integrated; await this.repo.AddMessage(decryptedMessage); } else { throw new Exception($"Invalid MessageType {decryptedMessage.MessageType}"); } return(decryptedMessage); } // foreach // If we are here, assume it's a new contact's message or RESENT message: CipherV2 encryptedMetaData = decodeMetaResponse.Result; KeyMaterial64 initialKey = this.e2eRatchet.GetInitialE2EDecryptionKey(xmessage.DynamicPublicKey); var decryptInitialMetaResponse = this.ixdsCryptoService.BinaryDecrypt(encryptedMetaData, initialKey, null); if (decryptInitialMetaResponse.IsSuccess) { XMessageMetaData initialMessageMetadata = decryptInitialMetaResponse.Result.GetBytes().DeserializeMessageMetadata(); var incomingPublicKey = initialMessageMetadata.SenderPublicKey; // If we received several resent messages, the first from that incoming contact has already produced that contact: var contact = contacts.SingleOrDefault(c => ByteArrays.AreAllBytesEqual(c.StaticPublicKey, incomingPublicKey)); bool isIncomingContact = false; if (contact == null) { // Create new contact and save it to make the ratchet work normally isIncomingContact = true; var date = DateTime.UtcNow; var incomingContact = new Identity { Id = Guid.NewGuid().ToString(), StaticPublicKey = incomingPublicKey, ContactState = ContactState.Valid, Name = "Anonymous", FirstSeenUtc = date, LastSeenUtc = date }; await this.repo.AddContact(incomingContact); contact = incomingContact; } await this.e2eRatchet.SaveIncomingDynamicPublicKeyOnSuccessfulDecryptionAsync(contact.Id, xmessage.DynamicPublicKey, xmessage.DynamicPublicKeyId); // add metadata to message decryptedMessage.SenderId = contact.Id; decryptedMessage.MessageType = initialMessageMetadata.MessageType; decryptedMessage.SenderLocalMessageId = initialMessageMetadata.SenderLocalMessageId.ToString(); if (decryptedMessage.MessageType.IsContent()) { var success = true; GetE2EDecryptionKeyResult getE2EDecryptionKeyResult = await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(contact.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint); KeyMaterial64 keyMaterial64 = getE2EDecryptionKeyResult.E2EDecryptionKeyMaterial; await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(contact.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint); if (decryptedMessage.MessageType == MessageType.Text || decryptedMessage.MessageType == MessageType.TextAndMedia) { // if the message has text, decrypt all text var decodeTextResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.TextCipher, null); if (!decodeTextResponse.IsSuccess) { success = false; // something is wrong, should have worked } else { var decrpytTextResponse = this.ixdsCryptoService.Decrypt(decodeTextResponse.Result, keyMaterial64, null); if (!decrpytTextResponse.IsSuccess) { success = false; // something is wrong, should have worked } else { decryptedMessage.ThreadText = decrpytTextResponse.Result.Text; } } } if (decryptedMessage.MessageType == MessageType.Media || decryptedMessage.MessageType == MessageType.TextAndMedia) { // if the message has image content, decrypt the image content var decodeImageResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.ImageCipher, null); if (!decodeImageResponse.IsSuccess) { success = false; // something is wrong, should have worked } else { var decrpytImageResponse = this.ixdsCryptoService.BinaryDecrypt(decodeImageResponse.Result, keyMaterial64, null); if (!decrpytImageResponse.IsSuccess) { success = false; // something is wrong, should have worked } else { decryptedMessage.ThreadMedia = decrpytImageResponse.Result.GetBytes(); } } } if (success) { decryptedMessage.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial64.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey()); decryptedMessage.LocalMessageState = LocalMessageState.Integrated; await this.contactListManager.ChatWorker_ContactUpdateReceived(null, contact.Id); await this.repo.AddMessage(decryptedMessage); return(decryptedMessage); } else { if (isIncomingContact) { await this.repo.DeleteContacts(new[] { contact.Id }); // delete the just incoming contact if the was an error anyway. } return(null); } } else { return(null); } } // nope, resend request (I hope we don't loop here!) string networkPayloadHash = NetworkPayloadHash.ComputeAsGuidString(xmessage.SerializedPayload); if (!this._resendsRequested.Contains(networkPayloadHash)) { var response = await this.chatClient.UploadResendRequest(new XResendRequest { Id = networkPayloadHash, RecipientId = null }); if (response.IsSuccess) { this._resendsRequested.Add(networkPayloadHash); } } } catch (Exception e) { this.logger.LogError(e.Message); } return(null); }