Metadata-Version: 2.4
Name: pyattest
Version: 1.0.5
Home-page: https://github.com/dreipol/pyattest
Author: dreipol
Author-email: dev@dreipol.ch
License: MIT
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.6.0
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cbor2
Requires-Dist: pyhanko_certvalidator
Requires-Dist: pyjwt
Requires-Dist: python-jose[cryptography]
Requires-Dist: cryptography
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: license-file
Dynamic: requires-dist
Dynamic: requires-python


# pyattest

pyattest is a server-side library that is designed to help you verify that you are talking to genuine instances of your app (i.e. that your client code is running as you intended it to). It provides a common interface that helps you verify attestations from either [Google](https://developer.android.com/google/play/integrity) or [Apple](https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server). The package works standalone but if you use django and need a full implementation with key generation and storage then [django-dreiattest](https://github.com/dreipol/django-dreiattest) could be of interest for you.

## Installation

pyattest is available on PyPI and can be installed via `$ python -m pip install pyattest`

## Usage

In it's most basic form you can create either a `GoogleConfig`, `GooglePlayIntegrityApiConfig` or `AppleConfig` instance, create an `Attestation` and verify it.

### Google Play Integrity API

The following parameters are important:

- `decryption_key`: A Base64 encoded AES key secret as described [here](https://developer.android.com/google/play/integrity/verdict#decrypt-verify)
- `verification_key`: A Base64 encoded public key as described [here](https://developer.android.com/google/play/integrity/verdict#decrypt-verify)
- `apk_package_name`: Name of your apk
- `allow_non_play_distribution`: Set to true if you want to verify apps distributed via other means than Google Play (you need to set `verify_code_signature_hex`) *Note: should not be used for dev builds set `production` to `False` in that case instead.*
- `verify_code_signature_hex`: The sha256 hash of the signing identity you use for distributing your app. This can be obtained using `./gradlew signingReport` in your Android project.
- `required_device_verdict`: If you want to require stronger integrity guarantees pass [the corresponding key](https://developer.android.com/google/play/integrity/setup#optional_device_information) here.
- `attest`: The jwt object string representing the attestation, which is a jws nested in a jwe object
- `nonce`: The nonce used to create the attestation

```python
config = GooglePlayIntegrityApiConfig(
            decryption_key=[decryption_key],
            verification_key=[decryption_key],
            apk_package_name='ch.dreipol.demo',
            production=True,
            allow_non_play_distribution=True,
            verify_code_signature_hex=["00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"],
            required_device_verdict="MEETS_STRONG_INTEGRITY"
        )
attestation = Attestation(attest, nonce, config)

try:
    attestation.verify()
except PyAttestException as exception:
    # Do your thing
    pass
```

### Google (Legacy: SafetyNet)

The following parameters are important:

- `key_id`: A Base64 encoded SHA-256 hash of your apps certificate
- `apk_package_name`: Name of your apk
- `production`: Ignores basic integrity and cts profile check if `False`
- `attest`: The jws object string representing the attestation
- `nonce`: The nonce used to create the attestation

```python
config = GoogleConfig(key_ids=[key_id], apk_package_name='ch.dreipol.demo', production=True)
attestation = Attestation(attest, nonce, config)

try:
    attestation.verify()
except PyAttestException as exception:
    # Do your thing
    pass
```

### Apple

The following parameters are important:

- `key_id`: SHA-256 hash of the public key form the cert you got back from the attestation
- `app_id`: Your app’s App ID, which is the concatenation of your 10-digit team identifier, a period, and your app’s CFBundleIdentifier value
- `production`: Checks for the appropriate `aaguid`
- `attest`: The apple attestation as binary
- `nonce`: The nonce used to create the attestation

```python
config = AppleConfig(key_id=key_id, app_id='1234ABCDEF.ch.dreipol.demo', production=True)
attestation = Attestation(attest, nonce, config)

try:
    attestation.verify()
except PyAttestException as exception:
    # Do your thing
    pass
```

### Assertion

Once you verified and obtained a public key, you can use it to `assert` further requests. For a full implementation on how to get to the public key check out [django-dreiattest](https://github.com/dreipol/django-dreiattest/blob/master/dreiattest/key.py). To check if an `assertion` is valid we check if it was signed with given `pem_key`.

- `assertion`: Raw bytes of the assertion you want to test
- `expected_hash`: The hash we want to compare the signature against
- `pem_key`: The public key to verify the signature
- `config`: A `AppleConfig` or `GoogleConfig` instance 

```
assertion = Assertion(assertion, expected_hash, pem_key, config)
assertion.verify()
```





