/// <summary> /// Validates ActivityInformation structure by itself. This function does not touch database and does not validate /// anything that depends on the context. /// </summary> /// <param name="Activity">Description of the activity to validate.</param> /// <param name="OwnerIdentityPublicKey">Public key of the activity's owner identity.</param> /// <param name="MessageBuilder">Network message builder of the client who sent this activity description.</param> /// <param name="RequestMessage">Full request message from client.</param> /// <param name="ErrorPrefix">Prefix to add to the validation error details.</param> /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param> /// <returns>true if the activity information is valid, false otherwise.</returns> public static bool ValidateActivityInformation(ActivityInformation Activity, byte[] OwnerIdentityPublicKey, ProxMessageBuilder MessageBuilder, ProxProtocolMessage RequestMessage, string ErrorPrefix, out ProxProtocolMessage ErrorResponse) { log.Trace("()"); bool res = false; ErrorResponse = null; string details = null; if (Activity == null) { Activity = new ActivityInformation(); } if (Activity.ProfileServerContact == null) { Activity.ProfileServerContact = new ServerContactInfo(); } SemVer version = new SemVer(Activity.Version); // Currently only supported version is 1.0.0. if (!version.Equals(SemVer.V100)) { log.Debug("Unsupported version '{0}'.", version); details = "version"; } if (details == null) { uint activityId = Activity.Id; // 0 is not a valid activity identifier. if (activityId == 0) { log.Debug("Invalid activity ID '{0}'.", activityId); details = "id"; } } if (details == null) { byte[] pubKey = Activity.OwnerPublicKey.ToByteArray(); bool pubKeyValid = (0 < pubKey.Length) && (pubKey.Length <= ProtocolHelper.MaxPublicKeyLengthBytes) && ByteArrayComparer.Equals(OwnerIdentityPublicKey, pubKey); if (!pubKeyValid) { log.Debug("Invalid public key '{0}' does not match identity public key '{1}'.", pubKey.ToHex(), OwnerIdentityPublicKey.ToHex()); details = "ownerPublicKey"; } } if (details == null) { ServerContactInfo sci = Activity.ProfileServerContact; bool networkIdValid = sci.NetworkId.Length == ProtocolHelper.NetworkIdentifierLength; IPAddress ipAddress = IPAddressExtensions.IpFromBytes(sci.IpAddress.ToByteArray()); bool ipAddressValid = (ipAddress != null) && (Config.Configuration.TestModeEnabled || !ipAddress.IsReservedOrLocal()); bool portValid = (1 <= sci.PrimaryPort) && (sci.PrimaryPort <= 65535); if (!networkIdValid || !ipAddressValid || !portValid) { log.Debug("Profile server contact's network ID is {0}, IP address is {1}, port is {2}.", networkIdValid ? "valid" : "invalid", ipAddressValid ? "valid" : "invalid", portValid ? "valid" : "invalid"); if (!networkIdValid) { details = "profileServerContact.networkId"; } else if (!ipAddressValid) { details = "profileServerContact.ipAddress"; } else if (!portValid) { details = "profileServerContact.primaryPort"; } } } if (details == null) { string activityType = Activity.Type; if (activityType == null) { activityType = ""; } int byteLen = Encoding.UTF8.GetByteCount(activityType); bool activityTypeValid = (0 < byteLen) && (byteLen <= ProxMessageBuilder.MaxActivityTypeLengthBytes); if (!activityTypeValid) { log.Debug("Activity type too long or zero length ({0} bytes, limit is {1}).", byteLen, ProxMessageBuilder.MaxActivityTypeLengthBytes); details = "type"; } } if (details == null) { GpsLocation locLat = new GpsLocation(Activity.Latitude, 0); GpsLocation locLong = new GpsLocation(0, Activity.Longitude); if (!locLat.IsValid()) { log.Debug("Latitude '{0}' is not a valid GPS latitude value.", Activity.Latitude); details = "latitude"; } else if (!locLong.IsValid()) { log.Debug("Longitude '{0}' is not a valid GPS longitude value.", Activity.Longitude); details = "longitude"; } } if (details == null) { uint precision = Activity.Precision; bool precisionValid = (0 <= precision) && (precision <= ProxMessageBuilder.MaxLocationPrecision); if (!precisionValid) { log.Debug("Precision '{0}' is not an integer between 0 and {1}.", precision, ProxMessageBuilder.MaxLocationPrecision); details = "precision"; } } if (details == null) { DateTime?startTime = ProtocolHelper.UnixTimestampMsToDateTime(Activity.StartTime); DateTime?expirationTime = ProtocolHelper.UnixTimestampMsToDateTime(Activity.ExpirationTime); if (startTime == null) { log.Debug("Invalid activity start time timestamp '{0}'.", Activity.StartTime); details = "startTime"; } else if (expirationTime == null) { log.Debug("Invalid activity expiration time timestamp '{0}'.", Activity.ExpirationTime); details = "expirationTime"; } else { if (startTime > expirationTime) { log.Debug("Activity expiration time has to be greater than or equal to its start time."); details = "expirationTime"; } else if (expirationTime.Value > DateTime.UtcNow.AddHours(ActivityBase.MaxActivityLifeTimeHours)) { log.Debug("Activity expiration time {0} is more than {1} hours in the future.", expirationTime.Value.ToString("yyyy-MM-dd HH:mm:ss"), ActivityBase.MaxActivityLifeTimeHours); details = "expirationTime"; } } } if (details == null) { string extraData = Activity.ExtraData; if (extraData == null) { extraData = ""; } // Extra data is semicolon separated 'key=value' list, max ActivityBase.MaxActivityExtraDataLengthBytes bytes long. int byteLen = Encoding.UTF8.GetByteCount(extraData); if (byteLen > ProxMessageBuilder.MaxActivityExtraDataLengthBytes) { log.Debug("Extra data too large ({0} bytes, limit is {1}).", byteLen, ProxMessageBuilder.MaxActivityExtraDataLengthBytes); details = "extraData"; } } if (details == null) { res = true; } else { ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, ErrorPrefix + details); } log.Trace("(-):{0}", res); return(res); }
public void NullsEqual() { Assert.Equal(0, _comparer.Compare(null, null)); Assert.True(ByteArrayComparer.ArraysEqual(null, null)); Assert.True(_comparer.Equals(null, null)); }
/// <summary> /// Checks whether the create activity request is valid. /// <para>This function does not verify the uniqueness of the activity identifier. /// It also does not verify whether a closer proximity server exists.</para> /// </summary> /// <param name="CreateActivityRequest">Create activity request part of the client's request message.</param> /// <param name="Client">Client that sent the request.</param> /// <param name="RequestMessage">Full request message from client.</param> /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param> /// <returns>true if the create activity request can be applied, false otherwise.</returns> public static bool ValidateCreateActivityRequest(CreateActivityRequest CreateActivityRequest, IncomingClient Client, ProxProtocolMessage RequestMessage, out ProxProtocolMessage ErrorResponse) { log.Trace("()"); bool res = false; ErrorResponse = null; string details = null; if (CreateActivityRequest == null) { CreateActivityRequest = new CreateActivityRequest(); } if (CreateActivityRequest.Activity == null) { CreateActivityRequest.Activity = new ActivityInformation(); } if (CreateActivityRequest.Activity.ProfileServerContact == null) { CreateActivityRequest.Activity.ProfileServerContact = new ServerContactInfo(); } SignedActivityInformation signedActivity = new SignedActivityInformation() { Activity = CreateActivityRequest.Activity, Signature = RequestMessage.Request.ConversationRequest.Signature }; if (ValidateSignedActivityInformation(signedActivity, Client.PublicKey, Client.MessageBuilder, RequestMessage, "", false, out ErrorResponse)) { byte[] ownerPubKey = CreateActivityRequest.Activity.OwnerPublicKey.ToByteArray(); if (!ByteArrayComparer.Equals(Client.PublicKey, ownerPubKey)) { log.Debug("Client's public key '{0}' does not match activity owner's public key '{1}'.", Client.PublicKey.ToHex(), ownerPubKey.ToHex()); details = "activity.ownerPublicKey"; } if (details == null) { int index = 0; foreach (ByteString serverId in CreateActivityRequest.IgnoreServerIds) { if (serverId.Length != ProtocolHelper.NetworkIdentifierLength) { log.Debug("Ignored server ID #{0} is not a valid network ID as its length is {1} bytes.", index, serverId.Length); details = "ignoreServerIds"; break; } index++; } } if (details == null) { res = true; } else { ErrorResponse = Client.MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, details); } } log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Checks whether the contract received from user with hosting registration request is valid. /// <para>This function does not verify that the hosting plan exists.</para> /// </summary> /// <param name="IdentityPublicKey">Public key of the identity that wants to register hosting.</param> /// <param name="Contract">Description of the contract.</param> /// <param name="MessageBuilder">Client's network message builder.</param> /// <param name="RequestMessage">Full request message from client.</param> /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param> /// <returns>true if the profile update request can be applied, false otherwise.</returns> public static bool ValidateRegisterHostingRequest(byte[] IdentityPublicKey, HostingPlanContract Contract, PsMessageBuilder MessageBuilder, PsProtocolMessage RequestMessage, out PsProtocolMessage ErrorResponse) { log.Trace("(IdentityPublicKey:'{0}')", IdentityPublicKey.ToHex()); bool res = false; ErrorResponse = null; string details = null; if (Contract == null) { Contract = new HostingPlanContract(); } if (!MessageBuilder.VerifySignedConversationRequestBodyPart(RequestMessage, Contract.ToByteArray(), IdentityPublicKey)) { log.Debug("Contract signature is invalid."); ErrorResponse = MessageBuilder.CreateErrorInvalidSignatureResponse(RequestMessage); details = ""; } if (details == null) { byte[] contractPubKey = Contract.IdentityPublicKey.ToByteArray(); bool publicKeyValid = ByteArrayComparer.Equals(contractPubKey, IdentityPublicKey); if (!publicKeyValid) { log.Debug("Contract public key '{0}' does not match client's public key '{1}'.", contractPubKey.ToHex(), IdentityPublicKey.ToHex()); details = "contract.identityPublicKey"; } } if (details == null) { DateTime?startTime = ProtocolHelper.UnixTimestampMsToDateTime(Contract.StartTime); bool startTimeValid = (startTime != null) && ((startTime.Value - DateTime.UtcNow).TotalMinutes >= -60); if (!startTimeValid) { if (startTime == null) { log.Debug("Invalid contract start time timestamp {0}.", Contract.StartTime); } else { log.Debug("Contract start time {0} is more than 1 hour in the past.", startTime.Value.ToString("yyyy-MM-dd HH:mm:ss")); } details = "contract.startTime"; } } if (details == null) { int typeSize = Encoding.UTF8.GetByteCount(Contract.IdentityType); bool typeValid = (0 < typeSize) && (typeSize <= IdentityBase.MaxProfileTypeLengthBytes); if (!typeValid) { log.Debug("Invalid contract identity type size in bytes {0}.", typeSize); details = "contract.identityType"; } } if (details == null) { res = true; } else { if (ErrorResponse == null) { ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, details); } } log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Checks whether AddRelatedIdentityRequest request is valid. /// </summary> /// <param name="Client">Client that sent the request.</param> /// <param name="AddRelatedIdentityRequest">Client's request message to validate.</param> /// <param name="MessageBuilder">Client's network message builder.</param> /// <param name="RequestMessage">Full request message from client.</param> /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param> /// <returns>true if the profile update request can be applied, false otherwise.</returns> public static bool ValidateAddRelatedIdentityRequest(IncomingClient Client, AddRelatedIdentityRequest AddRelatedIdentityRequest, PsMessageBuilder MessageBuilder, PsProtocolMessage RequestMessage, out PsProtocolMessage ErrorResponse) { log.Trace("()"); bool res = false; ErrorResponse = null; string details = null; if (AddRelatedIdentityRequest == null) { AddRelatedIdentityRequest = new AddRelatedIdentityRequest(); } if (AddRelatedIdentityRequest.CardApplication == null) { AddRelatedIdentityRequest.CardApplication = new CardApplicationInformation(); } if (AddRelatedIdentityRequest.SignedCard == null) { AddRelatedIdentityRequest.SignedCard = new SignedRelationshipCard(); } if (AddRelatedIdentityRequest.SignedCard.Card == null) { AddRelatedIdentityRequest.SignedCard.Card = new RelationshipCard(); } CardApplicationInformation cardApplication = AddRelatedIdentityRequest.CardApplication; SignedRelationshipCard signedCard = AddRelatedIdentityRequest.SignedCard; RelationshipCard card = signedCard.Card; byte[] applicationId = cardApplication.ApplicationId.ToByteArray(); byte[] cardId = card.CardId.ToByteArray(); if ((applicationId.Length == 0) || (applicationId.Length > RelatedIdentity.CardIdentifierLength)) { log.Debug("Card application ID is invalid."); details = "cardApplication.applicationId"; } if (details == null) { byte[] appCardId = cardApplication.CardId.ToByteArray(); if (!ByteArrayComparer.Equals(cardId, appCardId)) { log.Debug("Card IDs in application card and relationship card do not match."); details = "cardApplication.cardId"; } } if (details == null) { if (card.ValidFrom > card.ValidTo) { log.Debug("Card validFrom field is greater than validTo field."); details = "signedCard.card.validFrom"; } else { DateTime?cardValidFrom = ProtocolHelper.UnixTimestampMsToDateTime(card.ValidFrom); DateTime?cardValidTo = ProtocolHelper.UnixTimestampMsToDateTime(card.ValidTo); if (cardValidFrom == null) { log.Debug("Card validFrom value '{0}' is not a valid timestamp.", card.ValidFrom); details = "signedCard.card.validFrom"; } else if (cardValidTo == null) { log.Debug("Card validTo value '{0}' is not a valid timestamp.", card.ValidTo); details = "signedCard.card.validTo"; } } } if (details == null) { byte[] issuerPublicKey = card.IssuerPublicKey.ToByteArray(); bool pubKeyValid = (0 < issuerPublicKey.Length) && (issuerPublicKey.Length <= ProtocolHelper.MaxPublicKeyLengthBytes); if (!pubKeyValid) { log.Debug("Issuer public key has invalid length {0} bytes.", issuerPublicKey.Length); details = "signedCard.card.issuerPublicKey"; } } if (details == null) { byte[] recipientPublicKey = card.RecipientPublicKey.ToByteArray(); if (!ByteArrayComparer.Equals(recipientPublicKey, Client.PublicKey)) { log.Debug("Caller is not recipient of the card."); details = "signedCard.card.recipientPublicKey"; } } if (details == null) { if (!Client.MessageBuilder.VerifySignedConversationRequestBodyPart(RequestMessage, cardApplication.ToByteArray(), Client.PublicKey)) { log.Debug("Caller is not recipient of the card."); ErrorResponse = Client.MessageBuilder.CreateErrorInvalidSignatureResponse(RequestMessage); details = ""; } } if (details == null) { SemVer cardVersion = new SemVer(card.Version); if (!cardVersion.Equals(SemVer.V100)) { log.Debug("Card version is invalid or not supported."); details = "signedCard.card.version"; } } if (details == null) { if (Encoding.UTF8.GetByteCount(card.Type) > PsMessageBuilder.MaxRelationshipCardTypeLengthBytes) { log.Debug("Card type is too long."); details = "signedCard.card.type"; } } if (details == null) { RelationshipCard emptyIdCard = new RelationshipCard() { CardId = ProtocolHelper.ByteArrayToByteString(new byte[RelatedIdentity.CardIdentifierLength]), Version = card.Version, IssuerPublicKey = card.IssuerPublicKey, RecipientPublicKey = card.RecipientPublicKey, Type = card.Type, ValidFrom = card.ValidFrom, ValidTo = card.ValidTo }; byte[] hash = Crypto.Sha256(emptyIdCard.ToByteArray()); if (!ByteArrayComparer.Equals(hash, cardId)) { log.Debug("Card ID '{0}' does not match its hash '{1}'.", cardId.ToHex(64), hash.ToHex()); details = "signedCard.card.cardId"; } } if (details == null) { byte[] issuerSignature = signedCard.IssuerSignature.ToByteArray(); byte[] issuerPublicKey = card.IssuerPublicKey.ToByteArray(); if (!Ed25519.Verify(issuerSignature, cardId, issuerPublicKey)) { log.Debug("Issuer signature is invalid."); details = "signedCard.issuerSignature"; } } if (details == null) { res = true; } else { if (ErrorResponse == null) { ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, details); } } log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Checks whether a profile information is valid. /// </summary> /// <param name="Profile">Profile information to check.</param> /// <param name="IdentityPublicKey">Public key of the profile's identity.</param> /// <param name="MessageBuilder">Client's network message builder.</param> /// <param name="RequestMessage">Full request message from client.</param> /// <param name="ErrorPrefix">Prefix to add to the validation error details.</param> /// <param name="ErrorResponse">If the function fails, this is filled with error response message that is ready to be sent to the client.</param> /// <returns>true if the signed profile information is valid, false otherwise.</returns> public static bool ValidateProfileInformation(ProfileInformation Profile, byte[] IdentityPublicKey, PsMessageBuilder MessageBuilder, PsProtocolMessage RequestMessage, string ErrorPrefix, out PsProtocolMessage ErrorResponse) { log.Trace("()"); bool res = false; ErrorResponse = null; string details = null; if (Profile == null) { Profile = new ProfileInformation(); } SemVer version = new SemVer(Profile.Version); // Currently only supported version is 1.0.0. if (!version.Equals(SemVer.V100)) { log.Debug("Unsupported version '{0}'.", version); details = "version"; } if (details == null) { byte[] pubKey = Profile.PublicKey.ToByteArray(); bool pubKeyValid = (0 < pubKey.Length) && (pubKey.Length <= ProtocolHelper.MaxPublicKeyLengthBytes) && ByteArrayComparer.Equals(IdentityPublicKey, pubKey); if (!pubKeyValid) { log.Debug("Invalid public key '{0}' does not match identity public key '{1}'.", pubKey.ToHex(), IdentityPublicKey.ToHex()); details = "publicKey"; } } if (details == null) { int typeSize = Encoding.UTF8.GetByteCount(Profile.Type); bool typeValid = (0 < typeSize) && (typeSize <= IdentityBase.MaxProfileTypeLengthBytes); if (!typeValid) { log.Debug("Invalid type size in bytes {0}.", typeSize); details = "type"; } } if (details == null) { int nameSize = Encoding.UTF8.GetByteCount(Profile.Name); bool nameValid = (0 < nameSize) && (nameSize <= IdentityBase.MaxProfileNameLengthBytes); if (!nameValid) { log.Debug("Invalid name size in bytes {0}.", nameSize); details = "name"; } } if (details == null) { GpsLocation locLat = new GpsLocation(Profile.Latitude, 0); if (!locLat.IsValid()) { log.Debug("Invalid latitude {0}.", Profile.Latitude); details = "latitude"; } } if (details == null) { GpsLocation locLon = new GpsLocation(0, Profile.Longitude); if (!locLon.IsValid()) { log.Debug("Invalid longitude {0}.", Profile.Longitude); details = "longitude"; } } if (details == null) { int extraDataSize = Encoding.UTF8.GetByteCount(Profile.ExtraData); bool extraDataValid = extraDataSize <= IdentityBase.MaxProfileExtraDataLengthBytes; if (!extraDataValid) { log.Debug("Invalid extraData size in bytes {0}.", extraDataSize); details = "extraData"; } } if (details == null) { bool profileImageHashValid = (Profile.ProfileImageHash.Length == 0) || (Profile.ProfileImageHash.Length == ProtocolHelper.HashLengthBytes); if (!profileImageHashValid) { log.Debug("Invalid profile image hash size {0} bytes.", Profile.ProfileImageHash.Length); details = "profileImageHash"; } } if (details == null) { bool thumbnailImageHashValid = (Profile.ThumbnailImageHash.Length == 0) || (Profile.ThumbnailImageHash.Length == ProtocolHelper.HashLengthBytes); if (!thumbnailImageHashValid) { log.Debug("Invalid thumbnail image hash size {0} bytes.", Profile.ThumbnailImageHash.Length); details = "thumbnailImageHash"; } } if (details == null) { res = true; } else { ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, ErrorPrefix + details); } log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Compares this activity to other activity and returns list of changed properties. /// </summary> /// <param name="Other">Other activity to compare to.</param> /// <returns>Bit mask information about which properties are different.</returns> public ActivityChange CompareChangeTo(ActivityBase Other) { ActivityChange res = ActivityChange.None; SemVer thisVersion = new SemVer(this.Version); SemVer otherVersion = new SemVer(Other.Version); if (!thisVersion.Equals(otherVersion)) { res |= ActivityChange.Version; } if (this.ActivityId != Other.ActivityId) { res |= ActivityChange.ActivityId; } if (!ByteArrayComparer.Equals(this.OwnerIdentityId, Other.OwnerIdentityId)) { res |= ActivityChange.OwnerIdentityId; } if (!ByteArrayComparer.Equals(this.OwnerPublicKey, Other.OwnerPublicKey)) { res |= ActivityChange.OwnerPublicKey; } if (!ByteArrayComparer.Equals(this.OwnerProfileServerId, Other.OwnerProfileServerId)) { res |= ActivityChange.OwnerProfileServerId; } if (!ByteArrayComparer.Equals(this.OwnerProfileServerIpAddress, Other.OwnerProfileServerIpAddress)) { res |= ActivityChange.OwnerProfileServerIpAddress; } if (this.OwnerProfileServerPrimaryPort != Other.OwnerProfileServerPrimaryPort) { res |= ActivityChange.OwnerProfileServerPrimaryPort; } if (this.Type != Other.Type) { res |= ActivityChange.Type; } if (this.LocationLatitude != Other.LocationLatitude) { res |= ActivityChange.LocationLatitude; } if (this.LocationLongitude != Other.LocationLongitude) { res |= ActivityChange.LocationLongitude; } if (this.PrecisionRadius != Other.PrecisionRadius) { res |= ActivityChange.PrecisionRadius; } if (this.StartTime != Other.StartTime) { res |= ActivityChange.StartTime; } if (this.ExpirationTime != Other.ExpirationTime) { res |= ActivityChange.ExpirationTime; } if (this.ExtraData != Other.ExtraData) { res |= ActivityChange.ExtraData; } return(res); }