Accepted
Selective disclosure allows for the holder of OA documents to present subsets of the document to be verified. It does so by allowing users to obfuscated data fields without compromising the integrity of the document.
To achieve this, we compute a hash over individually hashed and salted key-value pairs. During obfuscation, we will hash the salted key-value pair and store the hash in another location of the document.
During integrity verification, we will recompute the hash of the document by:
- Nomalising the document into individual key-value pairs
- Hashing each of the individual key-value pairs
- Compute the target hash of the document
In this document, we will describe the steps to:
- Prepare an OA document
- Obfuscate a value on an issued OA document
- Check the integrity of an issued OA document
To generate an OA document, through a process known as document wrapping
, each value is converted from its primitive type to a string with a UUID, the data type and the original value.
The UUID prevents rainbow table attacks on the value after it has been obfuscated and the type will help disambiguate the original data type.
Sample raw data input:
{
"reference": "document identifier",
"validFrom": "2010-01-01T19:23:24Z",
"name": "document owner name",
"template": {
"name": "any",
"type": "EMBEDDED_RENDERER",
"url": "http://some.example.com"
},
"issuer": {
"id": "http://some.example.com",
"name": "DEMO STORE",
"identityProof": { "type": "DNS-TXT", "location": "tradetrust.io" }
},
"proof": {
"type": "OpenAttestationSignature2018",
"value": "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d",
"method": "TOKEN_REGISTRY"
},
"key1": "value1",
"key2": "value2",
"key3": ["value3.1", "value3.2"],
"key4": { "key41": "value4.1", "key42": "value4.2" }
}
Output after salt:
{
"reference": "2cc91073-3863-4db6-9f1a-b68665088caa:string:document identifier",
"validFrom": "ca425b5c-d25d-4a36-b0f7-e368fbbaf75d:string:2010-01-01T19:23:24Z",
"name": "c6864a9b-0367-4b8b-8cc5-f59cb6331f3d:string:document owner name",
"template": {
"name": "36a95964-fbe9-4c6d-ab6a-a5247c07f105:string:any",
"type": "45c266d0-3efe-4841-893c-96723cd7ddb5:string:EMBEDDED_RENDERER",
"url": "bbec168c-b9c3-4fb3-aa6f-3d1a1017f1e9:string:http://some.example.com"
},
"issuer": {
"id": "42b6991b-e4b5-444b-aba7-7ce97bbe8441:string:http://some.example.com",
"name": "da3fa13d-b0a9-4357-bd4c-4f3941304225:string:DEMO STORE",
"identityProof": {
"type": "e2039c5b-6cab-4679-9150-7266e34371a4:string:DNS-TXT",
"location": "684bb30d-a59e-4cc3-a8ed-44e6b72579d9:string:tradetrust.io"
}
},
"proof": {
"type": "bc64159e-90f3-4f0c-99bb-43818ff402e5:string:OpenAttestationSignature2018",
"value": "edd73480-3043-4cdc-aefd-f4ee21fc6a7f:string:0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d",
"method": "6ae5877f-be97-47df-99f7-7464a5ca1cde:string:TOKEN_REGISTRY"
},
"key1": "c1ba53a6-68b8-421e-ac80-7d4b71ca8e52:string:value1",
"key2": "0356d74e-10cd-4cc1-b1ba-eab23f8bea3d:string:value2",
"key3": [
"44faf2a4-d16f-4945-8784-d2b8a088edc5:string:value3.1",
"18eaf71f-3b4a-4064-b8f5-79092c2e04aa:string:value3.2"
],
"key4": {
"key41": "7e3b72f8-e521-4892-a26c-b56d3e2ab5cf:string:value4.1",
"key42": "c4f96a54-dda6-4fe2-9f49-bc77e8e19d02:string:value4.2"
}
}
After the individual values have been salted, the entire data object is flattened, while preserving the hierarchical structure of the data within the individual keys.
In our implementation the library flatley is used to flatten the object and js-sha3 is used to compute the keccak256
hash.
Output after flattening:
{
"reference": "2cc91073-3863-4db6-9f1a-b68665088caa:string:document identifier",
"validFrom": "ca425b5c-d25d-4a36-b0f7-e368fbbaf75d:string:2010-01-01T19:23:24Z",
"name": "c6864a9b-0367-4b8b-8cc5-f59cb6331f3d:string:document owner name",
"template.name": "36a95964-fbe9-4c6d-ab6a-a5247c07f105:string:any",
"template.type": "45c266d0-3efe-4841-893c-96723cd7ddb5:string:EMBEDDED_RENDERER",
"template.url": "bbec168c-b9c3-4fb3-aa6f-3d1a1017f1e9:string:http://some.example.com",
"issuer.id": "42b6991b-e4b5-444b-aba7-7ce97bbe8441:string:http://some.example.com",
"issuer.name": "da3fa13d-b0a9-4357-bd4c-4f3941304225:string:DEMO STORE",
"issuer.identityProof.type": "e2039c5b-6cab-4679-9150-7266e34371a4:string:DNS-TXT",
"issuer.identityProof.location": "684bb30d-a59e-4cc3-a8ed-44e6b72579d9:string:tradetrust.io",
"proof.type": "bc64159e-90f3-4f0c-99bb-43818ff402e5:string:OpenAttestationSignature2018",
"proof.value": "edd73480-3043-4cdc-aefd-f4ee21fc6a7f:string:0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d",
"proof.method": "6ae5877f-be97-47df-99f7-7464a5ca1cde:string:TOKEN_REGISTRY",
"key1": "c1ba53a6-68b8-421e-ac80-7d4b71ca8e52:string:value1",
"key2": "0356d74e-10cd-4cc1-b1ba-eab23f8bea3d:string:value2",
"key3.0": "44faf2a4-d16f-4945-8784-d2b8a088edc5:string:value3.1",
"key3.1": "18eaf71f-3b4a-4064-b8f5-79092c2e04aa:string:value3.2",
"key4.key41": "7e3b72f8-e521-4892-a26c-b56d3e2ab5cf:string:value4.1",
"key4.key42": "c4f96a54-dda6-4fe2-9f49-bc77e8e19d02:string:value4.2"
}
Once the data object has been salted and flattened, each key-value pair will be hashed.
In the example above, we will apply the keccak256
hash on the object {"key4.key41":"7e3b72f8-e521-4892-a26c-b56d3e2ab5cf:string:value4.1"}
which will yield c3edad333f0829b92a82cd3c09b7795b0f00f07dfbbfc5ff8779272d1eaba3a8
.
Once this function has been applied to all the keys, the output hashes will be sorted and stored in an array.
Output after all hashing individual keys (sorted):
[
"1b61eaed9b46a53c530ca7cbe1ed54621e9478b86efb445a932f3cb38d6a97da",
"28e72c3815a932b137f3dc0d9d9fa101d6d7477793acf5adcb5f34f0901c3811",
"2a73ae2d0e942748f632130c530e4d3eb0e91dcf8e560c52586ecf2cf84daefd",
"32559f7f05f498d2ffe89345a379fa6619cd20466474386b26ac8b328b28e03e",
"5464490fed778a23505a0da8cb8ffa8ebf47b85fa221f544d0757a96e5be315d",
"5c51bab9dda34200e24a5bf46ce39a82561515239f869d444cd7b458bb986483",
"6b19136bf4ff28c270b913786c329c6c1186857a5fb5b9897bc4bdf00f95041d",
"6ec1924f5f25afe82b01ed108775ab27125b0e7277687a1c1acef27ce34cb189",
"87da7c3a74118736144688664ffa621e7e39837c23719a0b4c88ee0fa0b08357",
"88c680973e4c58095b222aabfe2d62ffe8bf3a9dacccb6cea112805abdb2d475",
"91788b66ce30099e244d180bc18ce16b98de48598cd2ebf9f1cc7c508dc1b65e",
"9e029d96b11e4f4e4a37cb3bf03bf3af854b8ea059686945072f02c215e0aba6",
"a0b0a1e5992ad724ce101921001d9de2ff08a64c65d9e92b47142eb76fa03618",
"c04833e08f6a0d17cb8388db75dec3b07c603be118a1303f68332ae7eef26db5",
"c3edad333f0829b92a82cd3c09b7795b0f00f07dfbbfc5ff8779272d1eaba3a8",
"c6daa8acbd7b7591d6ea0055a6de3827b953ea3e295c024fc31c167cac1f112d",
"d8b26580983759e8cad2bcfd10dffdf375cd49b1e5d7b4424d7698216b683218",
"e3494dd41622c560a2df8cf9051f75a27ef651c46826e3eef6382187825794d5",
"f29d9f500d3843f547158fcd8b69b67406ad492621d9702a7903062f86ef97b0"
]
Finally, with the array of sorted hashes, keccak256
is applied again on the stringified array to obtain the targetHash
of the document.
keccak256(
JSON.stringify([
"1b61eaed9b46a53c530ca7cbe1ed54621e9478b86efb445a932f3cb38d6a97da",
"28e72c3815a932b137f3dc0d9d9fa101d6d7477793acf5adcb5f34f0901c3811",
"2a73ae2d0e942748f632130c530e4d3eb0e91dcf8e560c52586ecf2cf84daefd",
"32559f7f05f498d2ffe89345a379fa6619cd20466474386b26ac8b328b28e03e",
"5464490fed778a23505a0da8cb8ffa8ebf47b85fa221f544d0757a96e5be315d",
"5c51bab9dda34200e24a5bf46ce39a82561515239f869d444cd7b458bb986483",
"6b19136bf4ff28c270b913786c329c6c1186857a5fb5b9897bc4bdf00f95041d",
"6ec1924f5f25afe82b01ed108775ab27125b0e7277687a1c1acef27ce34cb189",
"87da7c3a74118736144688664ffa621e7e39837c23719a0b4c88ee0fa0b08357",
"88c680973e4c58095b222aabfe2d62ffe8bf3a9dacccb6cea112805abdb2d475",
"91788b66ce30099e244d180bc18ce16b98de48598cd2ebf9f1cc7c508dc1b65e",
"9e029d96b11e4f4e4a37cb3bf03bf3af854b8ea059686945072f02c215e0aba6",
"a0b0a1e5992ad724ce101921001d9de2ff08a64c65d9e92b47142eb76fa03618",
"c04833e08f6a0d17cb8388db75dec3b07c603be118a1303f68332ae7eef26db5",
"c3edad333f0829b92a82cd3c09b7795b0f00f07dfbbfc5ff8779272d1eaba3a8",
"c6daa8acbd7b7591d6ea0055a6de3827b953ea3e295c024fc31c167cac1f112d",
"d8b26580983759e8cad2bcfd10dffdf375cd49b1e5d7b4424d7698216b683218",
"e3494dd41622c560a2df8cf9051f75a27ef651c46826e3eef6382187825794d5",
"f29d9f500d3843f547158fcd8b69b67406ad492621d9702a7903062f86ef97b0"
])
);
Result:
"51d6b872aae578d6a4b7decd4370f50b73b5729d8357f3057e240c10bae64ab2"
Once the targetHash
of the document is calculated, it can be appended to the document under the signature
object.
Example of wrapped OA Document:
{
"version": "open-attestation/3.0",
"data": {
"reference": "2cc91073-3863-4db6-9f1a-b68665088caa:string:document identifier",
"validFrom": "ca425b5c-d25d-4a36-b0f7-e368fbbaf75d:string:2010-01-01T19:23:24Z",
"name": "c6864a9b-0367-4b8b-8cc5-f59cb6331f3d:string:document owner name",
"template": {
"name": "36a95964-fbe9-4c6d-ab6a-a5247c07f105:string:any",
"type": "45c266d0-3efe-4841-893c-96723cd7ddb5:string:EMBEDDED_RENDERER",
"url": "bbec168c-b9c3-4fb3-aa6f-3d1a1017f1e9:string:http://some.example.com"
},
"issuer": {
"id": "42b6991b-e4b5-444b-aba7-7ce97bbe8441:string:http://some.example.com",
"name": "da3fa13d-b0a9-4357-bd4c-4f3941304225:string:DEMO STORE",
"identityProof": {
"type": "e2039c5b-6cab-4679-9150-7266e34371a4:string:DNS-TXT",
"location": "684bb30d-a59e-4cc3-a8ed-44e6b72579d9:string:tradetrust.io"
}
},
"proof": {
"type": "bc64159e-90f3-4f0c-99bb-43818ff402e5:string:OpenAttestationSignature2018",
"value": "edd73480-3043-4cdc-aefd-f4ee21fc6a7f:string:0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d",
"method": "6ae5877f-be97-47df-99f7-7464a5ca1cde:string:TOKEN_REGISTRY"
},
"key1": "c1ba53a6-68b8-421e-ac80-7d4b71ca8e52:string:value1",
"key2": "0356d74e-10cd-4cc1-b1ba-eab23f8bea3d:string:value2",
"key3": [
"44faf2a4-d16f-4945-8784-d2b8a088edc5:string:value3.1",
"18eaf71f-3b4a-4064-b8f5-79092c2e04aa:string:value3.2"
],
"key4": {
"key41": "7e3b72f8-e521-4892-a26c-b56d3e2ab5cf:string:value4.1",
"key42": "c4f96a54-dda6-4fe2-9f49-bc77e8e19d02:string:value4.2"
}
},
"privacy": {
"obfuscatedData": []
},
"signature": {
"type": "SHA3MerkleProof",
"targetHash": "51d6b872aae578d6a4b7decd4370f50b73b5729d8357f3057e240c10bae64ab2",
"proof": [],
"merkleRoot": "51d6b872aae578d6a4b7decd4370f50b73b5729d8357f3057e240c10bae64ab2"
}
}
To obfuscate a value in the OA document, one simply hash the key-value pair with keccak256
and store the resulting hash in privacy.obfuscatedData
. This method can be used to obfuscate multiple key-value pairs in the document data file.
For example to obfuscated key4.key41
:
keccak256(
JSON.stringify({
"key4.key41": "7e3b72f8-e521-4892-a26c-b56d3e2ab5cf:string:value4.1"
})
);
Resulting hash:
c3edad333f0829b92a82cd3c09b7795b0f00f07dfbbfc5ff8779272d1eaba3a8
This hash will then be appended to the original OA document and the object that has been obfuscated will be removed:
{
"version": "open-attestation/3.0",
"data": {
"reference": "2cc91073-3863-4db6-9f1a-b68665088caa:string:document identifier",
"validFrom": "ca425b5c-d25d-4a36-b0f7-e368fbbaf75d:string:2010-01-01T19:23:24Z",
"name": "c6864a9b-0367-4b8b-8cc5-f59cb6331f3d:string:document owner name",
"template": {
"name": "36a95964-fbe9-4c6d-ab6a-a5247c07f105:string:any",
"type": "45c266d0-3efe-4841-893c-96723cd7ddb5:string:EMBEDDED_RENDERER",
"url": "bbec168c-b9c3-4fb3-aa6f-3d1a1017f1e9:string:http://some.example.com"
},
"issuer": {
"id": "42b6991b-e4b5-444b-aba7-7ce97bbe8441:string:http://some.example.com",
"name": "da3fa13d-b0a9-4357-bd4c-4f3941304225:string:DEMO STORE",
"identityProof": {
"type": "e2039c5b-6cab-4679-9150-7266e34371a4:string:DNS-TXT",
"location": "684bb30d-a59e-4cc3-a8ed-44e6b72579d9:string:tradetrust.io"
}
},
"proof": {
"type": "bc64159e-90f3-4f0c-99bb-43818ff402e5:string:OpenAttestationSignature2018",
"value": "edd73480-3043-4cdc-aefd-f4ee21fc6a7f:string:0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d",
"method": "6ae5877f-be97-47df-99f7-7464a5ca1cde:string:TOKEN_REGISTRY"
},
"key1": "c1ba53a6-68b8-421e-ac80-7d4b71ca8e52:string:value1",
"key2": "0356d74e-10cd-4cc1-b1ba-eab23f8bea3d:string:value2",
"key3": [
"44faf2a4-d16f-4945-8784-d2b8a088edc5:string:value3.1",
"18eaf71f-3b4a-4064-b8f5-79092c2e04aa:string:value3.2"
],
"key4": { "key42": "c4f96a54-dda6-4fe2-9f49-bc77e8e19d02:string:value4.2" }
},
"privacy": {
"obfuscatedData": [
"c3edad333f0829b92a82cd3c09b7795b0f00f07dfbbfc5ff8779272d1eaba3a8"
]
},
"signature": {
"type": "SHA3MerkleProof",
"targetHash": "51d6b872aae578d6a4b7decd4370f50b73b5729d8357f3057e240c10bae64ab2",
"proof": [],
"merkleRoot": "51d6b872aae578d6a4b7decd4370f50b73b5729d8357f3057e240c10bae64ab2"
}
}
To check the integrity of the document, we simply:
- Flatten the data object
- Hash individual key-value pairs
- Append and sort the hash from (2) with the hashes from
privacy.obfuscatedData
- Compute the keccak256 hash for the results from (3)
- Check that the resulting hash from (4) matches the
targetHash