dpapi-ng


Namedpapi-ng JSON
Version 0.2.0 PyPI version JSON
download
home_page
SummaryDPAPI NG decryption for Python
upload_time2023-06-02 01:09:47
maintainer
docs_urlNone
author
requires_python>=3.7
licenseMIT License Copyright (c) 2023 Jordan Borean, Red Hat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords dpapi dpapi-ng laps
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # dpapi_ng - Python DPAPI-NG De-/Encryption Library

[![Test workflow](https://github.com/jborean93/dpapi-ng/actions/workflows/ci.yml/badge.svg)](https://github.com/jborean93/dpapi-ng/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/jborean93/dpapi-ng/branch/main/graph/badge.svg?token=UEA7VoocS5)](https://codecov.io/gh/jborean93/dpapi-ng)
[![PyPI version](https://badge.fury.io/py/dpapi-ng.svg)](https://badge.fury.io/py/dpapi-ng)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jborean93/dpapi-ng/blob/main/LICENSE)

Library for [DPAPI NG](https://learn.microsoft.com/en-us/windows/win32/seccng/cng-dpapi), also known as CNG DPAPI, de- and encryption in Python.
It is designed to replicate the behaviour of [NCryptUnprotectSecret](https://learn.microsoft.com/en-us/windows/win32/api/ncryptprotect/nf-ncryptprotect-ncryptunprotectsecret) and [NCryptProtectSecret](https://learn.microsoft.com/en-us/windows/win32/api/ncryptprotect/nf-ncryptprotect-ncryptprotectsecret).
This can be used on non-Windows hosts to de-/encrypt DPAPI NG protected secrets, like PFX user protected password, or LAPS encrypted password.
It can either decrypt any DPAPI NG blobs using an offline copy of the domain's root key or de-/encrypt by using the credentials of the supplied user to retrieve the required information over RPC.

Currently only these protection descriptors are supported:

|Type|Purpose|
|-|-|
|SID|Only the SID user or members of the SID group can decrypt the secret|

This implements the [MS-GKDI Group Key Distribution Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/943dd4f6-6b80-4a66-8594-80df6d2aad0a).

## Requirements

* CPython 3.7+
* [cryptography](https://pypi.org/project/cryptography/)
* [dnspython >= 2.0.0](https://pypi.org/project/dnspython/)
* [pyspnego >= 0.9.0](https://pypi.org/project/pyspnego/)

## How to Install

To install dpapi-ng with all the basic features, run

```bash
python -m pip install dpapi-ng
```

### Kerberos Authentication

Kerberos authentication support won't be installed by default as it relies on system libraries and a valid compiler to be present.
The krb5 library and compiler can be installed by installing these packages:

```bash
# Debian/Ubuntu
apt-get install gcc python3-dev libkrb5-dev

# Centos/RHEL
yum install gcc python-devel krb5-devel

# Fedora
dnf install gcc python-devel krb5-devel

# Arch Linux
pacman -S gcc krb5
```

Once installed, the Kerberos Python extras can be installed with

```bash
python -m pip install dpapi-ng[kerberos]
```

Kerberos also needs to be configured to talk to the domain but that is outside the scope of this page.

### From Source

```bash
git clone https://github.com/jborean93/dpapi-ng.git
cd dpapi-ng
pip install -e .
```

## Examples

There is both a sync and asyncio API available to de-/encrypt a blob.

```python
import dpapi_ng


### DECRYPTION ###
dpapi_ng_blob = b"..."
decrypted_blob = dpapi_ng.ncrypt_unprotect_secret(dpapi_ng_blob)

# async equivalent to the above
decrypted_blob = await dpapi_ng.async_ncrypt_unprotect_secret(dpapi_ng_blob)


### ENCRYPTION ###
data = b"..."
target_sid = "S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX"
dpapi_ng_blob = dpapi_ng.ncrypt_protect_secret(data, target_sid)

# async equivalent to the above
dpapi_ng_blob = await dpapi_ng.async_ncrypt_protect_secret(data, target_sid)
```

To decrypt the blob, the key specified in the blob needs to be retrieved from the domain controller the blob was generated by.
To encrypt a blob, the group key of the target SID specified needs to be retrieved.
For decryption, the domain controller hostname is automatically retrieved through an DNS `SRV` lookup of `_ldap._tcp.dc._msdcs.{domain_name}` with the domain name found in the DPAPI-NG blob or with the value specified in the `server` kwarg.
For encryption, the domain controller hostname is either determined using the `domain_name` kwarg for a DNS `SRV` lookup, the `server` kwarg directly or by using the system's search domain (if available).
It will attempt to authenticate with the current user identifier which on Linux will only exist if `kinit` has already been called to retrieve a user's ticket.
Otherwise if no identity is available, the `username` and `password` kwargs can be used to specify a custom user.

The following kwargs can be used for `ncrypt_unprotect_secret`, `async_ncrypt_unprotect_secret`, `ncrypt_protect_secret` and `async_ncrypt_protect_secret`.

* `server`: Use this server as the RPC target if a key needs to be retrieved
* `username`: The username to authenticate as for the RPC connection
* `password`: The password to authenticate with for the RPC connection
* `auth_protocol`: The authentication protocol (`negotiate`, `kerberos`, `ntlm`) to use for the RPC connection
* `cache`: A cache to store keys retrieved for future operation

In addition to that, `ncrypt_protect_secret` and `async_ncrypt_protect_secret` take the following additional kwarg.

* `domain_name`: The name of the domain/forest to use when looking up the RPC target to use when retrieving the key info
* `root_key_identifier`: The root key identifier UUID, necessary in order to make the cache work for these functions

It is also possible to encrypt and decrypt the DPAPI-NG blob by providing the root key stored in the domain.
This can either be retrieved using an offline attack or through an LDAP query if running as a Domain Admin user.
To retrieve the domain root keys using PowerShell the following can be run:

```powershell
$configurationContext = (Get-ADRootDSE).configurationNamingContext
$getParams = @{
    LDAPFilter = '(objectClass=msKds-ProvRootKey)'
    SearchBase = "CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,$configurationContext"
    SearchScope = 'OneLevel'
    Properties = @(
        'cn'
        'msKds-KDFAlgorithmID'
        'msKds-KDFParam'
        'msKds-SecretAgreementAlgorithmID'
        'msKds-SecretAgreementParam'
        'msKds-PrivateKeyLength'
        'msKds-PublicKeyLength'
        'msKds-RootKeyData'
    )
}
Get-ADObject @getParams | ForEach-Object {
    [PSCustomObject]@{
        Version = 1
        RootKeyId = [Guid]::new($_.cn)
        KdfAlgorithm = $_.'msKds-KDFAlgorithmID'
        KdfParameters = [System.Convert]::ToBase64String($_.'msKds-KDFParam')
        SecretAgreementAlgorithm = $_.'msKds-SecretAgreementAlgorithmID'
        SecretAgreementParameters = [System.Convert]::ToBase64String($_.'msKds-SecretAgreementParam')
        PrivateKeyLength = $_.'msKds-PrivateKeyLength'
        PublicKeyLength = $_.'msKds-PublicKeyLength'
        RootKeyData = [System.Convert]::ToBase64String($_.'msKds-RootKeyData')
    }
}
```

The following `ldapsearch` command can be used outside of Windows:

```bash
ldapsearch \
    -b 'CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,DC=domain,DC=test' \
    -s one \
    '(objectClass=msKds-ProvRootKey)' \
    cn \
    msKds-KDFAlgorithmID \
    msKds-KDFParam \
    msKds-SecretAgreementAlgorithmID \
    msKds-SecretAgreementParam \
    msKds-PrivateKeyLength \
    msKds-PublicKeyLength \
    msKds-RootKeyData
```

_Note: ldapsearch will most likely need the -H and user bind information to succeed._

The information retrieved there can be stored in a cache and used for subsequent `ncrypt_protect_secret` and `ncrypt_unprotect_secret` calls:

```python
import uuid

import dpapi_ng

cache = dpapi_ng.KeyCache()

root_key_id = uuid.UUID("76ec8b2d-d444-4f67-9db7-2f62b4358b35")
cache.load_key(
    b"...",                             # msKds-RootKeydata
    root_key_id,                        # cn
    version=1,
    kdf_algorithm="SP800_108_CTR_HMAC", # msKds-KDFAlgorithmID
    kdf_parameters=b"...",              # msKds-KDFParam
    secret_algorithm="DH",              # mskds-SecretAgreementAlgorithmID
    secret_parameters=b"...",           # msKds-SecretAgreementParam
    private_key_length=512,             # msKds-PrivateKeyLength
    public_key_length=2048,             # msKds-PublicKeyLength
)

dpapi_ng.ncrypt_unprotect_secret(b"...", cache=cache)
```

Currently the `SP800_108_CTR_HMAC` KDF algorithm and `DH`, `ECDH_P256`, and `ECDH_P384` secret agreement algorithms have been tested to work.
The `ECDH_P521` secret agreement algorithm should also work but has been untested as a test environment cannot be created with it right now.

## Special Thanks

I would like to thank the following people (GitHub or Twitter handles in brackets) for their help on this project:

* Georg Sieber (@schorschii) for implementing encryption support
* Grzegorz Tworek (@0gtweet) and Michał Grzegorzewski for providing more information on the internal BCrypt* API workflow used in DPAPI-NG
* Marc-André Moreau (@awakecoding) for their help with reverse engineering some of the Windows APIs and talking through some theories
* SkelSec (@SkelSec) for help on the RPC calls and being available as a general sounding board for my theories
* Steve Syfuhs (@SteveSyfuhs) for connecting me with some Microsoft engineers to help understand some undocumented logic

Without their patience and knowledge this probably would not have been possible.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "dpapi-ng",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "dpapi,dpapi-ng,laps",
    "author": "",
    "author_email": "Jordan Borean <jborean93@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/ac/a9/246f56c03e45cf5d7fad81b66405dd01c2304c0d228b10b95cb4d3308592/dpapi-ng-0.2.0.tar.gz",
    "platform": null,
    "description": "# dpapi_ng - Python DPAPI-NG De-/Encryption Library\n\n[![Test workflow](https://github.com/jborean93/dpapi-ng/actions/workflows/ci.yml/badge.svg)](https://github.com/jborean93/dpapi-ng/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/jborean93/dpapi-ng/branch/main/graph/badge.svg?token=UEA7VoocS5)](https://codecov.io/gh/jborean93/dpapi-ng)\n[![PyPI version](https://badge.fury.io/py/dpapi-ng.svg)](https://badge.fury.io/py/dpapi-ng)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jborean93/dpapi-ng/blob/main/LICENSE)\n\nLibrary for [DPAPI NG](https://learn.microsoft.com/en-us/windows/win32/seccng/cng-dpapi), also known as CNG DPAPI, de- and encryption in Python.\nIt is designed to replicate the behaviour of [NCryptUnprotectSecret](https://learn.microsoft.com/en-us/windows/win32/api/ncryptprotect/nf-ncryptprotect-ncryptunprotectsecret) and [NCryptProtectSecret](https://learn.microsoft.com/en-us/windows/win32/api/ncryptprotect/nf-ncryptprotect-ncryptprotectsecret).\nThis can be used on non-Windows hosts to de-/encrypt DPAPI NG protected secrets, like PFX user protected password, or LAPS encrypted password.\nIt can either decrypt any DPAPI NG blobs using an offline copy of the domain's root key or de-/encrypt by using the credentials of the supplied user to retrieve the required information over RPC.\n\nCurrently only these protection descriptors are supported:\n\n|Type|Purpose|\n|-|-|\n|SID|Only the SID user or members of the SID group can decrypt the secret|\n\nThis implements the [MS-GKDI Group Key Distribution Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/943dd4f6-6b80-4a66-8594-80df6d2aad0a).\n\n## Requirements\n\n* CPython 3.7+\n* [cryptography](https://pypi.org/project/cryptography/)\n* [dnspython >= 2.0.0](https://pypi.org/project/dnspython/)\n* [pyspnego >= 0.9.0](https://pypi.org/project/pyspnego/)\n\n## How to Install\n\nTo install dpapi-ng with all the basic features, run\n\n```bash\npython -m pip install dpapi-ng\n```\n\n### Kerberos Authentication\n\nKerberos authentication support won't be installed by default as it relies on system libraries and a valid compiler to be present.\nThe krb5 library and compiler can be installed by installing these packages:\n\n```bash\n# Debian/Ubuntu\napt-get install gcc python3-dev libkrb5-dev\n\n# Centos/RHEL\nyum install gcc python-devel krb5-devel\n\n# Fedora\ndnf install gcc python-devel krb5-devel\n\n# Arch Linux\npacman -S gcc krb5\n```\n\nOnce installed, the Kerberos Python extras can be installed with\n\n```bash\npython -m pip install dpapi-ng[kerberos]\n```\n\nKerberos also needs to be configured to talk to the domain but that is outside the scope of this page.\n\n### From Source\n\n```bash\ngit clone https://github.com/jborean93/dpapi-ng.git\ncd dpapi-ng\npip install -e .\n```\n\n## Examples\n\nThere is both a sync and asyncio API available to de-/encrypt a blob.\n\n```python\nimport dpapi_ng\n\n\n### DECRYPTION ###\ndpapi_ng_blob = b\"...\"\ndecrypted_blob = dpapi_ng.ncrypt_unprotect_secret(dpapi_ng_blob)\n\n# async equivalent to the above\ndecrypted_blob = await dpapi_ng.async_ncrypt_unprotect_secret(dpapi_ng_blob)\n\n\n### ENCRYPTION ###\ndata = b\"...\"\ntarget_sid = \"S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX\"\ndpapi_ng_blob = dpapi_ng.ncrypt_protect_secret(data, target_sid)\n\n# async equivalent to the above\ndpapi_ng_blob = await dpapi_ng.async_ncrypt_protect_secret(data, target_sid)\n```\n\nTo decrypt the blob, the key specified in the blob needs to be retrieved from the domain controller the blob was generated by.\nTo encrypt a blob, the group key of the target SID specified needs to be retrieved.\nFor decryption, the domain controller hostname is automatically retrieved through an DNS `SRV` lookup of `_ldap._tcp.dc._msdcs.{domain_name}` with the domain name found in the DPAPI-NG blob or with the value specified in the `server` kwarg.\nFor encryption, the domain controller hostname is either determined using the `domain_name` kwarg for a DNS `SRV` lookup, the `server` kwarg directly or by using the system's search domain (if available).\nIt will attempt to authenticate with the current user identifier which on Linux will only exist if `kinit` has already been called to retrieve a user's ticket.\nOtherwise if no identity is available, the `username` and `password` kwargs can be used to specify a custom user.\n\nThe following kwargs can be used for `ncrypt_unprotect_secret`, `async_ncrypt_unprotect_secret`, `ncrypt_protect_secret` and `async_ncrypt_protect_secret`.\n\n* `server`: Use this server as the RPC target if a key needs to be retrieved\n* `username`: The username to authenticate as for the RPC connection\n* `password`: The password to authenticate with for the RPC connection\n* `auth_protocol`: The authentication protocol (`negotiate`, `kerberos`, `ntlm`) to use for the RPC connection\n* `cache`: A cache to store keys retrieved for future operation\n\nIn addition to that, `ncrypt_protect_secret` and `async_ncrypt_protect_secret` take the following additional kwarg.\n\n* `domain_name`: The name of the domain/forest to use when looking up the RPC target to use when retrieving the key info\n* `root_key_identifier`: The root key identifier UUID, necessary in order to make the cache work for these functions\n\nIt is also possible to encrypt and decrypt the DPAPI-NG blob by providing the root key stored in the domain.\nThis can either be retrieved using an offline attack or through an LDAP query if running as a Domain Admin user.\nTo retrieve the domain root keys using PowerShell the following can be run:\n\n```powershell\n$configurationContext = (Get-ADRootDSE).configurationNamingContext\n$getParams = @{\n    LDAPFilter = '(objectClass=msKds-ProvRootKey)'\n    SearchBase = \"CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,$configurationContext\"\n    SearchScope = 'OneLevel'\n    Properties = @(\n        'cn'\n        'msKds-KDFAlgorithmID'\n        'msKds-KDFParam'\n        'msKds-SecretAgreementAlgorithmID'\n        'msKds-SecretAgreementParam'\n        'msKds-PrivateKeyLength'\n        'msKds-PublicKeyLength'\n        'msKds-RootKeyData'\n    )\n}\nGet-ADObject @getParams | ForEach-Object {\n    [PSCustomObject]@{\n        Version = 1\n        RootKeyId = [Guid]::new($_.cn)\n        KdfAlgorithm = $_.'msKds-KDFAlgorithmID'\n        KdfParameters = [System.Convert]::ToBase64String($_.'msKds-KDFParam')\n        SecretAgreementAlgorithm = $_.'msKds-SecretAgreementAlgorithmID'\n        SecretAgreementParameters = [System.Convert]::ToBase64String($_.'msKds-SecretAgreementParam')\n        PrivateKeyLength = $_.'msKds-PrivateKeyLength'\n        PublicKeyLength = $_.'msKds-PublicKeyLength'\n        RootKeyData = [System.Convert]::ToBase64String($_.'msKds-RootKeyData')\n    }\n}\n```\n\nThe following `ldapsearch` command can be used outside of Windows:\n\n```bash\nldapsearch \\\n    -b 'CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,DC=domain,DC=test' \\\n    -s one \\\n    '(objectClass=msKds-ProvRootKey)' \\\n    cn \\\n    msKds-KDFAlgorithmID \\\n    msKds-KDFParam \\\n    msKds-SecretAgreementAlgorithmID \\\n    msKds-SecretAgreementParam \\\n    msKds-PrivateKeyLength \\\n    msKds-PublicKeyLength \\\n    msKds-RootKeyData\n```\n\n_Note: ldapsearch will most likely need the -H and user bind information to succeed._\n\nThe information retrieved there can be stored in a cache and used for subsequent `ncrypt_protect_secret` and `ncrypt_unprotect_secret` calls:\n\n```python\nimport uuid\n\nimport dpapi_ng\n\ncache = dpapi_ng.KeyCache()\n\nroot_key_id = uuid.UUID(\"76ec8b2d-d444-4f67-9db7-2f62b4358b35\")\ncache.load_key(\n    b\"...\",                             # msKds-RootKeydata\n    root_key_id,                        # cn\n    version=1,\n    kdf_algorithm=\"SP800_108_CTR_HMAC\", # msKds-KDFAlgorithmID\n    kdf_parameters=b\"...\",              # msKds-KDFParam\n    secret_algorithm=\"DH\",              # mskds-SecretAgreementAlgorithmID\n    secret_parameters=b\"...\",           # msKds-SecretAgreementParam\n    private_key_length=512,             # msKds-PrivateKeyLength\n    public_key_length=2048,             # msKds-PublicKeyLength\n)\n\ndpapi_ng.ncrypt_unprotect_secret(b\"...\", cache=cache)\n```\n\nCurrently the `SP800_108_CTR_HMAC` KDF algorithm and `DH`, `ECDH_P256`, and `ECDH_P384` secret agreement algorithms have been tested to work.\nThe `ECDH_P521` secret agreement algorithm should also work but has been untested as a test environment cannot be created with it right now.\n\n## Special Thanks\n\nI would like to thank the following people (GitHub or Twitter handles in brackets) for their help on this project:\n\n* Georg Sieber (@schorschii) for implementing encryption support\n* Grzegorz Tworek (@0gtweet) and Micha\u0142 Grzegorzewski for providing more information on the internal BCrypt* API workflow used in DPAPI-NG\n* Marc-Andr\u00e9 Moreau (@awakecoding) for their help with reverse engineering some of the Windows APIs and talking through some theories\n* SkelSec (@SkelSec) for help on the RPC calls and being available as a general sounding board for my theories\n* Steve Syfuhs (@SteveSyfuhs) for connecting me with some Microsoft engineers to help understand some undocumented logic\n\nWithout their patience and knowledge this probably would not have been possible.\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2023 Jordan Borean, Red Hat  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
    "summary": "DPAPI NG decryption for Python",
    "version": "0.2.0",
    "project_urls": {
        "homepage": "https://github.com/jborean93/dpapi-ng"
    },
    "split_keywords": [
        "dpapi",
        "dpapi-ng",
        "laps"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d09348b8f2e58cd6dece91afc4035c18f3f2fd0045bd16a751413ce73005178b",
                "md5": "f62f35678b9d2a3e060d4dda29697257",
                "sha256": "75572bab45ca280d0d836062f880c9d2f29b30e5d294d9158d35e98bde3ba38b"
            },
            "downloads": -1,
            "filename": "dpapi_ng-0.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f62f35678b9d2a3e060d4dda29697257",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 52907,
            "upload_time": "2023-06-02T01:09:45",
            "upload_time_iso_8601": "2023-06-02T01:09:45.723594Z",
            "url": "https://files.pythonhosted.org/packages/d0/93/48b8f2e58cd6dece91afc4035c18f3f2fd0045bd16a751413ce73005178b/dpapi_ng-0.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "aca9246f56c03e45cf5d7fad81b66405dd01c2304c0d228b10b95cb4d3308592",
                "md5": "7e8228afe74ed0ab3dfb9e9fd3042547",
                "sha256": "e6fca84a13586ca93901181af7c47644fc31f729de18f00b20cfd8dad4d48ef0"
            },
            "downloads": -1,
            "filename": "dpapi-ng-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "7e8228afe74ed0ab3dfb9e9fd3042547",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 59253,
            "upload_time": "2023-06-02T01:09:47",
            "upload_time_iso_8601": "2023-06-02T01:09:47.679713Z",
            "url": "https://files.pythonhosted.org/packages/ac/a9/246f56c03e45cf5d7fad81b66405dd01c2304c0d228b10b95cb4d3308592/dpapi-ng-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-06-02 01:09:47",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jborean93",
    "github_project": "dpapi-ng",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "dpapi-ng"
}
        
Elapsed time: 0.07576s