Skip to content

Dynamic KAS selection Mappings ​

What is KAS Mapping? ​

The KAS Mapping is an optional configuration in your SDSDK instance.

It allows for the dynamic selection of one or more Key Access Servers (KAS) during encryption based on dataAttributes.

Instead of always encrypting with the full configured kasList, the SDK evaluates mapping rules and determines which KAS should be used to protect the Data Encryption Key (DEK), either entirely or as cryptographic splits of it.

πŸ“„πŸ” Practical example: Protecting confidential project documents ​

A company needs to encrypt confidential documents before storing them or sharing them with external partners.

Different rules apply depending on:

  • Which partners are allowed to access the document
  • Which internal department must authorize the access to the document

Two types of access controls are required:

RuleSecurity behavior
Sharing with partnersAny authorized partner can decrypt the document
Internal authorizationMultiple internal departments must grant authorization

To implement this, the SDK mapping uses:

  • Permissive mappings β†’ OR logic between partners
  • Restrictive mappings β†’ AND logic between internal authorities

KAS Infrastructure ​

Each organization or internal authority operates its own Key Access Server.

OrganizationKAS
Internal Security Departmentkas-security
Legal Departmentkas-legal
Partner Company Akas-partner-a
Partner Company Bkas-partner-b

Mapping Configuration ​

An SDSDK is configured as follows:

ts
const config: SdsdkConfiguration = {
  defaultKasId: "kas-security",
  kasList: [
    { id: "kas-security", protocols: [Protocol.Kas], url: "...", publicKey: {...} },
    { id: "kas-legal", protocols: [Protocol.Kas], url: "...", publicKey: {...} },
    { id: "kas-partner-a", protocols: [Protocol.Kas], url: "...", publicKey: {...} },
    { id: "kas-partner-b", protocols: [Protocol.Kas], url: "...", publicKey: {...} }
  ],

  mappingSections: [
    {
      label: "document-access-rules",
      mapping: [
        {
          attributeName: "sharedWith",
          type: MappingItemType.Permissive,
          attributeValues: [
            { value: "partner-a", kasIds: ["kas-partner-a"] },
            { value: "partner-b", kasIds: ["kas-partner-b"] },
            { value: "all-partners", kasIds: ["kas-partner-a", "kas-partner-b"] }
          ]
        },

        {
          attributeName: "belongsTo",
          type: MappingItemType.Restrictive,
          attributeValues: [
            {
              value: "confidential-project",
              kasIds: ["kas-security", "kas-legal"]
            }
          ]
        }
      ]
    }
  ]
};

Encryption Example ​

A document belongs to a confidential project and can be shared with all partners.

ts
await sdsdk.encrypt({
  data: documentBytes,
  dataAttributes: [{ sharedWith: 'all-partners' }, { belongsTo: 'confidential-project' }],
  protocol: Protocol.Kas,
});

Resulting Encryption Model ​

Mapping evaluation produces:

RuleResult
sharedWith: all-partnerspermissive group β†’ kas-partner-a OR kas-partner-b
belongsTo: confidential-projectrestrictive β†’ kas-security AND kas-legal

The confidential document will be secured according to all these rules:

mapping

Security Implications ​

This enforces the following rule:

( partner A OR partner B ) AND security AND legal

Meaning that:

  • the document can be accessed by the authorized partner A or B
  • but internal authorization from both departments is required

Mapping Configuration Structure ​

Mapping is configured in SdsdkConfiguration:

typescript
{
  defaultKasId: "...",
  kasList: [...],
  mappingSections: [
    {
      label: 'example-mapping',
      mapping: [
        {
          attributeName: 'releasableTo',
          type: MappingItemType.Permissive,
          attributeValues: [
            { value: 'A-B', kasIds: ['kas-A', 'kas-B'] },
            { value: 'A', kasIds: ['kas-A'] },
            { value: 'B', kasIds: ['kas-B'] },
          ],
        },
      ],
    },
  ];
}

The mapping is an item in the mappingSections list.

Each mapping item defines:

  • attributeName: the DataAttribute key
  • type: how matching KAS are split (permissive or restrictive)
  • attributeValues: mapping between attribute value and KAS identifiers

⚠️ For the moment, only one mappingSection item is allowed in the mappingSections list. Each additional mappingSections item will cause an error. However, each section may contain multiple mapping items.

βš™οΈ How Mapping Is Evaluated (technical) ​

During encryption:

  1. Data is encrypted via a DEK (Data Encryption Key).
  2. The SDK extracts eligible dataAttributes (1-level object with string as value).
  3. For each attribute, the SDK getsΒ all the matching mapping item and values.
  4. These matches allow to split the DEK in different splits (sDEKs), then encrypt each sDEK with one or more KAS, according to mapping.
  5. Encrypted sDEKs are stored in the produced ZTDF manifest.

-> If no mapping rule matches or if a dataAttribute value is not defined in the mapping, then encryption fails.

-> Identical KAS sets are automatically deduplicated. (["kas-A" OR "kas-B"] = ["kas-B" OR "kas-A"])

