Collection Encryption
Tayra supports encrypting collections of personal data. When a property marked with [PersonalData] is a List<string>, string[], or other supported collection type, Tayra encrypts each element individually. This is useful for entities that store multiple PII values in a single field, such as phone numbers, email aliases, medical allergies, or previous addresses.
Defining Collection PII
Mark collection properties with the [PersonalData] attribute, the same way you mark scalar string properties. Tayra detects the collection type automatically and encrypts each element:
public class PatientRecord
{
[DataSubjectId(Prefix = "patient-")]
public Guid PatientId { get; set; }
[PersonalData]
public string FullName { get; set; } = "";
[PersonalData]
public List<string> Allergies { get; set; } = new();
[PersonalData]
public string[] PreviousSurnames { get; set; } = Array.Empty<string>();
public string Department { get; set; } = "";
}In the example above:
Allergiesis aList<string>-- each allergy is encrypted individuallyPreviousSurnamesis astring[]-- each surname is encrypted individuallyFullNameis a regularstring-- encrypted as usualDepartmenthas no attribute -- left untouched
Per-Element Encryption
Each element in the collection is encrypted independently with the same key (derived from the [DataSubjectId]). This means:
- Individual elements can be added or removed without re-encrypting the entire collection
- Each element has its own nonce (initialization vector), so identical plaintext values produce different ciphertext
- The collection size is preserved -- a list with 3 elements before encryption still has 3 elements after
// Before encryption
patient.Allergies = new List<string> { "Penicillin", "Peanuts", "Latex" };
// After encryption — 3 Base64-encoded ciphertext strings
patient.Allergies = new List<string>
{
"AQzR4x5k...", // Encrypted "Penicillin"
"AQnT7y2m...", // Encrypted "Peanuts"
"AQ8pL9dw...", // Encrypted "Latex"
};Supported Collection Types
The following collection types are supported for [PersonalData] encryption:
| Type | Encryption | Decryption | Notes |
|---|---|---|---|
List<string> | In-place mutation | In-place mutation | Elements are replaced with encrypted/decrypted values |
string[] | In-place mutation | In-place mutation | Array elements are replaced directly |
IList<string> | In-place mutation | In-place mutation | Works via the IList<string> interface |
Crypto-Shredding with Collections
When the encryption key is deleted (crypto-shredding), collection elements follow the same rules as scalar fields:
- If partial redaction is configured, each element's embedded redacted value is returned
- Otherwise, each element is replaced with an empty string (
"")
// After crypto-shredding (key deleted)
patient.Allergies = new List<string> { "", "", "" };Partial Redaction
Collection elements support the same partial redaction modes as scalar fields. Configure redaction on the [PersonalData] attribute:
public class ContactInfo
{
[DataSubjectId]
public Guid SubjectId { get; set; }
[PersonalData(Masking = MaskingStrategies.MaskAfter, MaskingParameter = 3)]
public List<string> EmailAddresses { get; set; } = new();
}After crypto-shredding, each email would retain its first 3 characters:
"jan*****" // was "jane@example.com"
"bob*****" // was "bob@work.org"Limitations
- Only
stringelement types are supported for collection encryption. Collections of non-string types (e.g.,List<int>,List<DateTime>) must use[SerializedPersonalData]on the property with a companionbyte[]field instead. - Null elements in a collection are skipped during encryption and decryption. They remain
nullin the collection. - Immutable collections (e.g.,
IReadOnlyList<string>,ImmutableList<string>) are not supported because Tayra modifies elements in-place. UseList<string>orstring[]instead. - Deep collections (collections of objects with
[DeepPersonalData]) are supported -- Tayra will recursively encrypt PII fields on each element. The element objects must have their own[DataSubjectId]or inherit the parent's key context.
Performance
Collection encryption processes elements sequentially. For very large collections (thousands of elements), the encryption time scales linearly. If performance is a concern, consider batching large collections or encrypting them as a single serialized blob with [SerializedPersonalData].
See Also
- Getting Started -- Core encrypt/decrypt/shred workflow
- Audit Trail -- Audit events for collection operations
- Observability -- Metrics for encryption operations
