コード例 #1
0
        /// <summary>
        /// Copies values from signed profile information to properties of this instance of the identity.
        /// </summary>
        /// <param name="SignedProfile">Signed information about the profile.</param>
        /// <param name="HostingServerId">In case of NeighborhIdentity, this is set to network identifier of the hosting server.</param>
        public void CopyFromSignedProfileInformation(SignedProfileInformation SignedProfile, byte[] HostingServerId)
        {
            if (HostingServerId == null)
            {
                HostingServerId = new byte[0];
            }

            ProfileInformation profile = SignedProfile.Profile;

            byte[]      pubKey     = profile.PublicKey.ToByteArray();
            byte[]      identityId = Crypto.Sha256(pubKey);
            GpsLocation location   = new GpsLocation(profile.Latitude, profile.Longitude);

            this.IdentityId               = identityId;
            this.HostingServerId          = HostingServerId;
            this.PublicKey                = pubKey;
            this.Version                  = profile.Version.ToByteArray();
            this.Name                     = profile.Name;
            this.Type                     = profile.Type;
            this.InitialLocationLatitude  = location.Latitude;
            this.InitialLocationLongitude = location.Longitude;
            this.ExtraData                = profile.ExtraData;
            this.ProfileImage             = profile.ProfileImageHash.Length != 0 ? profile.ProfileImageHash.ToByteArray() : null;
            this.ThumbnailImage           = profile.ThumbnailImageHash.Length != 0 ? profile.ThumbnailImageHash.ToByteArray() : null;
            this.Signature                = SignedProfile.Signature.ToByteArray();
        }
コード例 #2
0
        /// <summary>
        /// Creates a new instance of identity from SignedProfileInformation data structure.
        /// </summary>
        /// <param name="SignedProfile">Signed information about the profile.</param>
        /// <param name="HostingServerId">In case of NeighborhIdentity, this is set to network identifier of the hosting server.</param>
        /// <returns>New identity instance.</returns>
        public static NeighborIdentity FromSignedProfileInformation(SignedProfileInformation SignedProfile, byte[] HostingServerId)
        {
            NeighborIdentity res = new NeighborIdentity();

            res.CopyFromSignedProfileInformation(SignedProfile, HostingServerId);

            return(res);
        }
