diff --git a/.github/workflows/docsearch.yml b/.github/workflows/docsearch.yml index 5712286c..98d8557c 100644 --- a/.github/workflows/docsearch.yml +++ b/.github/workflows/docsearch.yml @@ -3,26 +3,24 @@ name: Run algolia docsearch on: push: branches: - - master + - master schedule: - - cron: '0 12 * * 1' + - cron: '0 12 * * 1' jobs: docsearch: runs-on: ubuntu-latest - if: github.repository == 'bloock/bloock-docs' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + - name: Setup environment + run: | + echo "APPLICATION_ID=$APPLICATION_ID" >> .env + echo "API_KEY=$API_KEY" >> .env + chmod +x ./bin/crawl.sh + env: + APPLICATION_ID: ${{ secrets.ALGOLIA_DOCSEARCH_APP_ID }} + API_KEY: ${{ secrets.ALGOLIA_DOCSEARCH_API_KEY }} - - name: Setup environment - run: | - echo "APPLICATION_ID=$APPLICATION_ID" >> .env - echo "API_KEY=$API_KEY" >> .env - chmod +x ./bin/crawl.sh - env: - APPLICATION_ID: ${{ secrets.ALGOLIA_DOCSEARCH_APP_ID }} - API_KEY: ${{ secrets.ALGOLIA_DOCSEARCH_API_KEY }} - - - name: Run docsearch - continue-on-error: true - run: | - ./bin/crawl.sh + - name: Run docsearch + continue-on-error: true + run: | + ./bin/crawl.sh diff --git a/docs/guides/products/authenticity/features/key-operations/digital-signature/sign.mdx b/docs/guides/products/authenticity/features/key-operations/digital-signature/sign.mdx index 85587ff3..4877c956 100644 --- a/docs/guides/products/authenticity/features/key-operations/digital-signature/sign.mdx +++ b/docs/guides/products/authenticity/features/key-operations/digital-signature/sign.mdx @@ -16,7 +16,7 @@ import SignPhp from '!!raw-loader!@site/examples/key/sign/sign.php'; import SignGo from '!!raw-loader!@site/examples/key/sign/sign.go'; \ No newline at end of file +/> diff --git a/docs/guides/products/identity/features/create-holder.mdx b/docs/guides/products/identity/features/create-holder.mdx index b85a15e0..34e73f5f 100644 --- a/docs/guides/products/identity/features/create-holder.mdx +++ b/docs/guides/products/identity/features/create-holder.mdx @@ -20,9 +20,9 @@ import CreateHolderPhp from '!!raw-loader!@site/examples/identity/create_holder/ import CreateHolderGo from '!!raw-loader!@site/examples/identity/create_holder/create_holder.go'; \ No newline at end of file +/> diff --git a/docs/guides/products/identity/features/create-issuer.mdx b/docs/guides/products/identity/features/create-issuer.mdx index ea4c7c4b..2d7a606b 100644 --- a/docs/guides/products/identity/features/create-issuer.mdx +++ b/docs/guides/products/identity/features/create-issuer.mdx @@ -6,10 +6,11 @@ sidebar_position: 1 In order to create an identity in BLOOCK Identity I will first need to create or have a Baby JubJub (BJJ) key. This key will be very important to have it saved because it will be used to control our Issuer. You have two options to get this key. + 1. You can create a BJJ key with our managed key product ([BLOOCK Keys product](go_to_keys_documentation)), your key will be identified by a UUID. This identifier will be enough to control your Issuer. -`Example: 6f36448d-49f3-4b0e-aa72-6e55863302e8` + `Example: 6f36448d-49f3-4b0e-aa72-6e55863302e8` 2. If you have created a BJJ type key locally, i.e. you are aware of its private key, then you can use it to control your Issuer. -`Example: bf5e13dd8d9f784aee781b4de7836caa3499168514553eaa3d892911ad3c345j` + `Example: bf5e13dd8d9f784aee781b4de7836caa3499168514553eaa3d892911ad3c345j` ## Examples @@ -22,7 +23,7 @@ import CreateIssuerPhp from '!!raw-loader!@site/examples/identity/create_issuer/ import CreateIssuerGo from '!!raw-loader!@site/examples/identity/create_issuer/create_issuer.go'; \ No newline at end of file +/> diff --git a/docs/guides/products/identity/features/credential-offering.mdx b/docs/guides/products/identity/features/credential-offering.mdx index 763c4faf..0f61185e 100644 --- a/docs/guides/products/identity/features/credential-offering.mdx +++ b/docs/guides/products/identity/features/credential-offering.mdx @@ -3,9 +3,9 @@ title: 'Credential Offering' sidebar_position: 4 --- -The credential offering process refers to the transfer of the Verifiable Credential (VC) to the assigned user or Holder. -It requires two agents (Issuer and Holder) and a [wallet or software](#what-is-a-wallet) where the credential will be stored. -At the Holder level, this is done by scanning a QR code generated by the Issuer from the Holder's wallet. +The credential offering process refers to the transfer of the Verifiable Credential (VC) to the assigned user or Holder. +It requires two agents (Issuer and Holder) and a [wallet or software](#what-is-a-wallet) where the credential will be stored. +At the Holder level, this is done by scanning a QR code generated by the Issuer from the Holder's wallet. At the protocol level this communication is highly secure thanks to the [DIDComm protocol](#dIDComm-messaging) used and the authentication and identity proofs mechanisms. ## Examples @@ -19,9 +19,9 @@ import CredentialOfferingPhp from '!!raw-loader!@site/examples/identity/credenti import CredentialOfferingGo from '!!raw-loader!@site/examples/identity/credential_offering/credential_offering.go'; \ No newline at end of file +/> diff --git a/docs/guides/products/identity/features/credential-revocation.mdx b/docs/guides/products/identity/features/credential-revocation.mdx index ac6ea0ed..d6cbd749 100644 --- a/docs/guides/products/identity/features/credential-revocation.mdx +++ b/docs/guides/products/identity/features/credential-revocation.mdx @@ -3,7 +3,7 @@ title: 'Credential Revocation' sidebar_position: 5 --- -Finally, Issuer may decide to revoke or invalidate any credential. Therefore, you will have to execute the [revocation](#revocation) process. +Finally, Issuer may decide to revoke or invalidate any credential. Therefore, you will have to execute the [revocation](#revocation) process. Having a credential revoked what it means is that the Holder will not be able to generate any [proof](#proofs). When you revoke a credential what we are doing is generate a new [Sparse Merkle Tree Proof](#proof) of revoation. Therefore, we need the [Issuer's state](#state-transition) to be processed once the revocation action is executed. And obviously this state transition will be marked by the [interval](#issuer-intervals) we have set. **It is important to note that the effect of the revocation will not be visible until this Issuer state transition is executed**. @@ -18,9 +18,9 @@ import CredentialRevocationPhp from '!!raw-loader!@site/examples/identity/creden import CredentialRevocationGo from '!!raw-loader!@site/examples/identity/credential_revocation/credential_revocation.go'; \ No newline at end of file +/> diff --git a/docs/guides/products/identity/features/e2e_examples/e2e-managed-key.mdx b/docs/guides/products/identity/features/e2e_examples/e2e-managed-key.mdx index 772d4aaa..81b2d132 100644 --- a/docs/guides/products/identity/features/e2e_examples/e2e-managed-key.mdx +++ b/docs/guides/products/identity/features/e2e_examples/e2e-managed-key.mdx @@ -14,9 +14,9 @@ import E2EManagedKeyPhp from '!!raw-loader!@site/examples/identity/e2e_examples/ import E2EManagedKeyGo from '!!raw-loader!@site/examples/identity/e2e_examples/e2e_managed_key/e2e_managed_key.go'; \ No newline at end of file +/> diff --git a/docs/guides/products/identity/features/other-functions.mdx b/docs/guides/products/identity/features/other-functions.mdx index 571311f7..1b32cff5 100644 --- a/docs/guides/products/identity/features/other-functions.mdx +++ b/docs/guides/products/identity/features/other-functions.mdx @@ -14,14 +14,13 @@ import GetCredentialPhp from '!!raw-loader!@site/examples/identity/get_credentia import GetCredentialGo from '!!raw-loader!@site/examples/identity/get_credential/get_credential.go'; - ## Get Schema import GetSchemaJs from '!!raw-loader!@site/examples/identity/get_schema/get_schema.ts'; @@ -31,14 +30,13 @@ import GetSchemaPhp from '!!raw-loader!@site/examples/identity/get_schema/get_sc import GetSchemaGo from '!!raw-loader!@site/examples/identity/get_schema/get_schema.go'; - ## Credential to JSON / JSON to Credential import CredentialJSONJs from '!!raw-loader!@site/examples/identity/credential_json/credential_json.ts'; @@ -48,13 +46,9 @@ import CredentialJSONPhp from '!!raw-loader!@site/examples/identity/credential_j import CredentialJSONGo from '!!raw-loader!@site/examples/identity/credential_json/credential_json.go'; - - - - diff --git a/docs/guides/products/identity/features/verification.mdx b/docs/guides/products/identity/features/verification.mdx index 07e60b91..714d3ee9 100644 --- a/docs/guides/products/identity/features/verification.mdx +++ b/docs/guides/products/identity/features/verification.mdx @@ -6,14 +6,16 @@ sidebar_position: 6 BLOOCK's identity product allows you to act as [verification agents](#the-verifier) as well. Once our users or holders have their credentials, they will want to be able to verify themselves. At this point, you have two options: + 1. Using external verifiers. -2. Create your own verifier through BLOOCK. +2. Create your own verifier through BLOOCK. If you want to use our verification process, here is how it works: + 1. You must create a request, which is nothing more than a QR code to show to the user or Holder you want to verify. In order to create the request you need to create a [Validation Query](#validation-queries). This functionality is available in our dashboard. -2. Then, you will have to wait for your Holder to scan the request with its wallet or software and generate the proof on your request. ->It is important to note that within the request that you will show to your Holder, you can specify what things you want to validate. For example, you can determine that you only want to verify that it holds a credential of the type BloockEmployee or you can verify something more specific, such as that your employee's date of birth is less than 2000. -3. Once you receive the proof from the Holder, it will be verified, and you will get a result of true or false, verified or not verified. The BLOOCK verification process establishes a customizable logic, adding an expiration time to the requests, by default 60 min. This means that when the request is created it will be invalidated after 60 min. If the user was already verified, after 60 minutes it will have to be verified again. +2. Then, you will have to wait for your Holder to scan the request with its wallet or software and generate the proof on your request. + > It is important to note that within the request that you will show to your Holder, you can specify what things you want to validate. For example, you can determine that you only want to verify that it holds a credential of the type BloockEmployee or you can verify something more specific, such as that your employee's date of birth is less than 2000. +3. Once you receive the proof from the Holder, it will be verified, and you will get a result of true or false, verified or not verified. The BLOOCK verification process establishes a customizable logic, adding an expiration time to the requests, by default 60 min. This means that when the request is created it will be invalidated after 60 min. If the user was already verified, after 60 minutes it will have to be verified again. ## Examples @@ -26,9 +28,9 @@ import VerificationPhp from '!!raw-loader!@site/examples/identity/verification/v import VerificationGo from '!!raw-loader!@site/examples/identity/verification/verification.go'; \ No newline at end of file +/> diff --git a/docs/guides/products/integrity/features/prepare-data.mdx b/docs/guides/products/integrity/features/prepare-data.mdx index aab6f548..7f98724d 100644 --- a/docs/guides/products/integrity/features/prepare-data.mdx +++ b/docs/guides/products/integrity/features/prepare-data.mdx @@ -85,7 +85,7 @@ import PrepareRecordPhp from '!!raw-loader!@site/examples/record/prepare_record/ import PrepareRecordGo from '!!raw-loader!@site/examples/record/prepare_record/prepare_record.go'; + +## Verifying signatures manually + +The Bloock-Signature header included in each signed event contains a timestamp and one signature. The timestamp is prefixed by t=, signature is prefixed by v1=. + +``` +Bloock-Signature: +t=1492774577, +v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd +``` + +Bloock generates signatures using a hash-based message authentication code ([HMAC](https://es.wikipedia.org/wiki/HMAC)) with [SHA-256](https://es.wikipedia.org/wiki/SHA-2). + +This code shows how to verify your webhook events signatures manually. + +import VerifyManualGo from '!!raw-loader!@site/examples/webhooks/verify-manual/verify-manual.go'; + + + {VerifyManualGo} + + +**Step 1: Extract the timestamp and signatures from the header** + +Split the header, using the , character as the separator, to get a list of elements. Then split each element, using the = character as the separator, to get a prefix and value pair. + +The value for the prefix t corresponds to the timestamp, and v1 corresponds to the signature. + +**Step 2: Determine the expected signature** + +Compute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret as the key, and use the concatenation of the timestamp with the payload. + +**Step 3: Compare the signatures** + +Compare the signature in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance. diff --git a/docs/partials/_sdk-code-block.mdx b/docs/partials/_sdk-code-block.mdx index 2d74d576..c20dc25e 100644 --- a/docs/partials/_sdk-code-block.mdx +++ b/docs/partials/_sdk-code-block.mdx @@ -3,29 +3,39 @@ import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; - - - {props.js} - - - - - {props.py} - - - - - {props.java} - - - - - {props.php} - - - - - {props.go} - - + {props.ts && ( + + + {props.ts} + + + )} + {props.py && ( + + + {props.py} + + + )} + {props.java && ( + + + {props.java} + + + )} + {props.php && ( + + + {props.php} + + + )} + {props.go && ( + + + {props.go} + + + )} diff --git a/examples/availability/publish_hosted/publish_hosted.py b/examples/availability/publish_hosted/publish_hosted.py index d37899c4..eb37dfe8 100644 --- a/examples/availability/publish_hosted/publish_hosted.py +++ b/examples/availability/publish_hosted/publish_hosted.py @@ -2,16 +2,17 @@ from bloock import bloock from bloock.client.record import RecordClient from bloock.client.availability import AvailabilityClient -from bloock.entity.loader import HostedLoader -from bloock.entity.publisher import HostedPublisher +from bloock.entity.availability.hosted_publisher import HostedPublisher -bloock.api_key = os.environ["API_KEY"] +if __name__ == "__main__": + bloock.api_key = os.environ["API_KEY"] -record_client = RecordClient() -availability_client = AvailabilityClient() + record_client = RecordClient() + availability_client = AvailabilityClient() -record = record_client.from_string("Hello world").build() + record = record_client.from_string("Hello world").build() -id = availability_client.publish(record, HostedPublisher()) + id = availability_client.publish(record, HostedPublisher()) + + print("Record was published successfully") -print("Record was published sucessfully") \ No newline at end of file diff --git a/examples/availability/publish_ipfs/publish_ipfs.py b/examples/availability/publish_ipfs/publish_ipfs.py index 608f4d3a..4b9e988d 100644 --- a/examples/availability/publish_ipfs/publish_ipfs.py +++ b/examples/availability/publish_ipfs/publish_ipfs.py @@ -2,15 +2,17 @@ import bloock from bloock.client.record import RecordClient from bloock.client.availability import AvailabilityClient -from bloock.entity.publisher import IpfsPublisher +from bloock.entity.availability.ipfs_publisher import IpfsPublisher -bloock.api_key = os.environ["API_KEY"] +if __name__ == "__main__": + bloock.api_key = os.environ["API_KEY"] -record_client = RecordClient() -availability_client = AvailabilityClient() + record_client = RecordClient() + availability_client = AvailabilityClient() -record = record_client.from_string("Hello world").build() + record = record_client.from_string("Hello world").build() -id = availability_client.publish(record, IpfsPublisher()) + id = availability_client.publish(record, IpfsPublisher()) + + print("Record was published successfully") -print("Record was published sucessfully") \ No newline at end of file diff --git a/examples/availability/retrieve_hosted/retrieve_hosted.py b/examples/availability/retrieve_hosted/retrieve_hosted.py index fd161df4..81c87ef5 100644 --- a/examples/availability/retrieve_hosted/retrieve_hosted.py +++ b/examples/availability/retrieve_hosted/retrieve_hosted.py @@ -1,13 +1,13 @@ import os from bloock import bloock from bloock.client.record import RecordClient -from bloock.client.availability import AvailabilityClient -from bloock.client.entity.publisher import HostedPublisher +from bloock.entity.availability.hosted_loader import HostedLoader -bloock.api_key = os.environ["API_KEY"] +if __name__ == "__main__": + bloock.api_key = os.environ["API_KEY"] -record_client = RecordClient() -availability_client = AvailabilityClient() + record_client = RecordClient() + + pid = "publish uuid result" + record = record_client.from_loader(HostedLoader(pid)).build() -id = "publish uuid result" -record = record_client.from_loader(HostedLoader(id=id)).build() \ No newline at end of file diff --git a/examples/availability/retrieve_ipfs/retrieve_ipfs.py b/examples/availability/retrieve_ipfs/retrieve_ipfs.py index d6d4536c..b34aa266 100644 --- a/examples/availability/retrieve_ipfs/retrieve_ipfs.py +++ b/examples/availability/retrieve_ipfs/retrieve_ipfs.py @@ -1,13 +1,13 @@ import os from bloock import bloock from bloock.client.record import RecordClient -from bloock.client.availability import AvailabilityClient -from bloock.client.entity.loader import IpfsLoader +from bloock.entity.availability.ipfs_loader import IpfsLoader -bloock.api_key = os.environ["API_KEY"] +if __name__ == "__main__": + bloock.api_key = os.environ["API_KEY"] -record_client = RecordClient() -availability_client = AvailabilityClient() + record_client = RecordClient() + + pid = "publish uuid result" + record = record_client.from_loader(IpfsLoader(pid)).build() -id = "publish uuid result" -record = record_client.from_loader(IpfsLoader(id=id)).build() \ No newline at end of file diff --git a/examples/docker-compose.yml b/examples/docker-compose.yml index 0fe47fe9..554d1ba4 100644 --- a/examples/docker-compose.yml +++ b/examples/docker-compose.yml @@ -33,6 +33,7 @@ services: cd /var/snippets/typescript; yarn add @bloock/sdk; + yarn add express body-parser; yarn add -D @types/node@20.0.0 eslint typescript typescript-eslint; touch /tmp/runner_ready_to_lint; diff --git a/examples/integrity/get_proof/get_proof.py b/examples/integrity/get_proof/get_proof.py index 7a9f524a..8bfdbfd5 100644 --- a/examples/integrity/get_proof/get_proof.py +++ b/examples/integrity/get_proof/get_proof.py @@ -1,2 +1,10 @@ -integrity_client = IntegrityClient() -proof = integrity_client.get_proof(records) +from bloock.client.integrity import IntegrityClient + +if __name__ == "__main__": + integrity_client = IntegrityClient() + + records = [ + # records + ] + proof = integrity_client.get_proof(records) + diff --git a/examples/integrity/send/send.py b/examples/integrity/send/send.py index 6b1e8256..0b0a858c 100644 --- a/examples/integrity/send/send.py +++ b/examples/integrity/send/send.py @@ -1,19 +1,22 @@ import os import bloock from bloock.client.record import RecordClient +from bloock.client.integrity import IntegrityClient -# we set the API key and create a client -bloock.api_key = os.environ["API_KEY"] -record_client = RecordClient() -integrity_client = IntegrityClient() +if __name__ == "__main__": + # we set the API key and create a client + bloock.api_key = os.environ["API_KEY"] + record_client = RecordClient() + integrity_client = IntegrityClient() -# we create an array of records which will contain -# the records we want to send -record = record_client.from_string("Hello world").build() -records = [record] + # we create an array of records which will contain + # the records we want to send + record = record_client.from_string("Hello world").build() + records = [record] -# finally we can send the records -send_receipts = integrity_client.send_records(records) + # finally we can send the records + send_receipts = integrity_client.send_records(records) + + # we get a receipt with information about the transaction + print(list(map(lambda x: x.__dict__, send_receipts))) # pretty print receipts -# we get a receipt with informationa about the transaction -print(list(map(lambda x: x.__dict__, send_receipts))) # pretty print receipts diff --git a/examples/integrity/verify_proof/verify_proof.py b/examples/integrity/verify_proof/verify_proof.py index 759fb74e..db8ef0e5 100644 --- a/examples/integrity/verify_proof/verify_proof.py +++ b/examples/integrity/verify_proof/verify_proof.py @@ -1,2 +1,11 @@ -integrity_client = IntegrityClient() -root = integrity_client.verify_proof(proof) +from bloock.client.integrity import IntegrityClient +from bloock.entity.integrity.proof import Proof + +if __name__ == "__main__": + integrity_client = IntegrityClient() + + # a proof + proof: Proof + + root = integrity_client.verify_proof(proof) + diff --git a/examples/integrity/verify_records/verify_records.py b/examples/integrity/verify_records/verify_records.py index 6ecc39d2..6751ec9d 100644 --- a/examples/integrity/verify_records/verify_records.py +++ b/examples/integrity/verify_records/verify_records.py @@ -1,8 +1,8 @@ import os import bloock +from bloock import Network from bloock.client.integrity import IntegrityClient from bloock.client.record import RecordClient -from bloock.entity.network import Network # we set the API key and create a client bloock.api_key = os.environ["API_KEY"] diff --git a/examples/integrity/verify_root/verify_root.py b/examples/integrity/verify_root/verify_root.py index 7a80c00f..6bb042d0 100644 --- a/examples/integrity/verify_root/verify_root.py +++ b/examples/integrity/verify_root/verify_root.py @@ -1,2 +1,9 @@ -integrity_client = IntegrityClient() -timestamp = integrity_client.validate_root(root, Network.ETHEREUM_MAINNET) +from bloock.client.integrity import IntegrityClient +from bloock.entity.integrity.network import Network + +if __name__ == "__main__": + integrity_client = IntegrityClient() + + root = "root" + timestamp = integrity_client.validate_root(root, Network.ETHEREUM_MAINNET) + diff --git a/examples/integrity/wait_anchor/wait_anchor.py b/examples/integrity/wait_anchor/wait_anchor.py index 563d38f2..be5697f7 100644 --- a/examples/integrity/wait_anchor/wait_anchor.py +++ b/examples/integrity/wait_anchor/wait_anchor.py @@ -3,19 +3,21 @@ from bloock.client.integrity import IntegrityClient from bloock.client.record import RecordClient -# we set the API key and create a client -bloock.api_key = os.environ["API_KEY"] -record_client = RecordClient() -integrity_client = IntegrityClient() +if __name__ == "__main__": + # we set the API key and create a client + bloock.api_key = os.environ["API_KEY"] + record_client = RecordClient() + integrity_client = IntegrityClient() -record = record_client.from_string("Hello world").build() + record = record_client.from_string("Hello world").build() + records = [record] -hash = record.get_hash() -records = [hash] + send_receipts = integrity_client.send_records(records) + + # Once we sent a record, we can wait for it's anchor + print("Waiting for anchor...") + + # we can optionally specify a timeout (if not set, default is 120000) + anchor = integrity_client.wait_anchor(send_receipts[0].anchor, timeout=120000) + print("Done!") -send_receipts = integrity_client.send_records(records) -# Once we sent a record, we can wait for it's anochor -print("Waiting for anchor...") -# we can optionally specify a timeout (if not set, default is 120000) -anchor = integrity_client.wait_anchor(send_receipts[0].anchor, timeout=120000) -print("Done!") diff --git a/examples/key/decrypt_aes/decrypt_aes.py b/examples/key/decrypt_aes/decrypt_aes.py index 29665f76..e5eeda70 100644 --- a/examples/key/decrypt_aes/decrypt_aes.py +++ b/examples/key/decrypt_aes/decrypt_aes.py @@ -1,31 +1,36 @@ -from bloock.entity.decrypter import AesDecrypter -from bloock.entity.encrypter import AesEncrypter +from bloock.entity.encryption.encrypter import Encrypter +from bloock.entity.key.key_type import KeyType +from bloock.entity.record.record import Record from bloock.client.encryption import EncryptionClient from bloock.client.record import RecordClient +from bloock.client.key import KeyClient -payload = "This will be encrypted" -record_client = RecordClient() -encryption_client = EncryptionClient() -key_client = KeyClient() +if __name__ == "__main__": + payload = "This will be encrypted" + record_client = RecordClient() + encryption_client = EncryptionClient() + key_client = KeyClient() -# encryption ... + # encryption ... + encrypted_record = Record() -print("Trying to decrypt with the valid password") + print("Trying to decrypt with the valid password") -key = key_client.new_local_key(KeyType.Aes256) + key = key_client.new_local_key(KeyType.Aes256) -# To decrypt a record during the building process -# we build a record from the encrypted record and add a decrypter -record_client = ( - record_client.from_record(encrypted_record) - .with_decrypter(AesDecrypter(DecrypterArgs(key))) - .build() -) + # To decrypt a record during the building process + # we build a record from the encrypted record and add a decrypter + record_client = ( + record_client.from_record(encrypted_record) + .with_decrypter(Encrypter(key)) + .build() + ) -# To decrypt an already encrypted record independently -decrypted_record = encryption_client.decrypt( - encrypted_record, AesDecrypter(DecrypterArgs(key)) -) + # To decrypt an already encrypted record independently + decrypted_record = encryption_client.decrypt( + encrypted_record, Encrypter(key) + ) + + print("Decryption successful") + print(f'Decrypted payload: "{decrypted_record.retrieve().decode()}"') -print(f"Decryption successful") -print(f'Decrypted payload: "{decrypted_record.retrieve().decode()}"') \ No newline at end of file diff --git a/examples/key/decrypt_rsa/decrypt_rsa.py b/examples/key/decrypt_rsa/decrypt_rsa.py index 27b4773e..6344e5cc 100644 --- a/examples/key/decrypt_rsa/decrypt_rsa.py +++ b/examples/key/decrypt_rsa/decrypt_rsa.py @@ -1,31 +1,36 @@ -from bloock.entity.decrypter import RsaDecrypter -from bloock.entity.encrypter import RsaEncrypter +from bloock.entity.encryption.encrypter import Encrypter +from bloock.entity.key.key_type import KeyType +from bloock.entity.record.record import Record from bloock.client.encryption import EncryptionClient from bloock.client.record import RecordClient +from bloock.client.key import KeyClient -payload = "This will be encrypted" -record_client = RecordClient() -encryption_client = EncryptionClient() -key_client = KeyClient() +if __name__ == "__main__": + payload = "This will be encrypted" + record_client = RecordClient() + encryption_client = EncryptionClient() + key_client = KeyClient() -# encryption ... + # an encrypted record + encrypted_record = Record() -print("Trying to decrypt with the valid password") + print("Trying to decrypt with the valid password") -key = key_client.new_local_key(KeyType.Rsa2048) + key = key_client.new_local_key(KeyType.Rsa2048) -# To decrypt a record during the building process -# we build a record from the encrypted record and add a decrypter -record_client = ( - record_client.from_record(encrypted_record) - .with_decrypter(RsaDecrypter(DecrypterArgs(key))) - .build() -) + # To decrypt a record during the building process + # we build a record from the encrypted record and add a decrypter + record_client = ( + record_client.from_record(encrypted_record) + .with_decrypter(Encrypter(key)) + .build() + ) -# To decrypt an already encrypted record independently -decrypted_record = encryption_client.decrypt( - encrypted_record, RsaDecrypter(DecrypterArgs(key)) -) + # To decrypt an already encrypted record independently + decrypted_record = encryption_client.decrypt( + encrypted_record, Encrypter(key) + ) + + print("Decryption successful") + print(f'Decrypted payload: "{decrypted_record.retrieve().decode()}"') -print(f"Decryption successful") -print(f'Decrypted payload: "{decrypted_record.retrieve().decode()}"') \ No newline at end of file diff --git a/examples/key/encrypt_aes/encrypt_aes.py b/examples/key/encrypt_aes/encrypt_aes.py index ee4163bc..49a005f2 100644 --- a/examples/key/encrypt_aes/encrypt_aes.py +++ b/examples/key/encrypt_aes/encrypt_aes.py @@ -1,26 +1,32 @@ -from bloock.entity.encrypter import AesEncrypter +from bloock.entity.encryption.encrypter import Encrypter +from bloock.entity.key.key_type import KeyType from bloock.client.encryption import EncryptionClient from bloock.client.record import RecordClient +from bloock.client.key import KeyClient -payload = "This will be encrypted" -record_client = RecordClient() -encryption_client = EncryptionClient() -key_client = KeyClient() +if __name__ == "__main__": + payload = "This will be encrypted" + record_client = RecordClient() + encryption_client = EncryptionClient() + key_client = KeyClient() -print(f'The following payload will be encrypted: "{payload}"') + print(f'The following payload will be encrypted: "{payload}"') -key = key_client.new_local_key(KeyType.Aes256) + key = key_client.new_local_key(KeyType.Aes256) -# To encrypt a record during the building process -encrypted_record = ( - record_client.from_string(payload) - .with_encrypter(AesEncrypter(EncrypterArgs(key))) - .build() -) + # To encrypt a record during the building process + encrypted_record = ( + record_client.from_string(payload) + .with_encrypter(Encrypter(key)) + .build() + ) + print("Encryption successful") + print(f"Encrypted payload: {encrypted_record.retrieve().decode()}") -# To encrypt a record independently -record = record_client.from_string(payload).build() -encrypted_record = encryption_client.encrypt(record, AesEncrypter(EncrypterArgs(key))) + # To encrypt a record independently + record = record_client.from_string(payload).build() + encrypted_record = encryption_client.encrypt(record, Encrypter(key)) + + print("Encryption successful") + print(f"Encrypted payload: {encrypted_record.retrieve().decode()}") -print("Encryption successful") -print(f"Encrypted payload: {encrypted_record.retrieve().decode()}") diff --git a/examples/key/encrypt_rsa/encrypt_rsa.py b/examples/key/encrypt_rsa/encrypt_rsa.py index 1079f9bc..54167ab6 100644 --- a/examples/key/encrypt_rsa/encrypt_rsa.py +++ b/examples/key/encrypt_rsa/encrypt_rsa.py @@ -1,26 +1,32 @@ -from bloock.entity.encrypter import RsaEncrypter +from bloock.entity.encryption.encrypter import Encrypter +from bloock.entity.key.key_type import KeyType from bloock.client.encryption import EncryptionClient from bloock.client.record import RecordClient +from bloock.client.key import KeyClient -payload = "This will be encrypted" -record_client = RecordClient() -encryption_client = EncryptionClient() -key_client = KeyClient() +if __name__ == "__main__": + payload = "This will be encrypted" + record_client = RecordClient() + encryption_client = EncryptionClient() + key_client = KeyClient() -print(f'The following payload will be encrypted: "{payload}"') + print(f'The following payload will be encrypted: "{payload}"') -key = key_client.new_local_key(KeyType.Rsa2048) + key = key_client.new_local_key(KeyType.Rsa2048) -# To encrypt a record during the building process -encrypted_record = ( - record_client.from_string(payload) - .with_encrypter(RsaEncrypter(EncrypterArgs(key))) - .build() -) + # To encrypt a record during the building process + encrypted_record = ( + record_client.from_string(payload) + .with_encrypter(Encrypter(key)) + .build() + ) + print("Encryption successful") + print(f"Encrypted payload: {encrypted_record.retrieve().decode()}") -# To encrypt a record independently -record = record_client.from_string(payload).build() -encrypted_record = encryption_client.encrypt(record, RsaEncrypter(EncrypterArgs(key))) + # To encrypt a record independently + record = record_client.from_string(payload).build() + encrypted_record = encryption_client.encrypt(record, Encrypter(key)) + + print("Encryption successful") + print(f"Encrypted payload: {encrypted_record.retrieve().decode()}") -print("Encryption successful") -print(f"Encrypted payload: {encrypted_record.retrieve().decode()}") \ No newline at end of file diff --git a/examples/key/load_local_certificate/load_local_certificate.py b/examples/key/load_local_certificate/load_local_certificate.py index e69de29b..b4db86af 100644 --- a/examples/key/load_local_certificate/load_local_certificate.py +++ b/examples/key/load_local_certificate/load_local_certificate.py @@ -0,0 +1,13 @@ +import os + +from bloock.client.key import KeyClient + +if __name__ == "__main__": + key_client = KeyClient() + + current_directory = os.getcwd() + file_path = current_directory + "certificate.p12" + with open(file_path, 'rb') as file: + file_bytes = file.read() + + local_certificate = key_client.load_local_certificate(file_bytes, "password") diff --git a/examples/key/load_local_key/load_local_key.py b/examples/key/load_local_key/load_local_key.py index 0f056af3..fb21279e 100644 --- a/examples/key/load_local_key/load_local_key.py +++ b/examples/key/load_local_key/load_local_key.py @@ -1,19 +1,24 @@ -key_client = KeyClient() +from bloock.entity.key.key_type import KeyType +from bloock.client.key import KeyClient -// Load a EcP256k key -local_key = key_client.load_local_key(KeyType.EcP256k, "public key", "private key") +if __name__ == "__main__": + key_client = KeyClient() -// Load a Rsa2048 key -local_key = key_client.load_local_key(KeyType.Rsa2048, "public key", "private key") + # Load a EcP256k key + local_key = key_client.load_local_key(KeyType.EcP256k, "private key") -// Load a Rsa3072 key -local_key = key_client.load_local_key(KeyType.Rsa3072, "public key", "private key") + # Load a Rsa2048 key + local_key = key_client.load_local_key(KeyType.Rsa2048, "private key") -// Load a Rsa4096 key -local_key = key_client.load_local_key(KeyType.Rsa4096, "public key", "private key") + # Load a Rsa3072 key + local_key = key_client.load_local_key(KeyType.Rsa3072, "private key") -// Load a Aes128 key -local_key = key_client.load_local_key(KeyType.Aes128, "key") + # Load a Rsa4096 key + local_key = key_client.load_local_key(KeyType.Rsa4096, "private key") + + # Load a Aes128 key + local_key = key_client.load_local_key(KeyType.Aes128, "password") + + # Load a Aes256 key + local_key = key_client.load_local_key(KeyType.Aes256, "password") -// Load a Aes256 key -local_key = key_client.load_local_key(KeyType.Aes256, "key") diff --git a/examples/key/load_managed_certificate/load_managed_certificate.py b/examples/key/load_managed_certificate/load_managed_certificate.py index e69de29b..f1413ba2 100644 --- a/examples/key/load_managed_certificate/load_managed_certificate.py +++ b/examples/key/load_managed_certificate/load_managed_certificate.py @@ -0,0 +1,6 @@ +from bloock.client.key import KeyClient + +if __name__ == "__main__": + key_client = KeyClient() + + local_certificate = key_client.load_managed_certificate("certificate id") diff --git a/examples/key/load_managed_key/load_managed_key.py b/examples/key/load_managed_key/load_managed_key.py index 37260f2f..4c4bcaa5 100644 --- a/examples/key/load_managed_key/load_managed_key.py +++ b/examples/key/load_managed_key/load_managed_key.py @@ -1,4 +1,8 @@ -key_client = KeyClient() +from bloock.client.key import KeyClient + +if __name__ == "__main__": + key_client = KeyClient() + + # Load a managed key + key = key_client.load_managed_key("key id") -// Load a managed key -key = key_client.load_managed_key("key id") diff --git a/examples/key/new_local_certificate/new_local_certificate.py b/examples/key/new_local_certificate/new_local_certificate.py index e69de29b..fb603c66 100644 --- a/examples/key/new_local_certificate/new_local_certificate.py +++ b/examples/key/new_local_certificate/new_local_certificate.py @@ -0,0 +1,20 @@ +from bloock.client.key import KeyClient +from bloock.entity.key.key_type import KeyType +from bloock.entity.key.local_certificate_params import LocalCertificateParams +from bloock.entity.key.subject_certificate_params import SubjectCertificateParams + +if __name__ == "__main__": + key_client = KeyClient() + + key_type = KeyType.Rsa2048 + subject_params = SubjectCertificateParams( + "Common name", + "Organization", + "Organization Unit", + "Location", + "State", + "US" + ) + params = LocalCertificateParams(key_type, subject_params, "password", 2) + local_certificate = key_client.new_local_certificate(params) + diff --git a/examples/key/new_local_key/new_local_key.py b/examples/key/new_local_key/new_local_key.py index 4786724b..7423e126 100644 --- a/examples/key/new_local_key/new_local_key.py +++ b/examples/key/new_local_key/new_local_key.py @@ -1,19 +1,24 @@ -key_client = KeyClient() +from bloock.entity.key.key_type import KeyType +from bloock.client.key import KeyClient -// Generate a EcP256k key -local_key = key_client.new_local_key(KeyType.EcP256k) +if __name__ == "__main__": + key_client = KeyClient() -// Generate a Rsa2048 key -local_key = key_client.new_local_key(KeyType.Rsa2048) + # Generate a EcP256k key + local_key = key_client.new_local_key(KeyType.EcP256k) -// Generate a Rsa3072 key -local_key = key_client.new_local_key(KeyType.Rsa3072) + # Generate a Rsa2048 key + local_key = key_client.new_local_key(KeyType.Rsa2048) -// Generate a Rsa4096 key -local_key = key_client.new_local_key(KeyType.Rsa4096) + # Generate a Rsa3072 key + local_key = key_client.new_local_key(KeyType.Rsa3072) -// Generate a Aes128 key -local_key = key_client.new_local_key(KeyType.Aes128) + # Generate a Rsa4096 key + local_key = key_client.new_local_key(KeyType.Rsa4096) + + # Generate a Aes128 key + local_key = key_client.new_local_key(KeyType.Aes128) + + # Generate a Aes256 key + local_key = key_client.new_local_key(KeyType.Aes256) -// Generate a Aes256 key -local_key = key_client.new_local_key(KeyType.Aes256) diff --git a/examples/key/new_managed_certificate/new_managed_certificate.py b/examples/key/new_managed_certificate/new_managed_certificate.py index e69de29b..64721311 100644 --- a/examples/key/new_managed_certificate/new_managed_certificate.py +++ b/examples/key/new_managed_certificate/new_managed_certificate.py @@ -0,0 +1,20 @@ +from bloock.client.key import KeyClient +from bloock.entity.key.key_type import KeyType +from bloock.entity.key.managed_certificate_params import ManagedCertificateParams +from bloock.entity.key.subject_certificate_params import SubjectCertificateParams + +if __name__ == "__main__": + key_client = KeyClient() + + key_type = KeyType.Rsa2048 + subject_params = SubjectCertificateParams( + "Common name", + "Organization", + "Organization Unit", + "Location", + "State", + "US" + ) + params = ManagedCertificateParams(key_type, subject_params, 5) + managed_certificate = key_client.new_managed_certificate(params) + diff --git a/examples/key/new_managed_key/new_managed_key.py b/examples/key/new_managed_key/new_managed_key.py index 4f267f6c..bf7249b1 100644 --- a/examples/key/new_managed_key/new_managed_key.py +++ b/examples/key/new_managed_key/new_managed_key.py @@ -1,26 +1,33 @@ -key_client = KeyClient() -protection = KeyProtectionLevel.SOFTWARE +from bloock.entity.key.key_type import KeyType +from bloock.client.key import KeyClient +from bloock.entity.key.key_protection_level import KeyProtectionLevel +from bloock.entity.key.managed_key_params import ManagedKeyParams -// Generate a EcP256k key -key_type = KeyType.EcP256k -managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) +if __name__ == "__main__": + key_client = KeyClient() + protection = KeyProtectionLevel.SOFTWARE -// Generate a Rsa2048 key -key_type = KeyType.Rsa2048 -managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) + # Generate a EcP256k key + key_type = KeyType.EcP256k + managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) -// Generate a Rsa3072 key -key_type = KeyType.Rsa3072 -managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) + # Generate a Rsa2048 key + key_type = KeyType.Rsa2048 + managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) -// Generate a Rsa4096 key -key_type = KeyType.Rsa4096 -managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) + # Generate a Rsa3072 key + key_type = KeyType.Rsa3072 + managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) -// Generate a Aes128 key -key_type = KeyType.Aes128 -managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) + # Generate a Rsa4096 key + key_type = KeyType.Rsa4096 + managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) + + # Generate a Aes128 key + key_type = KeyType.Aes128 + managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) + + # Generate a Aes256 key + key_type = KeyType.Aes256 + managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) -// Generate a Aes256 key -key_type = KeyType.Aes256 -managed_key = key_client.new_managed_key(ManagedKeyParams(protection, key_type)) diff --git a/examples/key/sign/sign.py b/examples/key/sign/sign.py index 45154a4a..db7985ca 100644 --- a/examples/key/sign/sign.py +++ b/examples/key/sign/sign.py @@ -1,40 +1,39 @@ from bloock.client.authenticity import AuthenticityClient from bloock.client.record import RecordClient -from bloock.client.entity.signer import EcdsaSigner from bloock.client.key import KeyClient +from bloock.entity.key.key_type import KeyType +from bloock.entity.authenticity.signer import Signer -authenticity_client = AuthenticityClient() -record_client = RecordClient() -key_client = KeyClient() +if __name__ == "__main__": + authenticity_client = AuthenticityClient() + record_client = RecordClient() + key_client = KeyClient() -key = key_client.new_local_key(KeyType.EcP256k) + key = key_client.new_local_key(KeyType.EcP256k) -signed_record = ( -record_client.from_string("Hello world") -.with_signer(EcdsaSigner(SignerArgs(key))) -.build() -) + signed_record = ( + record_client.from_string("Hello world").with_signer(Signer(key)).build() + ) -print("Record was signed sucessfully") + print("Record was signed successfully") -# we can add another signature with a different key + # we can add another signature with a different key -key = key_client.new_local_key(KeyType.EcP256k) + key = key_client.new_local_key(KeyType.EcP256k) -print("Adding another signature") -signed_record = ( -record_client.from_record(signed_record) -.with_signer(EcdsaSigner(SignerArgs(key))) -.build() -) + print("Adding another signature") + signed_record = ( + record_client.from_record(signed_record).with_signer(Signer(key)).build() + ) -print("Record was signed sucessfully") + print("Record was signed successfully") -hash = signed_record.get_hash() + record_hash = signed_record.get_hash() -print(f"Hash: {hash}") + print(f"Hash: {record_hash}") -signatures = authenticity_client.get_signatures(signed_record) + signatures = authenticity_client.get_signatures(signed_record) + + for i, signature in enumerate(signatures): + print(f"Signature {i + 1}: {signature}") -for i, signature in enumerate(signatures): -print(f"Signature {i + 1}: {signature.**dict**}") diff --git a/examples/key/verify/verify.py b/examples/key/verify/verify.py index cbf61bb4..1a9ef770 100644 --- a/examples/key/verify/verify.py +++ b/examples/key/verify/verify.py @@ -1,21 +1,23 @@ from bloock.client.authenticity import AuthenticityClient from bloock.client.record import RecordClient -from bloock.client.entity.signer import EcdsaSigner from bloock.client.key import KeyClient +from bloock.entity.key.key_type import KeyType +from bloock.entity.authenticity.signer import Signer -record_client = RecordClient() -authenticity_client = AuthenticityClient() -key_client = KeyClient() +if __name__ == "__main__": + record_client = RecordClient() + authenticity_client = AuthenticityClient() + key_client = KeyClient() -key = key_client.new_local_key(KeyType.EcP256k) + key = key_client.new_local_key(KeyType.EcP256k) -signed_record = ( - record_client.from_string("Hello world") - .with_signer(EcdsaSigner(SignerArgs(key, "some name"))) - .build() -) + signed_record = ( + record_client.from_string("Hello world") + .with_signer(Signer(key)) + .build() + ) -valid = authenticity_client.verify(signed_record) -if valid == true: - print("Signature was verified successfully") + valid = authenticity_client.verify(signed_record) + if valid: + print("Signature was verified successfully") diff --git a/examples/record/prepare_record/prepare_record.py b/examples/record/prepare_record/prepare_record.py index f29edd2c..a7fe2283 100644 --- a/examples/record/prepare_record/prepare_record.py +++ b/examples/record/prepare_record/prepare_record.py @@ -1,18 +1,17 @@ -import os -import bloock from bloock.client.record import RecordClient -record_client = RecordClient() -record = record_client.from_string("Hello world").build() -# we can get the hash of the record -hash = record.get_hash() +if __name__ == "__main__": + record_client = RecordClient() + record = record_client.from_string("Hello world").build() + # we can get the hash of the record + record_hash = record.get_hash() -# build a record from an existing record -record2 = record_client.from_record(record).build() + # build a record from an existing record + record2 = record_client.from_record(record).build() + + # we can read a file as an array of bytes + with open("sample.pdf", "rb") as file: + data = bytes(file.read()) + # and build a record from it + record3 = record_client.from_file(data).build() -# we can read a file as an array of bytes -with open("sample.pdf", "rb") as file: - data = bytes(file.read()) - # and build a record from it - record3 = record_client.from_file(data).build() - \ No newline at end of file diff --git a/examples/setup/setup/setup.py b/examples/setup/setup/setup.py index d8bd0082..cc9e8994 100644 --- a/examples/setup/setup/setup.py +++ b/examples/setup/setup/setup.py @@ -1,4 +1,5 @@ import os import bloock -bloock.api_key = os.getenv("API_KEY", default='apiKey') +bloock.api_key = os.getenv("API_KEY") + diff --git a/examples/webhooks/verify-manual/verify-manual.go b/examples/webhooks/verify-manual/verify-manual.go new file mode 100644 index 00000000..162b56b7 --- /dev/null +++ b/examples/webhooks/verify-manual/verify-manual.go @@ -0,0 +1,139 @@ +package main + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "net/http" + "strconv" + "strings" + "time" +) + +const ( + // SecretKey represents the client secret key associate to your webhook endpoint + SecretKey = "secret" + // DefaultTolerance indicates that signatures older than this will be rejected. + DefaultTolerance = 600 * time.Second + // signingVersion represents the version of the signature we currently use. + signingVersion = "v1" +) + +var ( + // ErrInvalidHeader invalid header + ErrInvalidHeader = errors.New("webhook has invalid Bloock-Signature header") + // ErrNotSigned not signed + ErrNotSigned = errors.New("webhook has no Bloock-Signature header") + // ErrNoValidSignature no valid signature + ErrNoValidSignature = errors.New("webhook had no valid signature") + // ErrTooOld too olad + ErrTooOld = errors.New("timestamp wasn't within tolerance") +) + +type signedHeader struct { + timestamp time.Time + signature []byte +} + +func main() { + + verifyHandler := func(w http.ResponseWriter, req *http.Request) { + enforceTolerance := false // decide if you want to set tolerance when verifying + + body, err := io.ReadAll(req.Body) + if err != nil { + log.Fatalf("Cannot read body request: %v", err) + } + bloockSignature := req.Header.Get("Bloock-Signature") + err = verifySignature(body, bloockSignature, SecretKey, enforceTolerance) + if err != nil { + log.Fatal(err) + } + log.Println("Valid Signature!") + } + + http.HandleFunc("/verify", verifyHandler) + log.Println("Listing for requests at http://localhost:8000/verify") + log.Fatal(http.ListenAndServe(":8000", nil)) +} + +func verifySignature(payload []byte, sigHeader string, secretKey string, enforceTolerance bool) error { + header, err := parseSignatureHeader(sigHeader) + if err != nil { + return err + } + expectedSignature, err := computeSignature(header.timestamp, payload, secretKey) + if err != nil { + return err + } + expiredTimestamp := time.Since(header.timestamp) > DefaultTolerance + if enforceTolerance && expiredTimestamp { + return ErrTooOld + } + + if hmac.Equal(expectedSignature, header.signature) { + return nil + } + return ErrNoValidSignature +} + +func parseSignatureHeader(header string) (*signedHeader, error) { + sh := &signedHeader{} + + if header == "" { + return sh, ErrNotSigned + } + + // Signed header looks like "t=1495999758,v1=ABC" + pairs := strings.Split(header, ",") + for _, pair := range pairs { + parts := strings.Split(pair, "=") + if len(parts) != 2 { + return sh, ErrInvalidHeader + } + + switch parts[0] { + case "t": + timestamp, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return sh, ErrInvalidHeader + } + sh.timestamp = time.Unix(timestamp, 0) + + case signingVersion: + sig, err := hex.DecodeString(parts[1]) + if err != nil { + continue + } + sh.signature = sig + + default: + continue + } + } + + if len(sh.signature) == 0 { + return sh, ErrNoValidSignature + } + + return sh, nil +} + +func computeSignature(t time.Time, payload []byte, secretKey string) ([]byte, error) { + buffer := new(bytes.Buffer) + if err := json.Compact(buffer, payload); err != nil { + return nil, err + } + mac := hmac.New(sha256.New, []byte(secretKey)) + mac.Write([]byte(fmt.Sprintf("%d", t.Unix()))) + mac.Write([]byte(".")) + mac.Write(buffer.Bytes()) + + return mac.Sum(nil), nil +} diff --git a/examples/webhooks/verify-sdk/verify-sdk.go b/examples/webhooks/verify-sdk/verify-sdk.go new file mode 100644 index 00000000..a7aab48e --- /dev/null +++ b/examples/webhooks/verify-sdk/verify-sdk.go @@ -0,0 +1,37 @@ +package main + +import ( + "io" + "log" + "net/http" + + "github.com/bloock/bloock-sdk-go/v2/client" +) + +// SecretKey represents the client secret key associate to your webhook endpoint +const SecretKey = "NHJTAE6ikKBccSaeCSBSWGdp7NmixXy7" + +func main() { + verifyHandler := func(w http.ResponseWriter, req *http.Request) { + enforceTolerance := false // decide if you want to set tolerance when verifying + body, err := io.ReadAll(req.Body) + if err != nil { + log.Fatalf("Cannot read body request: %v", err) + } + bloockSignature := req.Header.Get("Bloock-Signature") + + webhookClient := client.NewWebhookClient() + ok, err := webhookClient.VerifyWebhookSignature(body, bloockSignature, SecretKey, enforceTolerance) + if err != nil { + log.Fatal(err) + } + if !ok { + log.Fatal("Invalid Signature!") + } + log.Println("Valid Signature!") + } + + http.HandleFunc("/verify", verifyHandler) + log.Println("Listing for requests at http://localhost:8000/verify") + log.Fatal(http.ListenAndServe(":8000", nil)) +} diff --git a/examples/webhooks/verify-sdk/verify-sdk.py b/examples/webhooks/verify-sdk/verify-sdk.py new file mode 100644 index 00000000..1272bae5 --- /dev/null +++ b/examples/webhooks/verify-sdk/verify-sdk.py @@ -0,0 +1,30 @@ +from flask import Flask +from flask import request +import bloock +from bloock.client.webhook import WebhookClient + +app = Flask(__name__) + +SECRET_KEY = "NHJTAE6ikKBccSaeCSBSWGdp7NmixXy7" + + +@app.route('/verify', methods=['POST']) +def index(): + enforce_tolerance = False # decide if you want to set tolerance when verifying + + body = request.data + bloock_signature = request.headers['Bloock-Signature'] + + webhook_client = WebhookClient() + ok = webhook_client.verify_webhook_signature( + body, bloock_signature, SECRET_KEY, enforce_tolerance) + + if ok == False: + raise ValueError('Invalid Signature!') + else: + print('Valid Signature!') + + return 'Finish' + + +app.run(host='0.0.0.0', port=81) \ No newline at end of file diff --git a/examples/webhooks/verify-sdk/verify-sdk.ts b/examples/webhooks/verify-sdk/verify-sdk.ts new file mode 100644 index 00000000..093dbfa3 --- /dev/null +++ b/examples/webhooks/verify-sdk/verify-sdk.ts @@ -0,0 +1,39 @@ +import { WebhookClient } from '@bloock/sdk'; +import bodyParser from 'body-parser'; +import express from 'express'; + +const app = express(); +const port = 3000; + +const secretKey = 'NHJTAE6ikKBccSaeCSBSWGdp7NmixXy7'; + +const options = { + inflate: true, + limit: '100kb', + type: 'application/*', +}; +app.use(bodyParser.raw(options)); + +app.post('/verify', async (req, res) => { + const enforceTolerance = false; // decide if you want to set tolerance when verifying + const body = req.body; + const header = req.get('Bloock-Signature'); + + const webhookClient = new WebhookClient(); + const ok = await webhookClient.verifyWebhookSignature( + body, + header, + secretKey, + enforceTolerance + ); + if (!ok) { + console.error('Invalid Signature!'); + } else { + console.log('Valid Signature!'); + } + return res; +}); + +app.listen(port, () => { + console.log(`Example app listening on port ${port}`); +}); diff --git a/static/static/docs/guides/webhooks/configure/1-configure.png b/static/static/docs/guides/webhooks/configure/1-configure.png new file mode 100644 index 00000000..5a31bf74 Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/1-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/10-configure.png b/static/static/docs/guides/webhooks/configure/10-configure.png new file mode 100644 index 00000000..83326e8e Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/10-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/2-configure.png b/static/static/docs/guides/webhooks/configure/2-configure.png new file mode 100644 index 00000000..2d23e3de Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/2-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/3-configure.png b/static/static/docs/guides/webhooks/configure/3-configure.png new file mode 100644 index 00000000..6ec3b641 Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/3-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/4-configure.png b/static/static/docs/guides/webhooks/configure/4-configure.png new file mode 100644 index 00000000..d1be7da4 Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/4-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/5-configure.png b/static/static/docs/guides/webhooks/configure/5-configure.png new file mode 100644 index 00000000..0ff82720 Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/5-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/6-configure.png b/static/static/docs/guides/webhooks/configure/6-configure.png new file mode 100644 index 00000000..69944356 Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/6-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/7-configure.png b/static/static/docs/guides/webhooks/configure/7-configure.png new file mode 100644 index 00000000..90490d30 Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/7-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/8-configure.png b/static/static/docs/guides/webhooks/configure/8-configure.png new file mode 100644 index 00000000..df5cbf16 Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/8-configure.png differ diff --git a/static/static/docs/guides/webhooks/configure/9-configure.png b/static/static/docs/guides/webhooks/configure/9-configure.png new file mode 100644 index 00000000..eccf43fe Binary files /dev/null and b/static/static/docs/guides/webhooks/configure/9-configure.png differ diff --git a/static/static/docs/guides/webhooks/verify/1-verify.png b/static/static/docs/guides/webhooks/verify/1-verify.png new file mode 100644 index 00000000..422757dd Binary files /dev/null and b/static/static/docs/guides/webhooks/verify/1-verify.png differ