Skip to content

fix: add serializationOptions flag for AAD UTF8 sorting #1581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 9, 2025
14 changes: 7 additions & 7 deletions modules/branch-keystore-node/test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {

export const DDB_TABLE_NAME = 'KeyStoreDdbTable'
export const LOGICAL_KEYSTORE_NAME = DDB_TABLE_NAME
export const BRANCH_KEY_ID = '75789115-1deb-4fe3-a2ec-be9e885d1945'
export const BRANCH_KEY_ACTIVE_VERSION = 'fed7ad33-0774-4f97-aa5e-6c766fc8af9f'
export const BRANCH_KEY_ID = '3f43a9af-08c5-4317-b694-3d3e883dcaef'
export const BRANCH_KEY_ACTIVE_VERSION = 'a4905627-4b7f-4272-a847-f50dae245737'
export const BRANCH_KEY_ID_WITH_EC = '4bb57643-07c1-419e-92ad-0df0df149d7c'
export const BRANCH_KEY_ACTIVE_VERSION_UTF8_BYTES = Buffer.from(
BRANCH_KEY_ACTIVE_VERSION,
Expand Down Expand Up @@ -51,7 +51,7 @@ export const ENCRYPTED_ACTIVE_BRANCH_KEY = new EncryptedHierarchicalKey(
[TYPE_FIELD]: BRANCH_KEY_ACTIVE_TYPE,
[BRANCH_KEY_ACTIVE_VERSION_FIELD]:
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
[HIERARCHY_VERSION_FIELD]: '1',
[KMS_FIELD]: KEY_ARN,
[TABLE_FIELD]: LOGICAL_KEYSTORE_NAME,
Expand All @@ -60,7 +60,7 @@ export const ENCRYPTED_ACTIVE_BRANCH_KEY = new EncryptedHierarchicalKey(
)

const ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT_BASE64 =
'AQIBAHhTIzkciiF5TDB8qaCjctFmv6Dx+4yjarauOA4MtH0jwgFcb8VH4blkX0w7e59l8tl4AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM2tJUaqT5i07TTV9FAgEQgDsWBTM/N+rN+N7A1Js6TXVxbb64vt8eQ+G2LUs5yy98l11pXe78HZKnD+/YoUevUY1YDskV3ATRE+x2+g=='
'AQIBAHhTIzkciiF5TDB8qaCjctFmv6Dx+4yjarauOA4MtH0jwgHZhG1KfZ/k1VQMBZzo0X+GAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMG5wDTuB2qzfR/mOKAgEQgDtbcAO39/bHj6BGaqgZTd3DSKHmpORsoaHLilWhAHryOlSjAiXK1NZxil7hOLcxjBzKE0QsMAaWJVtwag=='
const ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT = new Uint8Array(
// @ts-ignore
Buffer.from(ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT_BASE64, 'base64')
Expand All @@ -71,7 +71,7 @@ export const ENCRYPTED_VERSION_BRANCH_KEY = new EncryptedHierarchicalKey(
[BRANCH_KEY_IDENTIFIER_FIELD]: BRANCH_KEY_ID,
[TYPE_FIELD]:
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
[HIERARCHY_VERSION_FIELD]: '1',
[KMS_FIELD]: KEY_ARN,
[TABLE_FIELD]: LOGICAL_KEYSTORE_NAME,
Expand All @@ -84,7 +84,7 @@ export const ACTIVE_BRANCH_KEY: BranchKeyRecord = {
[TYPE_FIELD]: BRANCH_KEY_ACTIVE_TYPE,
[BRANCH_KEY_ACTIVE_VERSION_FIELD]:
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
[HIERARCHY_VERSION_FIELD]: 1,
[KMS_FIELD]: KEY_ARN,
[BRANCH_KEY_FIELD]: ENCRYPTED_ACTIVE_BRANCH_KEY_CIPHERTEXT,
Expand All @@ -94,7 +94,7 @@ export const VERSION_BRANCH_KEY: BranchKeyRecord = {
[BRANCH_KEY_IDENTIFIER_FIELD]: BRANCH_KEY_ID,
[TYPE_FIELD]:
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
[HIERARCHY_VERSION_FIELD]: 1,
[KMS_FIELD]: KEY_ARN,
[BRANCH_KEY_FIELD]: ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
EncryptedDataKey,
EncryptionContext,
} from '@aws-crypto/material-management'
import { serializeFactory, uInt16BE } from '@aws-crypto/serialize'
import {
serializeFactory,
uInt16BE,
SerializeOptions,
} from '@aws-crypto/serialize'
import { compare } from './portable_compare'

// 512 bits of 0 for padding between hashes in decryption materials cache ID generation.
Expand All @@ -21,8 +25,9 @@ export function buildCryptographicMaterialsCacheKeyHelpers<
toUtf8: (input: Uint8Array) => string,
sha512: (...data: (Uint8Array | string)[]) => Promise<Uint8Array>
): CryptographicMaterialsCacheKeyHelpersInterface<S> {
const sorting: SerializeOptions = { utf8Sorting: true }
const { serializeEncryptionContext, serializeEncryptedDataKey } =
serializeFactory(fromUtf8)
serializeFactory(fromUtf8, sorting)

return {
buildEncryptionMaterialCacheKey,
Expand Down
44 changes: 44 additions & 0 deletions modules/decrypt-node/test/decrypt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,50 @@ describe('decrypt', () => {
})
).to.rejectedWith(Error, 'maxEncryptedDataKeys exceeded.')
})

it('will fail to decrypt ciphertext with high utf8 codepoints and no utf8 sorting on keyring', async () => {
const { decrypt } = buildDecrypt({
commitmentPolicy: CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT,
maxEncryptedDataKeys: 3,
})
const hkeyring = fixtures.hKeyring(false)
const ciphertext = fixtures.hierarchyMessageWithHighUtf8CodePoints()

await expect(
decrypt(hkeyring, ciphertext, {
encoding: 'base64',
})
).to.rejectedWith(Error, 'Unsupported state or unable to authenticate data')
})

it('will decrypt ciphertext with high utf8 codepoints with a keyring configured to do utf8 sorting', async () => {
const { decrypt } = buildDecrypt({
commitmentPolicy: CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT,
maxEncryptedDataKeys: 3,
})
const hkeyring = fixtures.hKeyring(true)
const ciphertext = fixtures.hierarchyMessageWithHighUtf8CodePoints()
const { plaintext } = await decrypt(hkeyring, ciphertext, {
encoding: 'base64',
})
expect(plaintext).to.deep.equal(Buffer.from('Hello World'))
})

it('will decrypt ciphertext with high utf8 codepoints with a multikeyring with both utf8 and no utf8 sorting', async () => {
const { decrypt } = buildDecrypt({
commitmentPolicy: CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT,
maxEncryptedDataKeys: 3,
})
const hkeyringUtf8 = fixtures.hKeyring(true)
const hkeyringNoUtf8 = fixtures.hKeyring(false)
const multikeyring = fixtures.multiKeyring(hkeyringUtf8, hkeyringNoUtf8)

const ciphertext = fixtures.hierarchyMessageWithHighUtf8CodePoints()
const { plaintext } = await decrypt(multikeyring, ciphertext, {
encoding: 'base64',
})
expect(plaintext).to.deep.equal(Buffer.from('Hello World'))
})
})

function chunkCipherTextStream(ciphertext: Buffer, { size }: { size: number }) {
Expand Down
37 changes: 36 additions & 1 deletion modules/decrypt-node/test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import {
NodeEncryptionMaterial,
KeyringNode,
KeyringTraceFlag,
MultiKeyringNode,
} from '@aws-crypto/material-management-node'

import { KmsHierarchicalKeyRingNode } from '@aws-crypto/kms-keyring-node'
import { BranchKeyStoreNode } from '@aws-crypto/branch-keystore-node'
export function base64CiphertextAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384() {
return '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'
}
Expand Down Expand Up @@ -74,6 +76,10 @@ export function fourEdksMessage() {
return 'AYAAFO/VP38MeBJoKQshvIaF8TgAAAAEAAR0ZW1wABhrZXkzAAAAgAAAAAzIotUMc7SaFTbzk0EAIKgxmVzmYedtQj5od6KglliFAx7G3OBYHMgyvbGQVJSyAAR0ZW1wABhrZXkwAAAAgAAAAAwBe+86+eb8+uYOeoIAIFmT8yZvThnsJigzsRen9OJc0kuGE+rJyalk+yF5VdNBAAR0ZW1wABhrZXkyAAAAgAAAAAy939QOrzUF3XKc0m8AICSGMg1tdgULYD15Jr7RWkFgqCXjtwyUK86xqrU+OzV9AAR0ZW1wABhrZXkxAAAAgAAAAAxE6lJVWjxWLtvnkBYAIJUl4vhbLEjNS/3g3of4T/QvAR7TGPJZgv7cLqOP0T7uAgAAAAAMAAAQAAAAAAAAAAAAAAAAAOMcqPpQVjBzbYAHIPjMM1T/////AAAAAQAAAAAAAAAAAAAAAQAAAAQPcr1WkUGY1IDMmCgdibk0zwg4Yg=='
}

export function hierarchyMessageWithHighUtf8CodePoints() {
return 'AgV4PI3AdgfggyWInxz6XkfmQMbOd7/RgCN9HTTg7KeiczgAaQACABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREE2MTQvUGlNNTkyNkZwN3NWeUltVjhNZVZ4eEJNeFRpajI5Y3ozeFVZaFIwT29wbHJjT0sxNWFrR25BcENiOGkxZz09AALCtgAE8JOJqQABABFhd3Mta21zLWhpZXJhcmNoeQAkNmUxZjVlZGMtOWE3MC00YThlLWFkZmItMzdhZWVmZGVhOWU2AFx9XILjG4l3+qJ1BzpOHfZ5mf0eHmX4r3+q16U1LRGGFoVxS2mPKAqwWMUSwe2tNCe3G6kANAmRi4pyD2FK0VpuHx5FbpNgP3BR+U3cHYXiSg1vMMYEMl3usnss1QIAABAA1/mKQ+4BMR0aajtum3RQQxKfvFpi5DeCTVt0V8x7ibGHP7FCZzQWujM7M/rcfkeo/////wAAAAEAAAAAAAAAAAAAAAEAAAALxddd43JEIrllMceZonlyQGtPTX1zTsf+vwChAGcwZQIxALKuLqVHZNuvXhyjjRzs8ysgtJqvcVgvIgX1ExBKHueZLP7XsZOSUG/4SqxMFXGzuQIwOf1zLim/I65beZF1p1az1gyD+UzpWIa/vg9y0wjsYPDUEb9sUyUVU3etQ+y0LwI2'
}

export function decryptKeyring(): KeyringNode {
class TestKeyring extends KeyringNode {
async _onEncrypt(): Promise<NodeEncryptionMaterial> {
Expand All @@ -95,6 +101,35 @@ export function decryptKeyring(): KeyringNode {
return new TestKeyring()
}

export function hKeyring(utf8Sorting: boolean): KeyringNode {
const keyStoreTableName = 'KeyStoreDdbTable'
const logicalKeyStoreName = keyStoreTableName
const kmsKeyId =
'arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126'
const keyStore = new BranchKeyStoreNode({
storage: { ddbTableName: keyStoreTableName },
logicalKeyStoreName: logicalKeyStoreName,
kmsConfiguration: { identifier: kmsKeyId },
})
const branchKeyId = '6e1f5edc-9a70-4a8e-adfb-37aeefdea9e6'
return new KmsHierarchicalKeyRingNode({
branchKeyId,
keyStore,
cacheLimitTtl: 600, // 10 min
utf8Sorting,
})
}

export function multiKeyring(
keyring1: KeyringNode,
keyring2: KeyringNode
): KeyringNode {
return new MultiKeyringNode({
generator: keyring1,
children: [keyring2],
})
}

export interface VectorTest {
ciphertext: string
commitment: string
Expand Down
2 changes: 1 addition & 1 deletion modules/encrypt-browser/src/encrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import { fromUtf8 } from '@aws-sdk/util-utf8-browser'
import { getWebCryptoBackend } from '@aws-crypto/web-crypto-backend'

const serialize = serializeFactory(fromUtf8)
const serialize = serializeFactory(fromUtf8, { utf8Sorting: true })
const { messageAADContentString, messageAAD } = aadFactory(fromUtf8)

export interface EncryptInput {
Expand Down
2 changes: 1 addition & 1 deletion modules/encrypt-node/src/encrypt_stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { Duplex } from 'stream'

const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
const { serializeMessageHeader, headerAuthIv, buildMessageHeader } =
serializeFactory(fromUtf8)
serializeFactory(fromUtf8, { utf8Sorting: true })

export interface EncryptStreamInput {
suiteId?: AlgorithmSuiteIdentifier
Expand Down
2 changes: 1 addition & 1 deletion modules/encrypt-node/src/framed_encrypt_stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@aws-crypto/material-management-node'

const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
const serialize = serializeFactory(fromUtf8)
const serialize = serializeFactory(fromUtf8, { utf8Sorting: true })
const { finalFrameHeader, frameHeader } = serialize
const aadUtility = aadFactory(fromUtf8)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export function aesKeyring(keyInfo: AesKeyInfo, key: AESKey) {
keyNamespace,
unencryptedMasterKey,
wrappingSuite,
utf8Sorting: true,
})
}

Expand Down
9 changes: 6 additions & 3 deletions modules/integration-node/src/integration_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ import {
MessageHeader,
needs,
DecryptOutput,
getCompatibleCommitmentPolicy,
} from '@aws-crypto/client-node'
import { version } from './version'
import { URL } from 'url'
import got from 'got'
import streamToPromise from 'stream-to-promise'
const { encrypt, decrypt, decryptUnsignedMessageStream } = buildClient(
CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
)
import { ZipFile } from 'yazl'
import { createWriteStream } from 'fs'
import { v4 } from 'uuid'
Expand Down Expand Up @@ -58,6 +56,9 @@ async function runDecryption(
testVectorInfo: TestVectorInfo
): Promise<DecryptOutput> {
const cmm = decryptMaterialsManagerNode(testVectorInfo.keysInfo)
const { decrypt, decryptUnsignedMessageStream } = buildClient(
CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
)
if (testVectorInfo.decryptionMethod == 'streaming-unsigned-only') {
const plaintext: Buffer[] = []
let messageHeader: MessageHeader | false = false
Expand Down Expand Up @@ -147,6 +148,8 @@ export async function testEncryptVector(
handleEncryptResult: HandleEncryptResult
): Promise<TestVectorResult> {
const { name, keysInfo, encryptOp, plainTextData } = info
const commitmentPolicy = getCompatibleCommitmentPolicy(encryptOp.suiteId)
const { encrypt } = buildClient(commitmentPolicy)
try {
const cmm = encryptMaterialsManagerNode(keysInfo)
const { result: encryptResult } = await encrypt(
Expand Down
15 changes: 13 additions & 2 deletions modules/kms-keyring-node/src/kms_hkeyring_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface KmsHierarchicalKeyRingNodeInput {
//= type=implication
//# - MAY provide a [Partition ID](#partition-id)
partitionId?: string
utf8Sorting?: boolean
}

export interface IKmsHierarchicalKeyRingNode extends KeyringNode {
Expand Down Expand Up @@ -104,6 +105,7 @@ export class KmsHierarchicalKeyRingNode
public declare maxCacheSize?: number
public declare _cmc: CryptographicMaterialsCache<NodeAlgorithmSuite>
declare readonly _partition: Buffer
public declare _utf8Sorting: boolean

constructor({
branchKeyId,
Expand All @@ -113,6 +115,7 @@ export class KmsHierarchicalKeyRingNode
cache,
maxCacheSize,
partitionId,
utf8Sorting,
}: KmsHierarchicalKeyRingNodeInput) {
super()

Expand Down Expand Up @@ -256,6 +259,12 @@ export class KmsHierarchicalKeyRingNode
readOnlyProperty(this, 'maxCacheSize', maxCacheSize)
readOnlyProperty(this, '_cmc', cache)

if (utf8Sorting === undefined) {
readOnlyProperty(this, '_utf8Sorting', false)
} else {
readOnlyProperty(this, '_utf8Sorting', utf8Sorting)
}

Object.freeze(this)
/* Postcondition: The HKR object must be frozen */
}
Expand Down Expand Up @@ -299,7 +308,8 @@ export class KmsHierarchicalKeyRingNode
const edk = wrapPlaintextDataKey(
pdk,
branchKeyMaterials,
encryptionMaterial
encryptionMaterial,
this._utf8Sorting
)

// return the modified encryption material with the new edk and newly
Expand Down Expand Up @@ -428,7 +438,8 @@ export class KmsHierarchicalKeyRingNode
udk = unwrapEncryptedDataKey(
ciphertext,
branchKeyMaterials,
decryptionMaterial
decryptionMaterial,
this._utf8Sorting
)
} catch (e) {
//= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#ondecrypt
Expand Down
20 changes: 13 additions & 7 deletions modules/kms-keyring-node/src/kms_hkeyring_node_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ const hexBytesToString = (input: Uint8Array): string =>
Buffer.from(input).toString('hex')
export const { uuidv4ToCompressedBytes, decompressBytesToUuidv4 } =
uuidv4Factory(stringToHexBytes, hexBytesToString)
export const { serializeEncryptionContext } =
serializeFactory(stringToUtf8Bytes)

export function getBranchKeyId(
{ branchKeyId, branchKeyIdSupplier }: IKmsHierarchicalKeyRingNode,
Expand Down Expand Up @@ -290,7 +288,8 @@ export function getPlaintextDataKey(material: NodeEncryptionMaterial) {
export function wrapPlaintextDataKey(
pdk: Uint8Array,
branchKeyMaterials: NodeBranchKeyMaterial,
{ encryptionContext }: NodeEncryptionMaterial
{ encryptionContext }: NodeEncryptionMaterial,
utf8Sorting: boolean
): Uint8Array {
// get what we need from branch key material to wrap the pdk
const branchKey = branchKeyMaterials.branchKey()
Expand Down Expand Up @@ -319,7 +318,8 @@ export function wrapPlaintextDataKey(
const wrappedAad = wrapAad(
branchKeyIdAsBytes,
branchKeyVersionAsBytesCompressed,
encryptionContext
encryptionContext,
utf8Sorting
)

// encrypt the pdk into an edk
Expand Down Expand Up @@ -361,10 +361,14 @@ export function wrapPlaintextDataKey(
export function wrapAad(
branchKeyIdAsBytes: Buffer,
version: Buffer,
encryptionContext: EncryptionContext
encryptionContext: EncryptionContext,
utf8Sorting: boolean
) {
/* Precondition: Branch key version must be 16 bytes */
needs(version.length === 16, 'Branch key version must be 16 bytes')
const { serializeEncryptionContext } = serializeFactory(stringToUtf8Bytes, {
utf8Sorting: utf8Sorting,
})

/* The AAD section is uInt16BE(length) + AAD
* see: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-aad
Expand Down Expand Up @@ -528,7 +532,8 @@ export function destructureCiphertext(
export function unwrapEncryptedDataKey(
ciphertext: Uint8Array,
branchKeyMaterials: NodeBranchKeyMaterial,
{ encryptionContext, suite }: NodeDecryptionMaterial
{ encryptionContext, suite }: NodeDecryptionMaterial,
utf8Sorting: boolean
) {
// get what we need from the branch key materials to unwrap the edk
const branchKey = branchKeyMaterials.branchKey()
Expand Down Expand Up @@ -557,7 +562,8 @@ export function unwrapEncryptedDataKey(
const wrappedAad = wrapAad(
branchKeyIdAsBytes,
branchKeyVersionAsBytesCompressed,
encryptionContext
encryptionContext,
utf8Sorting
)

// decipher the edk to get the udk/pdk
Expand Down
4 changes: 2 additions & 2 deletions modules/kms-keyring-node/test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {

export const DDB_TABLE_NAME = 'KeyStoreDdbTable'
export const LOGICAL_KEYSTORE_NAME = DDB_TABLE_NAME
export const BRANCH_KEY_ID = '75789115-1deb-4fe3-a2ec-be9e885d1945'
export const BRANCH_KEY_ACTIVE_VERSION = 'fed7ad33-0774-4f97-aa5e-6c766fc8af9f'
export const BRANCH_KEY_ID = '3f43a9af-08c5-4317-b694-3d3e883dcaef'
export const BRANCH_KEY_ACTIVE_VERSION = 'a4905627-4b7f-4272-a847-f50dae245737'
export const BRANCH_KEY_ID_WITH_EC = '4bb57643-07c1-419e-92ad-0df0df149d7c'

export const KEY_ARN =
Expand Down
Loading