Carduus Databricks Getting Started Guide¶
Carduus is an implementaiton of the Open Privacy Preserving Record Linkage (OPPRL) specification. It allows organizations to replace personally identifiable information (PII) with enrypted tokens that can be used to correlate data records pertaining to the same subject while keeping the subject anonymous.
This notebook demonstrates the use of the carduus
python package in databricks to parallelize the tokenization processes using Spark clusters.
The following is a 5 row sample dataset of PII that will be tokenized. Notice that despite cosmetic differences the first 2 rows (label = 1) are describing the same subject and will receive the same sets of token values.
pii = spark.createDataFrame(
[
(1, "Jonas", "Salk", "male", "1914-10-28"),
(1, "jonas", "salk", "M", "1914-10-28"),
(2, "Elizabeth", "Blackwell", "F", "1821-02-03"),
(3, "Edward", "Jenner", "m", "1749-05-17"),
(4, "Alexander", "Fleming", "M", "1881-08-06"),
],
("label", "first_name", "last_name", "gender", "birth_date"),
)
display(pii)
label | first_name | last_name | gender | birth_date |
---|---|---|---|---|
1 | Jonas | Salk | male | 1914-10-28 |
1 | jonas | salk | M | 1914-10-28 |
2 | Elizabeth | Blackwell | F | 1821-02-03 |
3 | Edward | Jenner | m | 1749-05-17 |
4 | Alexander | Fleming | M | 1881-08-06 |
One-time Workspace Setup¶
See the Databricks documenation for managing secrets to get started. You will need an to install the Databricks CLI and authenticate as a user that can manage Databricks secrets.
Create a secret scope for carduus
encryption keys by running the following command with the Databricks CLI.
databricks secrets create-scope carduus
Add your private key as a secret in the carduus
scope.
(cat << EOF
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
EOF
) | databricks secrets put-secret carduus PrivateKey
Add the secrets for the public keys of the third party organiations you will be sending and receiving tokenized data to/from.
(cat << EOF
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOF
) | databricks secrets put-secret carduus MyPartnerPublicKey
Optional Databricks Compute Setup¶
Users can provide encryption keys to Carduus via programmatic access to Databricks secrets (via dbutils
) or by setting specific environment variables to take the value from Databricks secrets. See the Databricks secrets documentation for more information.
Users who wish to use programmatic access via dbutils
don't need any cluster configuration and can skip the remainder of this section.
To configure your Databricks compute such that your encryption keys will be available as environment variables, use the following syntax in the Databricks Compute configuration UI. See the Databricks documentation on Environment Variables for more information.
SPINDLE_TOKEN_PRIVATE_KEY={{secrets/carduus/PrivateKey}}
SPINDLE_TOKEN_RECIPIENT_PUBLIC_KEY={{secrets/carduus/MyPartnerPublicKey}}
Install carduus¶
You can install carduus
on your cluster, or within the notebook. See the Databricks docs for more information.
%pip install carduus
Tokenization of PII¶
from carduus.token import tokenize, OpprlPii, OpprlToken
tokens = tokenize(
pii,
pii_transforms=dict(
first_name=OpprlPii.first_name,
last_name=OpprlPii.last_name,
gender=OpprlPii.gender,
birth_date=OpprlPii.birth_date,
),
tokens=[OpprlToken.token1, OpprlToken.token2, OpprlToken.token3],
private_key=dbutils.secrets.getBytes("carduus", "PrivateKey"),
)
display(tokens)
label | first_name | last_name | gender | birth_date | opprl_token_1 | opprl_token_2 | opprl_token_3 |
---|---|---|---|---|---|---|---|
1 | JONAS | SALK | M | 1914-10-28 | t+Yg6k4aOm5xMOMjT1nUCVbw1xM6mITKRx/APB+oU0dNo/AN2q/p20Pu2fiKd4wX5iFVK119DJAHYkFJYuI1BxgLBrzkiQKdEJKn1kMzA6k= | AWkmGR88bq2Fm/OJpcliqpYxYl43BQETABQT53Y4F8m1nJBnsPJIrQECXjC2qwiX47TI78GHDTuNNu2Sw1r6UAYILDQyFu9swKwcwf99C68= | Njlxk7k6GnIHAeV5rK43FOas0JrIB7SYd8xkRRhJikvR+OxYSmpwJyHM5tEc25+srlFOekqx6MTPDMQwUMQ/wxx33ETla7Q3po5Ypjf8Z7g= |
1 | JONAS | SALK | M | 1914-10-28 | t+Yg6k4aOm5xMOMjT1nUCVbw1xM6mITKRx/APB+oU0dNo/AN2q/p20Pu2fiKd4wX5iFVK119DJAHYkFJYuI1BxgLBrzkiQKdEJKn1kMzA6k= | AWkmGR88bq2Fm/OJpcliqpYxYl43BQETABQT53Y4F8m1nJBnsPJIrQECXjC2qwiX47TI78GHDTuNNu2Sw1r6UAYILDQyFu9swKwcwf99C68= | Njlxk7k6GnIHAeV5rK43FOas0JrIB7SYd8xkRRhJikvR+OxYSmpwJyHM5tEc25+srlFOekqx6MTPDMQwUMQ/wxx33ETla7Q3po5Ypjf8Z7g= |
2 | ELIZABETH | BLACKWELL | F | 1821-02-03 | E4G7QvnP21Q7Wp71rTE7uMWEkB03SrFuJ4rhLBPrAx4IS41dAYLWIRsPeGkzlgmVmYrCa3rrABZPvAsKnUAeE+FHpJhUPacdfDYsBQuGu5I= | VTY0gtsgUZoIxfoFXzwchDqFkofPZdylJlszvW8g1OoFXCrpCFzLLxk0SdNkEXTBdNof0lA1Dqk9NPIBOZcW8UfcQjGlb66Jo4TRvc9kXUo= | bOTIQg+gcOQ0NToyORzUJrf64oRMlHHRZBQPDueJzHBl1tIzq6l9Y/plH1N9K7Gy3FVSPsqNl+6SWpkgTWKinJKEQLt2wiCVbLgAw86Vc/U= |
3 | EDWARD | JENNER | M | 1749-05-17 | 4wGL1/yszKMjW7E3d2pk5fTcWLAKDp/4Dc03sIL0DG27Cu+QZUYYMf8pIyBwL/6ZMPhmr2X9QsOLTaq05ri09g6zB8HhCDuHUtRavdM0x1w= | 4j8xonF4Lcu8dpzcKwaw9UD1Lxuz5AcZv/fOnHwZhVifVDsWNEKVvyem3s4zqb8t6WeNSsC/rd6MYWKtZp5uvbq4pnqmC4gf1cwJa1E7dc8= | 7XIFi7mFqbvKK5NhZHSz6ZWIoL7ZyOP2QtHxSfhwwhThSnfe2deudM5hb+3RAc7r1Osb8wXd4mkqu9zlY1CUHc6HM2eah7TYCGHGTeLgRV0= |
4 | ALEXANDER | FLEMING | M | 1881-08-06 | kxwqjyikxC7WNxuX7cHCZp+Q7XEG5IYuVEshHi4RsyLgjM3lHHhi60Z3v5IpxKznFc+QNdea0S1vUyqyXDcbmzP597l91ZR4da7+cqA2Z4Q= | GD/IeZRqsRYWn/D8YfTw4FS+gT6bAM4oPvHlswcrc62n3KBH1ungFhTdHmYZXnOlsTVrFBluxcv5qUNCxDrxMgczN3StZZd+LS9YaSb+fsQ= | 00MN/EeN8M2ILTLLdgw0qNwjDfyBOROoKZydE05dXlPlv7Y+JVuoOpFdzlSu3KF4gTRhyw8B8qBWO7uwq5wAHS+poqtP/sHf4xqcCZ8WI4Y= |
Transcrypt Tokens As Sender¶
trans- (latin: across) -crypt (greek: hidden, secret)
The OPPRL specification also provides procedures for facilitating the safe transfer of data between trusted parties without a secure connection.
The process of Transcryption is the re-encrypting of data from one scheme to another without reversing the initial encryption to recover the PII. When the sender transcrypts their tokens, the token values of the records no longer match to the sender's data and records within the data being shared no longer match with eachother regardless of the underlying PII.
Notice that the first 2 records pertaining to the same subject (label = 1) no longer have identical tokens.
from carduus.token import transcrypt_out
tokens_to_send = transcrypt_out(
tokens,
token_columns=("opprl_token_1", "opprl_token_2", "opprl_token_3"),
recipient_public_key=dbutils.secrets.getBytes("carduus", "MyPartnerPublicKey"),
private_key=dbutils.secrets.getBytes("carduus", "PrivateKey"),
).drop("first_name", "last_name", "birth_date")
display(tokens_to_send)
label | gender | opprl_token_1 | opprl_token_2 | opprl_token_3 |
---|---|---|---|---|
1 | M | fUxhkLG+G9blbk/zH7TFlIQy23OjevfCmYlb1yMS85NvcumQLLpKiz7QcTaFsWE9CB80gvzFI6i4m4By+iGy8xDTCSaxry7BkWr5L3jn2T5mcf1ZpSOtlNJcWS9Ts5rVbigsO8EsWJSTn61o78VbOb8YZ/2p7AT63dUqqCBM7pabv9MH2I2QIzjzwgEZ0C9BmRmsXkkZ4JMstaJu+lIWZyyMFzBKe471X5rRo4HL0FxT+VSMdXz/IFeNSAImJCtBglEfCMZhfCMqU1MKG+BsqXK1mhujmm20neIwJMOqnIueG3pWKXtcn/btcaFUnwzTs793L3lZt57mrHmiJTLLVw== | U7uj6e8Y5qBanGmZsPvV36cA3zhoH/kWyTv/ffC+aRlrzjUyQe4jc9bJQ+UG9OsdOwcrcr1OI+EoovhqGBpJOCgWud4tWz9SPQMRh2Cf54qbG+ODYWCxKSTpPwJ7+SHPf+8iKebVEK7wT6iYaw6GS5OLqxYKL3hHcpZupV8bu5GZfzGpeqNl8XzwE1A/H+pwrusZziRbMgoRIaW49VnrKH3Z4R5GbH0zwUEZTSN3rguljNQgtG1RqL/njy/RtQo7wh7jusDA5GHvd7X0tCDCxN/VP6NkSPj5r/xLXvgcPQwLEbG1UxR7sUldFxzl0TnLIWJNmi3fJAAbSu10dP3pTQ== | jGnVejN4ODaSh+V0jA1rOrj4lpFXYprnZ+SFP63oFWNjmDudsRo1KIGTxSXi977j8OXM6HTFBUMS3PzVGvr+ixDx7VuEEJ/QfiTI5XLDlyim71sXn1YzDn0hSDFPHEF39vAL5M5oii6uCEsdEroTccfOgWQj6J+F/GAPwL7otxa5X7S7ZPJqMDTQ3E8VmLQSFImwG19SaTdxcFFwFJ4bfR0DaIIvb8/e/RrRT4oHSqcoZDIHo4jHS/7DaM+QwA9/hulErOfVUKPDqtpBgs88sL/MwHgXtkNo2xsK0mQ4hlVkETbMv4amgrnFtIWAVUx/ngT6jt1dAtbH0P8x3p0V0w== |
1 | M | gk0V1PNQ8fsPtqOm4tPEQY0M1Dic4o3l1oO/cS3XTHdujoKOmAR1sDfdvrGboNfR3hmmLCgSfQc+n7kxvbGKkMIRfc/DMcBCaYgr+3vVyE+N8d5kXd6hZE1WrCdWOgj1prxk/70Hk53n05kLQxppLney2NSgkBj6FwrJ9VXrbCm4zqTAwlwWdNBweX+7qRhuMbxUiXkChx52OqgPGdpIvMFNAJVtL/B11/3hIgh3soUKncKXEv8HChs18wJbJHrHLRRwcZS19DTctaJkKQxqqqMOlzYHAGP6lVczQGIaUh7m8Y0JCfYNpkg+3YATRf1EOOmAmgrYYuJLt4l/BHVKWg== | HsSe2Dj8OjRUFzWEaTBfd46mEY4kf/KBvNfwY+pe2btJ2ES6mTaXt4QS5bRw8HyMmazjUF8Hz0pBnZZGfO236mscDvv8TaK5cRrn5HROXBxVXdpb/fz8CumggXKobY33iuk8bD97NpA6R4fwTnphtQc8E9ofrW1tsecFHYJNFO4HefjlKaeS0x/dQ9bEU1iD/n7vtNZVBp5jNQuLktSjTer+tMACvdrDPLZFL5hOSa0nOuA32+g9GYQD5pZUJ6r4LM87IHXF+ggf/Q64/aeDzFVIGiznsqsKg/cUm9msrY/thcOvYXWjb1r1XND3d/z3zjInKcTZ75JgVwLoXQbVIQ== | dJDc+FiR8PZ9eco5WUBNUQ/Mnp3WRBmJOgvDUwNi09fA32Q5CvOQtfGaV7fqK0meFdP2OM7/XBAr4ZZhvp7UG/SHiZkOYTQxyKUgEAFtuEf9MpUamX5fA4M11h/bE95YPfabkuw6R52Ghxmg3yvQCM8Mv8JZjYJU416ko88+TjaMv8TKGM5mRxhpfkfOrdAgUENFVCLAi9Q95Glu26Y8EXuVqHAYfdILFe9XTpaM9BtZbfTPgFs7yPAtO/RqHVRGMGnuGwSNLhBViN/QV1uUys5tuU5yFT9MzM9VBifwHIpiGvl0l+j9fwZclergwuf3e4vGuFMnQZmDCuMAdzRsbw== |
2 | F | QXS7khXYHRoNrE5Oqpy04c9+PtrOrSrgGtzje8cMVDuBTH63gsJxqeHYKk1IXaiRcniFtDaQ4M4+lOuMlnPNhMihd5kxD4fMUarL5XffQtBFr8H3gQDRbSV0kxFPJHVqtXBWO2pNWe6DN/cYJR7XtGSwY0dyWXWTRhkt6YCc5DP6uah3OUbKQEzCgONYXdHDyn7qsdOprz6EU15e2xSwfrnfH8ysYfAAHU/gQKJhZXmXtPqt5vzkXSm6H2A+4n/+AY/Nd/Qz9kl4zrqzWsctyzqDHLtw01O4w60bNV4lekYLkCBOOManC5wHwAni3PxtE5XzA4HJqHXKiBCAWAzpLg== | ef3MO6eLNr8wHv9CMYXSbbEj8e+CUwJMbVVQql0Hsj/3V99SXflIP/Bec7iOaeX/nymKyAHaDkDjimp8EZqeboPa9p8BVViW0n1Tor6XRW7WRsmHjwLcKyJ1IGgRk08rO6PdLTUcHRAQXtsvlJuFq3EabqZk3ZliZcmljpVij3MKsnQ8VVYKZYGz17yUzgUIOXMT1C3Ouw0SjSgViI5vEQj0nTY9NrZyIN5FS4v4tf0x/Lm71uXpiiVULGEm1eHpV+I1UiP0J2O/fIUd50LoDIfC0G8dZXSoak55Rc1HI8F2Y6dyavuxi2t/k+bF8A6fFuy7vk9WEsKPiyDZ8AcN6A== | WD9jVu0cbXhq4p7NQbI2vhFCdvU4GI/kHUjTmXzp7OLY7BvEbTNxlDvrEbeIkoxm8y/fcygVk3ioMMhZ81pFy+MxE2XlfsjWvVCers2Ltw+ua2dTS14FKRp8MYiFj7516rYt81hPINBK8/DzW9jtXqUdDWdHiivZDUBz8xPK+hmUuPdm8ppWPNelHS6autmjb1jBB6bEwKK38MEEkZRbE6Uj/78usd9f7YV2+HcTRy2jg2V6LcdxW8i0fNb5cJAKzKCabGt6r9WLISz8VYKJPNFMgAegpnywaPqAoBgBqFaqp2F8q0g/UTp3uxrQjKEtSVJ9i9ggVuhAK+Gwcfs1XA== |
3 | M | Hpp7B0EB4UvIxDYRbRVHXnhZL2ZgLNh9ALK7VsoQX276BsC4CfqwC0BLchSnHNEAj/6YYT3qsqg4AGbab0RnPfAjmBj8i8pBmndZK2kicyUbVKhzXABO5kEjBVPCMz/7g6fIysLHczHg3kd3cibyAyQTKDkYyoVhnwxG9Izj0aKBi5HzqDAkKopDwz/I228fEggoN39Pd8pbjdx2+a0BEFvJeI7m9twRsd26GxBLrXuVH6EGAVu/vDUEge2f8gCT9CaFCQq5JbyLf+uqxDBU2/GHT0IcEENtHpFCFNP3JHtnOPbICUPaZhpkc5u0TEz7O41oklslvQ/w8fEh7aNnfg== | T/DoAbgx27LunmbwlY3WcTbAwYJRQeOEx6E0KSdvzM+n5dhMB39uWAdu9qilR4xnqw2+Vbt1dP+/M4q7tTOo00w990IVdHfaPAOTU/53YmjgkKqT/VzG9MZYIx8tqr3MTSmvwOtzz4yXKF3mtCg+xWI09zP96WYktLsbZKenZwNsHXtyAa3Zl5/FAas4Q8CTFg3F8xN+joKZCyWZVnnTRTG5SCyJW7M1dlr+nMMm9Awee6NvpgYBXHKfLWwk+aMJxpcR/311RUvQUEMuc52L7hsAHF+t8r81mIysGIuuKaCAR29KtYKNmYA2dxI0hJfs1lvLOmAVfxm6lSifAsgg0w== | Ya2uz/a8dEV7D4X95pd/qfUEmkb8nMIEsTzCCdwHm6OkkNO++O0bjAlCuCeMcNVxAEBw6RtW3QGZH87/j3EWCzyUEGSS/ZRQbfcDt/BSx6YCEjA/ZfEHrpKWw/fSoLq8kO8X3CNzkB3T40rCtbp3c+376Qg0EswgquLvWxhLgBeRnDuM5jh/PKuvxPz3GBIP6JKVKy3L3tBdxv+tw2RqlUmbIh2/El3xT6K6jp0fOtcdI61tKY9B2mYoncGF9jyzZo8Mg/dr9OLbswhV6DLYeUbnJN8osT54EQ5dtdwZ6e62BsSSzzBQkpgrAA8IHMaiiPKhtMJJvr88HjHMN8zbrA== |
4 | M | a7ajv6ohrH3dZw12jt5CkSyOKhCqpvb0RGP4t8c58BWm0VrjbU2nzOwp1tXtJxOnH7uA1v7kyGbyv4sRY4X7rP3xzVVcgISygpdcfD+aF8ZdwTeHYHouwbgw8S6Ms0lIHNKKgZI74Wxj/m/WU6qt1ZVee8RYpb4a0O/5DbUBIC69Mg4QNHB0yjPfBXnf+b9ZdjfX58uQWEVAvLrE+hxEX73TEsOa5HVr+49FRdMq9zJy4YC8tjoOheRrhsb6oArIzZFqL9k2sc2SigJsdqWJMi8dAZFu2Xiw1nLPPWb/7WlyEcTjtu83BLfxh8Mif5xmh75FuoplA+6CRM863l7Zzw== | K8wHTWPPDLBouAH0brBmbPLBMQyUWzPEB1ifobca6lWIwIoy/H0zPGvStngqOTfNcHkO00TkU691gGZbKP5PluCz11onslBJXYhATxx03WpbWXCai/uyb9fmXRwmWxFXNhrZ9gaVhPDhnQ8JuQT35bcP4XckcmNNOlz2z5KG11d6icJ4ExkdZWFDa/9KZqOekYWmbkZJB4Z6kFBfn1RmnsiLWIG+iBV4vcG9XDIu3xECt4cg69jog4nzRKXQXZsLsDAhCkZ2dyGTOXERlZp4H3+gklwuA7rUWG3/EpBFlzsbhnx4J7E40CGl3Rlq6PAu7oiMNoODsCFqyuRhIC2RwA== | dk96GXnw9B5qaDHsOK90Dhg0ErenPL+9zY34ikYk+clTFFujgG9SJnKIhxYJQ1vsH5ExntoeY5BJdt5keUFZm4jN6XPI981p4eNdgtFPRdFddqVFBd/OotfDhoW9S9WtEKPKvKAbJZBmYhVXLvxKS95LkYhWeklIQQ4WxU4mEgVmgKQUUy115Wxy3N4jwFErJocrCnHPmfv4d18a0ch848ie1LXp3HnfRKaIWwNkZSxeDrofjHJxsGa3ETZcTPMHfYCdvkfC62mZKXBbVgZz2CPoD0D1iCN6wUeVHA3FpKg87mD30J5jVCoa93oq+LEBgMqgsJqrpxCdSO8wdT7Aaw== |
Transcrypt Tokens As Recipient¶
When receiving the data with transcrypted tokens, the recepiant must call a function to re-encrypt the tokens again so that they match with the rest of the tokens across their data assets.
For this demonstration, we will load the encryption key our hypothetical recipient and provess the transcrypted tokens we created above.
Notice that the first 2 records pertaining to the same subject (label = 1) have identical tokens again, but do these tokens are not the same as the orgional tokens because they are encrypted with the scheme for the recipient.
from carduus.token import transcrypt_in
tokens2 = transcrypt_in(
tokens_to_send,
token_columns=("opprl_token_1", "opprl_token_2", "opprl_token_3"),
private_key=dbutils.secrets.getBytes("carduus", "OrgBPrivateKey"),
)
display(tokens2)
label | gender | opprl_token_1 | opprl_token_2 | opprl_token_3 |
---|---|---|---|---|
1 | M | n6YNZapNFyiP3rPO6JmlNZnxfz4PL9lfn3IOoERk9cSLe2w91EDdKHL+nNgZ60SzeRQzcM3nHDRQevzOfkFLf4dJTJg+8uLtcqHa3sHn0D4= | gcbO8zyzdiMsWiWrChcskBcPHJ4z04CmZ4DrY+V20n6XaSpq8mJQ41um85gwSEcoOdIYE2ucFPn9mf3QKmNnBrRTYoA1q9KkSWK/Z6etbMg= | AaGOVbbebDiOSq/lMVORAyeEHf8RcfrvPF7SCNei1wcSK7IJph22uxOPnfmmNNpAwzo9Q6Tr8XYOXwxgOYY7JG6NcX9O/1qVSGvEq3wb17E= |
1 | M | n6YNZapNFyiP3rPO6JmlNZnxfz4PL9lfn3IOoERk9cSLe2w91EDdKHL+nNgZ60SzeRQzcM3nHDRQevzOfkFLf4dJTJg+8uLtcqHa3sHn0D4= | gcbO8zyzdiMsWiWrChcskBcPHJ4z04CmZ4DrY+V20n6XaSpq8mJQ41um85gwSEcoOdIYE2ucFPn9mf3QKmNnBrRTYoA1q9KkSWK/Z6etbMg= | AaGOVbbebDiOSq/lMVORAyeEHf8RcfrvPF7SCNei1wcSK7IJph22uxOPnfmmNNpAwzo9Q6Tr8XYOXwxgOYY7JG6NcX9O/1qVSGvEq3wb17E= |
2 | F | jSXy3EODCctLC6bYmp4PLwTyvIlruiSHVJG+PzkrffKsypBII409DuMDq+20mCoQIOnqZXqChkUNhrqiKPQX+E3wfFQWiJSEPk/HlHYKH7U= | w1XiSj7vz4R68DpkQxMqSfFikQvylx3ZPrOoBQWBH3CypDgShSmw+6tC5Ows+hKVZkan+13letcC4u8ifO612dlg63hYZ/ONfTSGevD14ao= | 5Fk8udPfGm44jIDkFbhRJYIQnG4jjgpMhygEX7eHacpy1fCnuOMJB/5IP87570eISwYWqsRSuDna8iRqywrj8KTiVbMoY7p8Mg7Je3uFGKY= |
3 | M | 5etiSY9RzaVmpefeRGR0ajR08O6aez6ZrJUoAKrfIrNzOPHLAQqi4tXIp+4kvIhp4YygsXjleWirtfs8hbl0fwFWeGQ4vUBBsXUY0jG8MlU= | WJ340Z+/qZy/5yTvABqJMYgrkTzZtXdHmKW/OduRiIpidBgVF8iijUfi/+M8nOKnUkgFbsCrqFtI57XP5GNuAvFmptyRS6EqPbOupF8PcNE= | xnR2qS0zGdfBZrmBomMJezAPv/UYCQdpW0nFV7KiZotpYG5Q/d9EK5Ew27SaFP2dJNNVCwCEXCV+jM+RBXIuMnxbVWvy8FjPODx3UR5Qrrg= |
4 | M | tfhWByB2vs7mTwVf1xQZ7n4KVf9U9JqdeINK6DJtMj/7msCQVAljCH+X58pbdjLfMDZlrMHZtiVjpl5i9nfrcj/iou5IClUnycIhupaT8JM= | wBgFLcQj/QwyWe7YXrnHvNXoMQvNOmaBFi6q0gMbVSuO1f8xQk8fXoBN18/SH50amlmLs+4GV1eYatY+rvVEFVb4RJMSds91ZH/yjyLIkTw= | /OfhKKqUTgAS+vABrSVG9sW0CrpiJmHDkWQ9fTOBAKASwsY9IDnBufIoKSs3SXjr24rYBvdFWMLZWAGfVukz91/yy39MsgeX7MJERxHigAQ= |