⚠️ After mapping resolution:

  • Each selected KAS must support the requested encryption protocol.
  • If not, encryption fails with a KasSelectionError.

Mapping Modes ​

Mapping supports two modes.

Permissive Mode (permissive) ​

Permissive mode behaves like a logical OR.

Within a single split, the entire DEK is wrapped for each KAS. Any of those KAS can independently unwrap that DEK (logical OR inside the split).

Example ​

An SDSDK instance with this mapping:

typescript
{
  attributeName: "releasableTo",
  type: MappingItemType.Permissive,
  attributeValues: [
    { value: "A-B", kasIds: ["kas-A", "kas-B"] },
    { value: "B-C", kasIds: ["kas-B", "kas-C"] }

  ]
},
{
  attributeName: "releasableTo2",
  type: MappingItemType.Permissive,
  attributeValues: [
    { value: "B-C", kasIds: ["kas-B", "kas-C"] }
  ]
}

Encryption input:

Sdsdk.encrypt() is called with these data attributes in input:

typescript
dataAttributes: [{ releasableTo: 'A-B' }];

Result:

  • entire DEK is wrapped for:
    • kas-A
    • kas-B

Multiple permissive attributes ​

Each matching attribute produces its own split.

Example:

Sdsdk.encrypt() is called with these data attributes in input:

typescript
dataAttributes: [{ releasableTo: 'A-B' }, { releasableTo2: 'B-C' }];

If both match permissive rules:

Result:

  • 2 independent encryption splits
  • One split that can be decrypted by A OR B
  • One split that can be decrypted by B OR C

⚠️ Note on multiple permissive attributes
In permissive mode, when several dataAttributes share the same attributeName, the SDK resolves each value and merges all mapped KAS into one combined split (logical OR) before generating the final encryption split. For example:

typescript
dataAttributes: [{ releasableTo: 'A-B' }, { releasableTo: 'B-C' }];

Will be interpreted with this result:

  • 1 encryption split
  • entire DEK is wrapped for:
    • kas-A
    • kas-B
    • kas-C

Restrictive Mode (restrictive) ​

Restrictive mode creates independent encryption splits per matching KAS.

Each matching KAS generates its own split & each resulting split protects a distinct cryptographic share of the DEK.

During the decryption, all shares are required to reconstruct the DEK

Example: ​

An SDSDK instance with this mapping:

typescript
{
  attributeName: "mission",
  type: MappingItemType.Restrictive,
  attributeValues: [
    { value: "alpha", kasIds: ["kas-A", "kas-B"] },
    { value: "beta", kasIds: ["kas-B", "kas-C"] }

  ]
}

Encryption input:

Sdsdk.encrypt() is called with these data attributes in input:

typescript
dataAttributes: [{ mission: 'alpha' }];

Result:

  • The DEK is split into multiple cryptographic shares (SDEKs)
  • One is encrypted by kas-A
  • One is encrypted by kas-B

⚠️ Note on duplicated restrictive attributes
In restrictive mode, when different splits are encrypted by the same KAS, the SDSDK deduplicate these splits. For example, Sdsdk.encrypt() is called with these data attributes in input:

typescript
dataAttributes: [{ mission: 'ALPHA' }, { mission: 'BETA' }];

Result in the following splits:

  • ["kas-A"]
  • ["kas-B"]
  • ["kas-B"]
  • ["kas-C"]

Which is simplified as:

  • ["kas-A"]
  • ["kas-B"]
  • ["kas-C"]

Combining Permissive and Restrictive Rules ​

An SDSDK instance with this mapping:

typescript
{
  attributeName: "releasableTo",
  type: MappingItemType.Permissive,
  attributeValues: [
    { value: "A-B", kasIds: ["kas-A", "kas-B"] },
    { value: "B-C", kasIds: ["kas-B", "kas-C"] }

  ]
},
{
  attributeName: "mission",
  type: MappingItemType.Restrictive,
  attributeValues: [
    { value: "alpha", kasIds: ["kas-A", "kas-B"] },
    { value: "beta", kasIds: ["kas-B", "kas-C"] }

  ]
}

Permissive and restrictive attributes can be combined to define rules.

Example:

typescript
dataAttributes: [
  { releasableTo: 'A-B' }, // permissive β†’ [kas-A, kas-B]
  { mission: 'alpha' }, // restrictive β†’ [kas-A], [kas-B]
];

Result:

  • 3 independent encryption splits
  • One split for A OR B
  • One split for kas-A
  • One split for kas-B

Just like before, identical KAS sets are automatically deduplicated:

dataAttributes: [ { releasableTo: 'A-B' }, // permissive β†’ [kas-A, kas-B] { mission: 'alpha' }, // restrictive β†’ [kas-A], [kas-B] { mission: 'beta' }, // restrictive β†’ [kas-B], [kas-C] ];

Result in the following splits:

  • ["kas-A"] OR ["kas-B"]
  • ["kas-A"]
  • ["kas-B"]
  • ["kas-B"]
  • ["kas-C"]

Which is simplified as:

  • ["kas-A"] OR ["kas-B"]
  • ["kas-A"]
  • ["kas-B"]
  • ["kas-C"]