コード例 #3
0
        /// <summary>
        /// Checks whether a signed profile information is valid.
        /// </summary>
        /// <param name="SignedProfile">Signed 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="InvalidSignatureToDetails">If set to true, invalid signature error will be reported as invalid value in signature field.</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 and signed correctly by the given identity, false otherwise.</returns>
        public static bool ValidateSignedProfileInformation(SignedProfileInformation SignedProfile, byte[] IdentityPublicKey, PsMessageBuilder MessageBuilder, PsProtocolMessage RequestMessage, string ErrorPrefix, bool InvalidSignatureToDetails, out PsProtocolMessage ErrorResponse)
        {
            log.Trace("()");
            ErrorResponse = null;

            if (SignedProfile == null)
            {
                SignedProfile = new SignedProfileInformation();
            }
            if (SignedProfile.Profile == null)
            {
                SignedProfile.Profile = new ProfileInformation();
            }

            bool res = false;

            if (ValidateProfileInformation(SignedProfile.Profile, IdentityPublicKey, MessageBuilder, RequestMessage, ErrorPrefix + "profile.", out ErrorResponse))
            {
                // IdentityBase.InternalInvalidProfileType is a special internal type of profile that we use to prevent problems in the network.
                // This is not an elegant solution.
                // See NeighborhoodActionProcessor.NeighborhoodProfileUpdateAsync case NeighborhoodActionType.AddProfile for more information.
                if (SignedProfile.Profile.Type != IdentityBase.InternalInvalidProfileType)
                {
                    byte[] signature = SignedProfile.Signature.ToByteArray();
                    byte[] data      = SignedProfile.Profile.ToByteArray();

                    if (Ed25519.Verify(signature, data, IdentityPublicKey))
                    {
                        res = true;
                    }
                    else
                    {
                        ErrorResponse = InvalidSignatureToDetails ? MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, ErrorPrefix + "signature") : MessageBuilder.CreateErrorInvalidSignatureResponse(RequestMessage);
                    }
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
コード例 #4
0
        /// <summary>
        /// Creates SignedProfileInformation representation of the identity's profile.
        /// </summary>
        /// <returns>SignedProfileInformation structure describing the profile.</returns>
        public SignedProfileInformation ToSignedProfileInformation()
        {
            GpsLocation location         = this.GetInitialLocation();
            SignedProfileInformation res = new SignedProfileInformation()
            {
                Profile = new ProfileInformation()
                {
                    Version            = new SemVer(this.Version).ToByteString(),
                    PublicKey          = ProtocolHelper.ByteArrayToByteString(this.PublicKey),
                    Type               = this.Type,
                    Name               = this.Name,
                    ExtraData          = this.ExtraData,
                    Latitude           = location.GetLocationTypeLatitude(),
                    Longitude          = location.GetLocationTypeLongitude(),
                    ProfileImageHash   = ProtocolHelper.ByteArrayToByteString(this.ProfileImage != null ? this.ProfileImage : new byte[0]),
                    ThumbnailImageHash = ProtocolHelper.ByteArrayToByteString(this.ThumbnailImage != null ? this.ThumbnailImage: new byte[0])
                },
                Signature = ProtocolHelper.ByteArrayToByteString(this.Signature != null ? this.Signature : new byte[0])
            };

            return(res);
        }
コード例 #5
0
        /// <summary>
        /// Checks whether the update profile request is valid.
        /// </summary>
        /// <param name="Identity">Identity on which the update operation is about to be performed.</param>
        /// <param name="UpdateProfileRequest">Update profile request part of the client's request message.</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 ValidateUpdateProfileRequest(HostedIdentity Identity, UpdateProfileRequest UpdateProfileRequest, PsMessageBuilder MessageBuilder, PsProtocolMessage RequestMessage, out PsProtocolMessage ErrorResponse)
        {
            log.Trace("(Identity.IdentityId:'{0}')", Identity.IdentityId.ToHex());

            bool res = false;

            ErrorResponse = null;

            if (UpdateProfileRequest == null)
            {
                UpdateProfileRequest = new UpdateProfileRequest();
            }
            if (UpdateProfileRequest.Profile == null)
            {
                UpdateProfileRequest.Profile = new ProfileInformation();
            }

            SignedProfileInformation signedProfile = new SignedProfileInformation()
            {
                Profile   = UpdateProfileRequest.Profile,
                Signature = RequestMessage.Request.ConversationRequest.Signature
            };

            if (ValidateSignedProfileInformation(signedProfile, Identity.PublicKey, MessageBuilder, RequestMessage, "", false, out ErrorResponse))
            {
                string details = null;

                // Check if the update is a valid profile initialization.
                // If the profile is updated for the first time (aka is being initialized),
                // NoPropagation must be false.
                if (!Identity.Initialized && UpdateProfileRequest.NoPropagation)
                {
                    log.Debug("Attempt to initialize profile with NoPropagation set to false.");
                    details = "noPropagation";
                }

                if (details == null)
                {
                    // Profile type is unchangable after registration.
                    bool identityTypeValid = Identity.Type == UpdateProfileRequest.Profile.Type;
                    if (!identityTypeValid)
                    {
                        log.Debug("Attempt to change profile type.");
                        details = "profile.type";
                    }
                }

                if (details == null)
                {
                    byte[] profileImage     = UpdateProfileRequest.ProfileImage.ToByteArray();
                    byte[] profileImageHash = UpdateProfileRequest.Profile.ProfileImageHash.ToByteArray();

                    // Profile image must be PNG or JPEG image, no bigger than Identity.MaxProfileImageLengthBytes.
                    bool eraseImage        = profileImageHash.Length == 0;
                    bool profileImageValid = (profileImage.Length <= HostedIdentity.MaxProfileImageLengthBytes) && (eraseImage || ImageManager.ValidateImageWithHash(profileImage, profileImageHash));
                    if (!profileImageValid)
                    {
                        log.Debug("Invalid profile image.");
                        details = "profileImage";
                    }
                }

                if (details == null)
                {
                    byte[] thumbnailImage     = UpdateProfileRequest.ThumbnailImage.ToByteArray();
                    byte[] thumbnailImageHash = UpdateProfileRequest.Profile.ThumbnailImageHash.ToByteArray();

                    // Profile image must be PNG or JPEG image, no bigger than Identity.MaxThumbnailImageLengthBytes.
                    bool eraseImage          = thumbnailImageHash.Length == 0;
                    bool thumbnailImageValid = (thumbnailImage.Length <= IdentityBase.MaxThumbnailImageLengthBytes) && (eraseImage || ImageManager.ValidateImageWithHash(thumbnailImage, thumbnailImageHash));
                    if (!thumbnailImageValid)
                    {
                        log.Debug("Invalid thumbnail image.");
                        details = "thumbnailImage";
                    }
                }


                if (details == null)
                {
                    res = true;
                }
                else
                {
                    ErrorResponse = MessageBuilder.CreateErrorInvalidValueResponse(RequestMessage, details);
                }
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
コード例 #6
0
        /// <summary>
        /// Updates identity profile and creates neighborhood action to propagate the change unless the client did not want the propagation.
        /// </summary>
        /// <param name="IdentityId">Network identifier of the identity to update.</param>
        /// <param name="SignedProfile">Signed profile information with updated values.</param>
        /// <param name="ProfileImageChanged">True if profile image is about to change.</param>
        /// <param name="ThumbnailImageChanged">True if thumbnail image is about to change.</param>
        /// <param name="NoPropagation">True if the client does not want this change of profile to be propagated to the neighborhood.</param>
        /// <param name="IdentityNotFound">If the function fails because the identity is not found, this referenced value is set to true.</param>
        /// <param name="ImagesToDelete">If the function succeeds and the profile images are altered, old image files has to be deleted, in which case their hashes
        /// are returned in this list, which has to be initialized by the caller.</param>
        /// <returns>true if the function succeeds, false otherwise.</returns>
        public async Task <bool> UpdateProfileAndPropagateAsync(byte[] IdentityId, SignedProfileInformation SignedProfile, bool ProfileImageChanged, bool ThumbnailImageChanged, bool NoPropagation, StrongBox <bool> IdentityNotFound, List <byte[]> ImagesToDelete)
        {
            log.Trace("()");
            bool res = false;

            bool signalNeighborhoodAction = false;
            bool success = false;

            List <byte[]> imagesToDelete = new List <byte[]>();

            ProfileInformation profile = SignedProfile.Profile;

            DatabaseLock[] lockObjects = new DatabaseLock[] { UnitOfWork.HostedIdentityLock, UnitOfWork.FollowerLock, UnitOfWork.NeighborhoodActionLock };
            using (IDbContextTransaction transaction = await unitOfWork.BeginTransactionWithLockAsync(lockObjects))
            {
                try
                {
                    HostedIdentity identity = (await GetAsync(i => (i.IdentityId == IdentityId) && (i.Cancelled == false))).FirstOrDefault();
                    if (identity != null)
                    {
                        bool isProfileInitialization = !identity.Initialized;

                        identity.Initialized = true;
                        identity.Version     = profile.Version.ToByteArray();
                        identity.Name        = profile.Name;

                        GpsLocation location = new GpsLocation(profile.Latitude, profile.Longitude);
                        identity.SetInitialLocation(location);
                        identity.ExtraData = profile.ExtraData;
                        identity.Signature = SignedProfile.Signature.ToByteArray();

                        if (ProfileImageChanged)
                        {
                            if (identity.ProfileImage != null)
                            {
                                imagesToDelete.Add(identity.ProfileImage);
                            }
                            identity.ProfileImage = profile.ProfileImageHash.Length != 0 ? profile.ProfileImageHash.ToByteArray() : null;
                        }

                        if (ThumbnailImageChanged)
                        {
                            if (identity.ThumbnailImage != null)
                            {
                                imagesToDelete.Add(identity.ThumbnailImage);
                            }
                            identity.ThumbnailImage = profile.ThumbnailImageHash.Length != 0 ? profile.ThumbnailImageHash.ToByteArray() : null;
                        }


                        Update(identity);


                        if (!NoPropagation)
                        {
                            // The profile change has to be propagated to all our followers
                            // we create database actions that will be processed by dedicated thread.
                            NeighborhoodActionType actionType = isProfileInitialization ? NeighborhoodActionType.AddProfile : NeighborhoodActionType.ChangeProfile;
                            string extraInfo = identity.PublicKey.ToHex();
                            signalNeighborhoodAction = await unitOfWork.NeighborhoodActionRepository.AddIdentityProfileFollowerActionsAsync(actionType, identity.IdentityId, extraInfo);
                        }

                        await unitOfWork.SaveThrowAsync();

                        transaction.Commit();
                        success = true;
                    }
                    else
                    {
                        IdentityNotFound.Value = true;
                    }
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }

                if (!success)
                {
                    log.Warn("Rolling back transaction.");
                    unitOfWork.SafeTransactionRollback(transaction);
                }

                unitOfWork.ReleaseLock(lockObjects);
            }

            if (success)
            {
                // Only when the function succeeds the old images can be deleted.
                ImagesToDelete.AddRange(imagesToDelete);

                // Send signal to neighborhood action processor to process the new series of actions.
                if (signalNeighborhoodAction)
                {
                    Network.NeighborhoodActionProcessor neighborhoodActionProcessor = (Network.NeighborhoodActionProcessor)Base.ComponentDictionary[Network.NeighborhoodActionProcessor.ComponentName];
                    neighborhoodActionProcessor.Signal();
                }

                res = true;
            }


            log.Trace("(-):{0}", res);
            return(res);
        }