Create script to generate PGP Smart Card AIDs
This commit is contained in:
commit
ff1b434178
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"license.author": "c0de <c0de@c0de.dev>",
|
||||||
|
"license.extension": ".md",
|
||||||
|
"license.filename": "LICENSE",
|
||||||
|
"license.year": "auto"
|
||||||
|
}
|
28
LICENSE.md
Normal file
28
LICENSE.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023, c0de <c0de@c0de.dev>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# PGP AID Generator
|
||||||
|
|
||||||
|
Script to generate a valid Application Identifier (AID) for an implementation
|
||||||
|
of a PGP Smart Cart (such as SmartPGP)
|
||||||
|
|
||||||
|
The AID is 16 bytes in length and must be unique (multiple cards should have different AIDs).
|
||||||
|
The AID is written to the Smart Card during application install, and can not be changed without
|
||||||
|
reinstalling the `SmartPGP.cap`
|
||||||
|
|
||||||
|
## The structure of the AID is described as follows
|
||||||
|
|
||||||
|
The first 6 bytes are static and always `0xD2 0x76 0x00 0x01 0x24 0x01`. The next 2 bytes represent the version of the PGP Specification, which is `0x03 0x04` for SmartPGP (PGP 3.4)
|
||||||
|
|
||||||
|
The following 2 bytes are the "Manufacturer ID"; They're supposed to be registered by OpenPGP devs, but using an unknown one doesn't cause issues, and it looks better than "Test Card".
|
||||||
|
|
||||||
|
Additionally, newer versions of OpenPGP include a new "Manufacturer Name" attribute, that is not part of the AID
|
||||||
|
|
||||||
|
[Official Manufacturer IDs](https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=scd/app-openpgp.c;hb=HEAD#l292)
|
||||||
|
|
||||||
|
The manufacturer id this script uses is `0xC0 0xDE`, feel free to change this to something else.
|
||||||
|
|
||||||
|
Following the manufacturer id, the next 4 bytes are for the serial number. This must be unique for all cards that share the same manufacturer id.
|
||||||
|
|
||||||
|
There are 2 bytes after the serial number, that are static and always `0x00 0x00` (reserved for future use)
|
||||||
|
|
||||||
|
To generate the serial number, you must provide this script with the following information:
|
||||||
|
|
||||||
|
1. The `ICSerialNumber` value from `gp -i`
|
||||||
|
2. One or more domain names that you control
|
||||||
|
|
||||||
|
The UUID of `ICSerialNumber` will be created using the namespace of the IC Manufacturer; In this
|
||||||
|
case, we are using Infineon. You should define the namespace for your device if different.
|
||||||
|
|
||||||
|
For every domain name provided, its UUID will be calculated, in the DNS namespace.
|
||||||
|
All UUIDs will then be concatinated together, and the SHA1 hash will be taken from that.
|
||||||
|
|
||||||
|
The SHA1 hash will then have a UUID generated from that, in the SHA1 namespace.
|
||||||
|
The first 8 characters of the resulting UUID are the serial number.
|
||||||
|
|
||||||
|
A complete AID that could be produced by this script is: `d276000124010304c0decdf177610000`
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
aid_generator.py <ic_serial_number> <domain_name>...
|
||||||
|
|
101
aid_generator.py
Normal file
101
aid_generator.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Script to generate a valid Application Identifier (AID) for an implementation
|
||||||
|
of a PGP Smart Cart (such as SmartPGP)
|
||||||
|
|
||||||
|
The AID is 16 bytes in length and must be unique (multiple cards should have different AIDs).
|
||||||
|
The AID is written to the Smart Card during application install, and can not be changed without
|
||||||
|
reinstalling the `SmartPGP.cap`
|
||||||
|
|
||||||
|
The structure of the AID is described as follows:
|
||||||
|
The first 6 bytes are static and always `0xD2 0x76 0x00 0x01 0x24 0x01`. The next 2 bytes represent
|
||||||
|
the version of the PGP Specification, which is `0x03 0x04` for SmartPGP (PGP 3.4)
|
||||||
|
|
||||||
|
The following 2 bytes are the "Manufacturer ID"; They're supposed to be registered by OpenPGP devs,
|
||||||
|
but using an unknown one doesn't cause issues, and it looks better than "Test Card". Additionally,
|
||||||
|
newer versions of OpenPGP include a new "Manufacturer Name" attribute, that is not part of the AID
|
||||||
|
|
||||||
|
The list of registered manufacturer ids can be found here:
|
||||||
|
https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=scd/app-openpgp.c;hb=HEAD#l292
|
||||||
|
|
||||||
|
The manufacturer id this script uses is `0xC0 0xDE`, feel free to change this to something else.
|
||||||
|
|
||||||
|
Following the manufacturer id, the next 4 bytes are for the serial number. This must be unique for
|
||||||
|
all cards that share the same manufacturer id. There are 2 bytes after the serial number, that are
|
||||||
|
static and always `0x00 0x00` (reserved for future use)
|
||||||
|
|
||||||
|
To generate the serial number, you must provide this script with the following information:
|
||||||
|
|
||||||
|
1. The `ICSerialNumber` value from `gp -i`
|
||||||
|
2. One or more domain names that you control
|
||||||
|
|
||||||
|
The UUID of `ICSerialNumber` will be created using the namespace of the IC Manufacturer; In this
|
||||||
|
case, we are using Infineon. You should define the namespace for your device if different.
|
||||||
|
|
||||||
|
For every domain name provided, its UUID will be calculated, in the DNS namespace.
|
||||||
|
All UUIDs will then be concatinated together, and the SHA1 hash will be taken from that.
|
||||||
|
|
||||||
|
The SHA1 hash will then have a UUID generated from that, in the SHA1 namespace.
|
||||||
|
The first 8 characters of the resulting UUID are the serial number.
|
||||||
|
|
||||||
|
A complete AID that could be produced by this script is: `d276000124010304c0decdf177610000`
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
aid_generator.py <ic_serial_number> <domain_name>...
|
||||||
|
aid_generator.py (-h | --help)
|
||||||
|
aid_generator.py --version
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h --help Show this screen
|
||||||
|
--version Show current version
|
||||||
|
"""
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from docopt import docopt
|
||||||
|
|
||||||
|
AID_PREFIX = "d276000124010304" # RID:10, APP:2, SPEC:4
|
||||||
|
AID_POSTFIX = "0000" # RFU:4
|
||||||
|
MANUFACTURER_ID = "c0de"
|
||||||
|
|
||||||
|
NAMESPACE_INFINEON: uuid.UUID = uuid.uuid5(uuid.NAMESPACE_OID, "1.2.276.0.68")
|
||||||
|
"""UUID Namespace for Infineon devices, defined from OID Namespace
|
||||||
|
|
||||||
|
https://oid-rep.orange-labs.fr/get/1.2.276.0.68"""
|
||||||
|
|
||||||
|
NAMESPACE_SHAONE: uuid.UUID = uuid.uuid5(uuid.NAMESPACE_OID, "1.3.14.3.2.26")
|
||||||
|
"""UUID Namespace for SHA1 hashes, defined from OID Namespace
|
||||||
|
|
||||||
|
https://oid-rep.orange-labs.fr/get/1.3.14.3.2.26"""
|
||||||
|
|
||||||
|
|
||||||
|
def main(options: dict[str, Any]) -> None:
|
||||||
|
"""Main method"""
|
||||||
|
|
||||||
|
uuid_hash = hashlib.sha1()
|
||||||
|
uuid_result: uuid.UUID
|
||||||
|
aid_result: str
|
||||||
|
|
||||||
|
if (
|
||||||
|
len(options.get("<ic_serial_number>", "")) > 0
|
||||||
|
and len(options.get("<domain_name>", [])) > 0
|
||||||
|
):
|
||||||
|
ic_uuid: uuid.UUID = uuid.uuid5(
|
||||||
|
NAMESPACE_INFINEON, str(options["<ic_serial_number>"])
|
||||||
|
)
|
||||||
|
uuid_hash.update(ic_uuid.bytes)
|
||||||
|
|
||||||
|
for domain in list(options["<domain_name>"]):
|
||||||
|
uuid_hash.update(uuid.uuid5(uuid.NAMESPACE_DNS, str(domain)).bytes)
|
||||||
|
|
||||||
|
uuid_result = uuid.uuid5(NAMESPACE_SHAONE, uuid_hash.hexdigest())
|
||||||
|
aid_result = f"{AID_PREFIX}{MANUFACTURER_ID}{uuid_result.hex[:8]}{AID_POSTFIX}"
|
||||||
|
print(f"Your AID is: {aid_result}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
arguments: dict[str, Any] = docopt(__doc__, version="2023.08.1")
|
||||||
|
main(arguments)
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
docopt==0.6.2
|
Loading…
Reference in New Issue
Block a user