/// <param name="receivedFromUser">comes from local contact book</param> internal static InviteSessionDescription Decrypt_Verify(ICryptoLibrary cryptoLibrary, byte[] encryptedSdData, InviteRequestPacket req, InviteAck1Packet ack1, bool ack1SdIsReady, InviteSession session, UserId receivedFromUser, DateTime localTimeNowUtc ) { #region key, iv PacketProcedures.CreateBinaryWriter(out var ms2, out var w2); req.GetSharedSignedFields(w2); ack1.GetSharedSignedFields(w2, ack1SdIsReady); var iv = cryptoLibrary.GetHashSHA256(ms2.ToArray()).Take(16).ToArray(); ms2.Write(session.SharedInviteAckDhSecret, 0, session.SharedInviteAckDhSecret.Length); var aesKey = cryptoLibrary.GetHashSHA256(ms2.ToArray()); // here SHA256 is used as KDF, together with common fields from packets, including both ECDH public keys and timestamp #endregion // decrypt var plainTextSdData = new byte[encryptedSdData.Length]; cryptoLibrary.ProcessAesCbcBlocks(false, aesKey, iv, encryptedSdData, plainTextSdData); var r = new InviteSessionDescription(); var reader = PacketProcedures.CreateBinaryReader(plainTextSdData, 0); r.Flags = reader.ReadByte(); if ((r.Flags & FlagsMask_MustBeZero) != 0) { throw new NotImplementedException(); } r.UserCertificate = UserCertificate.Decode_AssertIsValidNow(reader, cryptoLibrary, receivedFromUser, localTimeNowUtc); r.DirectChannelEndPoint = PacketProcedures.DecodeIPEndPoint(reader); r.DirectChannelToken32 = DirectChannelToken32.Decode(reader); r.SessionType = (SessionType)reader.ReadByte(); r.UserCertificateSignature = UserCertificateSignature.DecodeAndVerify(reader, cryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, ack1SdIsReady); r.WriteSignedFields(w); }, r.UserCertificate); return(r); }
/// <summary> /// initializes parameters to transmit direct (p2p) packets form requester A to neighbor N /// </summary> public void Decrypt_ack1_ToResponderTxParametersEncrypted_AtRequester_DeriveSharedDhSecret(Logger logger, RegisterRequestPacket req, RegisterAck1Packet ack1) { SharedDhSecret = _engine.CryptoLibrary.DeriveEcdh25519SharedSecret(LocalEcdhe25519PrivateKey, ack1.ResponderEcdhePublicKey.Ecdh25519PublicKey); #region iv, key PacketProcedures.CreateBinaryWriter(out var ms, out var writer); req.GetSharedSignedFields(writer, true); ack1.GetSharedSignedFields(writer, false, false); var iv = _engine.CryptoLibrary.GetHashSHA256(ms.ToArray()).Take(16).ToArray(); ms.Write(SharedDhSecret, 0, SharedDhSecret.Length); var aesKey = _engine.CryptoLibrary.GetHashSHA256(ms.ToArray()); // here SHA256 is used as KDF, together with common fields from packets, including both ECDH public keys and timestamp #endregion var toNeighborTxParametersDecrypted = new byte[ack1.ToResponderTxParametersEncrypted.Length]; _engine.CryptoLibrary.ProcessAesCbcBlocks(false, aesKey, iv, ack1.ToResponderTxParametersEncrypted, toNeighborTxParametersDecrypted); // parse toNeighborTxParametersDecrypted using (var reader = new BinaryReader(new MemoryStream(toNeighborTxParametersDecrypted))) { RemoteEndpoint = PacketProcedures.DecodeIPEndPoint(reader); RemoteNeighborToken32 = NeighborToken32.Decode(reader); var magic16 = reader.ReadUInt16(); if (magic16 != Magic16_responderToRequester) { throw new BrokenCipherException(); } } if (logger.WriteToLog_detail_enabled) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"decrypted remote responder endpoint={RemoteEndpoint}, remoteNeighborToken={RemoteNeighborToken32} from ACK1"); } } }
/// <summary> /// when REQ is received from neighbor, verifies senderHMAC and NeighborToken32 /// </summary> /// <param name="receivedFromNeighborNullable">is NULL when decoding REQ from A at EP</param> public static RegisterRequestPacket Decode_OptionallyVerifyNeighborHMAC(byte[] udpData, ConnectionToNeighbor receivedFromNeighborNullable, int numberOfDimensions) { var r = new RegisterRequestPacket(); r.DecodedUdpPayloadData = udpData; var reader = PacketProcedures.CreateBinaryReader(udpData, 1); var flags = reader.ReadByte(); if ((flags & FlagsMask_MustBeZero) != 0) { throw new NotImplementedException(); } if ((flags & Flag_AtoEP) == 0) { if (receivedFromNeighborNullable == null) { throw new UnmatchedFieldsException(); } r.NeighborToken32 = NeighborToken32.Decode(reader); if (receivedFromNeighborNullable.LocalNeighborToken32.Equals(r.NeighborToken32) == false) { throw new UnmatchedFieldsException(); } } else { if (receivedFromNeighborNullable != null) { throw new UnmatchedFieldsException(); } } r.RequesterRegistrationId = RegistrationId.Decode(reader); r.RequesterNeighborsBusySectorIds = reader.ReadUInt16(); r.RequesterEcdhePublicKey = EcdhPublicKey.Decode(reader); r.ReqTimestamp64 = reader.ReadInt64(); r.MinimalDistanceToNeighbor = reader.ReadUInt32(); if ((flags & Flag_DirectionVectorExists) != 0) { r.DirectionVectorNullable = new sbyte[numberOfDimensions]; for (int i = 0; i < numberOfDimensions; i++) { r.DirectionVectorNullable[i] = reader.ReadSByte(); } } r.EpEndpoint = PacketProcedures.DecodeIPEndPoint(reader); r.RequesterSignature = RegistrationSignature.Decode(reader); if ((flags & Flag_AtoEP) != 0) { r.ProofOfWork2 = reader.ReadBytes(64); } r.NumberOfHopsRemaining = reader.ReadByte(); r.NumberOfRandomHopsRemaining = reader.ReadByte(); r.ReqP2pSeq16 = RequestP2pSequenceNumber16.Decode(reader); if ((flags & Flag_AtoEP) == 0) { r.NeighborHMAC = HMAC.Decode(reader); if (r.NeighborHMAC.Equals(receivedFromNeighborNullable.GetNeighborHMAC(r.GetSignedFieldsForNeighborHMAC)) == false) { throw new BadSignatureException("invalid REGISTER REQ NeighborHMAC 1573"); } } return(r); }
/// <summary> /// decodes the packet, decrypts ToNeighborTxParametersEncrypted, verifies NeighborSignature, verifies match to register REQ /// </summary> /// <param name="newConnectionToNeighborAtRequesterNullable">if not null (at requester) - this procedure verifies ResponderSignature</param> /// <param name="reader">is positioned after first byte = packet type</param> public static RegisterAck1Packet DecodeAndOptionallyVerify(Logger logger, byte[] ack1UdpData, RegisterRequestPacket reqNullable, ConnectionToNeighbor newConnectionToNeighborAtRequesterNullable) { var reader = PacketProcedures.CreateBinaryReader(ack1UdpData, 1); var ack1 = new RegisterAck1Packet(); ack1.DecodedUdpPayloadData = ack1UdpData; ack1.Flags = reader.ReadByte(); if ((ack1.Flags & Flag_EPtoA) == 0) { ack1.NeighborToken32 = NeighborToken32.Decode(reader); } if ((ack1.Flags & FlagsMask_MustBeZero) != 0) { throw new NotImplementedException(); } ack1.RequesterRegistrationId = RegistrationId.Decode(reader); ack1.ReqTimestamp64 = reader.ReadInt64(); ack1.ResponderEcdhePublicKey = EcdhPublicKey.Decode(reader); ack1.ToResponderTxParametersEncrypted = reader.ReadBytes(ToResponderTxParametersEncryptedLength); ack1.ResponderRegistrationId = RegistrationId.Decode(reader); if (newConnectionToNeighborAtRequesterNullable != null) { if (reqNullable == null) { throw new ArgumentException(); } ack1.ResponderSignature = RegistrationSignature.DecodeAndVerify( reader, newConnectionToNeighborAtRequesterNullable.Engine.CryptoLibrary, w => { reqNullable.GetSharedSignedFields(w, true); ack1.GetSharedSignedFields(w, false, true); }, ack1.ResponderRegistrationId); } else { // at proxy we don't verify responder's signature, to avoid high spending of resources ack1.ResponderSignature = RegistrationSignature.Decode(reader); } if (reqNullable != null) { ack1.AssertMatchToRegisterReq(reqNullable); if (newConnectionToNeighborAtRequesterNullable != null) { newConnectionToNeighborAtRequesterNullable.Decrypt_ack1_ToResponderTxParametersEncrypted_AtRequester_DeriveSharedDhSecret(logger, reqNullable, ack1); } } if ((ack1.Flags & Flag_EPtoA) != 0) { ack1.RequesterEndpoint = PacketProcedures.DecodeIPEndPoint(reader); } else { ack1.ReqP2pSeq16 = RequestP2pSequenceNumber16.Decode(reader); ack1.NeighborHMAC = HMAC.Decode(reader); } return(ack1); }