Spindle-token Databricks Getting Started Guide¶
Spindle-token is an implementation of the Open Privacy Preserving Record Linkage (OPPRL) specification. It allows organizations to replace personally identifiable information (PII) with encrypted 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 spindle-token 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 spindle-token encryption keys by running the following command with the Databricks CLI.
databricks secrets create-scope spindle-token
Add your private key as a secret in the spindle-token scope.
(cat << EOF
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
EOF
) | databricks secrets put-secret spindle-token PrivateKey
Add the secrets for the public keys of the third party organizations you will be sending and receiving tokenized data to/from.
(cat << EOF
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOF
) | databricks secrets put-secret spindle-token MyPartnerPublicKey
Optional Databricks Compute Setup¶
Users can provide encryption keys to spindle-token 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/spindle-token/PrivateKey}}
SPINDLE_TOKEN_RECIPIENT_PUBLIC_KEY={{secrets/spindle-token/MyPartnerPublicKey}}
Install spindle-token¶
You can install spindle-token on your cluster, or within the notebook. See the Databricks docs for more information.
%pip install spindle-token
Tokenization of PII¶
from spindle_token import tokenize
from spindle_token.opprl import OpprlV1
tokens = tokenize(
pii,
col_mapping={
OpprlV1.first_name: "first_name",
OpprlV1.last_name: "last_name",
OpprlV1.gender: "gender",
OpprlV1.birth_date: "birth_date",
},
tokens=[OpprlV1.token1, OpprlV1.token2, OpprlV1.token3],
private_key=dbutils.secrets.getBytes("spindle-token", "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= |
Transcode Tokens As Sender¶
The OPPRL specification also provides procedures for facilitating the safe transfer of data between trusted parties without a secure connection.
The process of transcoding will re-encrypt of data from one scheme to another without reversing to the original PII or emitting any intermediate values. When the sender transcodes 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. The tokens produced by the sender with transcode_out are called ephemeral tokens.
Notice that the first 2 records pertaining to the same subject (label = 1) no longer have identical tokens.
from spindle_token import transcode_out
from spindle_token.opprl import OpprlV1
ephemeral_tokens = transcode_out(
tokens,
tokens=(OpprlV1.token1, OpprlV1.token2, OpprlV1.token3),
recipient_public_key=dbutils.secrets.getBytes("spindle-token", "MyPartnerPublicKey"),
private_key=dbutils.secrets.getBytes("spindle-token", "PrivateKey"),
).drop("first_name", "last_name", "birth_date")
display(ephemeral_tokens)
| 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== |
Transcode Tokens As Recipient¶
When receiving the data with ephemeral tokens, the recipient must call a function to transcode 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 process the ephemeral 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 original tokens because they are encrypted with the scheme for the recipient.
from spindle_token import transcode_in
from spindle_token.opprl import OpprlV1
tokens2 = transcode_in(
ephemeral_tokens,
tokens=(OpprlV1.token1, OpprlV1.token2, OpprlV1.token3),
private_key=dbutils.secrets.getBytes("spindle-token", "PrivateKey"),
)
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= |