Skip to content

[PersonalData]

The [PersonalData] attribute marks a string property as containing personal data that should be encrypted. When EncryptAsync is called, the field's plaintext value is replaced with Base64-encoded AES-256-GCM ciphertext. When DecryptAsync is called, the original value is restored -- unless the encryption key has been deleted (crypto-shredded), in which case a replacement value is returned.

Basic Usage

Apply [PersonalData] to any string property alongside a [DataSubjectId] on the same class:

cs
public class ContactInfo
{
    [DataSubjectId]
    public Guid UserId { get; set; }

    [PersonalData]
    public string FirstName { get; set; } = "";

    [PersonalData]
    public string LastName { get; set; } = "";

    [PersonalData]
    public string PhoneNumber { get; set; } = "";
}
anchor

All three fields (FirstName, LastName, PhoneNumber) will be encrypted using the key derived from UserId.

Custom Mask Values

The MaskValue property specifies what value is returned when the encryption key has been deleted:

cs
public class UserAccount
{
    [DataSubjectId]
    public Guid Id { get; set; }

    [PersonalData(MaskValue = "[deleted user]")]
    public string DisplayName { get; set; } = "";

    [PersonalData(MaskValue = "deleted@example.com")]
    public string Email { get; set; } = "";

    [PersonalData(MaskValue = "000-00-0000")]
    public string TaxId { get; set; } = "";
}
anchor

After crypto-shredding:

  • DisplayName returns "[deleted user]"
  • Email returns "deleted@example.com"
  • TaxId returns "000-00-0000"

If MaskValue is not set, the default mask value for string fields is an empty string ("").

Groups

The Group property links a [PersonalData] field to a specific [DataSubjectId]. This enables multi-key scenarios where different fields on the same entity use different encryption keys:

cs
public class PatientRecord
{
    [DataSubjectId(Group = "medical")]
    public Guid PatientId { get; set; }

    [PersonalData(Group = "medical")]
    public string Diagnosis { get; set; } = "";

    [PersonalData(Group = "medical")]
    public string Treatment { get; set; } = "";

    /// <summary>
    /// Not in the "medical" group — uses a separate key.
    /// </summary>
    [DataSubjectId]
    public Guid RecordId { get; set; }

    [PersonalData]
    public string PatientName { get; set; } = "";
}
anchor

In this example:

  • Diagnosis and Treatment are encrypted with the key derived from PatientId (group "medical").
  • PatientName is encrypted with the key derived from RecordId (default group).
  • Shredding the "medical" group key destroys only the medical data. The patient's name remains decryptable.

Properties Reference

PropertyTypeDefaultDescription
Groupstring?nullLinks this field to a [DataSubjectId] with the same group name. Fields without a group use the default (ungrouped) data subject ID.
MaskValuestring?nullValue returned after crypto-shredding. Defaults to "" for strings if not specified.
Maskingstring?nullPartial masking strategy. When set (using a MaskingStrategies constant), a masked version of the value is embedded at encrypt time and returned after shredding instead of MaskValue.
MaskingParameterint0Mode-specific parameter. For MaskAfter, the number of leading characters to preserve. For MaskBefore, the number of trailing characters.

Masking Strategies

MaskingStrategies ConstantEffectExample InputExample Output
(not set / null)No masking; uses MaskValue----
MaskAfterKeep first N characters, mask the rest"Jane Doe" (N=2)"Ja******"
MaskBeforeKeep last N characters, mask the rest"Jane Doe" (N=3)"*****Doe"
MaskEmailDomainKeep local part, mask domain"jane@example.com""jane@***.***"

Masking vs. MaskValue

When Masking is set to a MaskingStrategies constant, the masked value is embedded in the ciphertext at encryption time. After crypto-shredding, the masked value is returned instead of the MaskValue string. This allows you to retain partial information (e.g., masked email) while still being GDPR-compliant.

Supported Types

[PersonalData] is designed for string properties. For non-string types (int, DateTime, decimal, etc.), use [SerializedPersonalData] instead.

Tayra also supports string collections (List<string>, string[], IList<string>, IReadOnlyList<string>, ICollection<string>). Simply apply [PersonalData] to the collection property and each element will be individually encrypted.

See Also