예제 #1
0
def sign_certificate_builder(
    session: PivSession,
    slot: SLOT,
    key_type: KEY_TYPE,
    builder: x509.CertificateBuilder,
) -> x509.Certificate:
    """Sign a Certificate."""
    dummy_key = _dummy_key(key_type)
    cert = builder.sign(dummy_key, hashes.SHA256(), default_backend())

    sig = session.sign(
        slot,
        key_type,
        cert.tbs_certificate_bytes,
        hashes.SHA256(),
        padding.PKCS1v15(),  # Only used for RSA
    )

    seq = Tlv.parse_list(Tlv.unpack(0x30, cert.public_bytes(Encoding.DER)))
    # Replace signature, add unused bits = 0
    seq[2] = Tlv(seq[2].tag, b"\0" + sig)
    # Re-assemble sequence
    der = Tlv(0x30, b"".join(seq))

    return x509.load_der_x509_certificate(der, default_backend())
예제 #2
0
    def generate_ec_key(self, key_slot, curve_name, timestamp=None):
        """Requires Admin PIN verification."""
        if timestamp is None:
            timestamp = int(time.time())

        attributes = _format_ec_attributes(key_slot, curve_name)
        self._put_data(key_slot.key_id, attributes)
        resp = self._app.send_apdu(0, INS.GENERATE_ASYM, 0x80, 0x00, key_slot.crt)

        data = Tlv.parse_dict(Tlv.unwrap(0x7F49, resp))
        pubkey_enc = data[0x86]

        self._put_data(key_slot.gen_time, struct.pack(">I", timestamp))
        # TODO: Calculate and write fingerprint

        if curve_name == "x25519":
            # Added in 2.0
            from cryptography.hazmat.primitives.asymmetric import x25519

            return x25519.X25519PublicKey.from_public_bytes(pubkey_enc)
        if curve_name == "ed25519":
            # Added in 2.6
            from cryptography.hazmat.primitives.asymmetric import ed25519

            return ed25519.Ed25519PublicKey.from_public_bytes(pubkey_enc)

        curve = getattr(ec, curve_name.upper())
        try:
            # Added in cryptography 2.5
            return ec.EllipticCurvePublicKey.from_encoded_point(curve(), pubkey_enc)
        except AttributeError:
            return ec.EllipticCurvePublicNumbers.from_encoded_point(
                curve(), pubkey_enc
            ).public_key(default_backend())
예제 #3
0
class KEY_SLOT(_KeySlot, Enum):  # noqa: N801
    SIG = _KeySlot("SIGNATURE", 1, 0xC1, 0xC7, 0xCE, 0xD6, Tlv(0xB6))
    ENC = _KeySlot("ENCRYPTION", 2, 0xC2, 0xC8, 0xCF, 0xD7, Tlv(0xB8))
    AUT = _KeySlot("AUTHENTICATION", 3, 0xC3, 0xC9, 0xD0, 0xD8, Tlv(0xA4))
    ATT = _KeySlot(
        "ATTESTATION", 4, 0xDA, 0xDB, 0xDD, 0xD9, Tlv(0xB6, Tlv(0x84, b"\x81"))
    )
예제 #4
0
    def generate_rsa_key(self, key_slot, key_size, timestamp=None):
        """Requires Admin PIN verification."""
        if (4, 2, 0) <= self.version < (4, 3, 5):
            raise NotSupportedError(
                "RSA key generation not supported on this YubiKey")

        if timestamp is None:
            timestamp = int(time.time())

        neo = self.version < (4, 0, 0)
        if not neo:
            attributes = _format_rsa_attributes(key_size)
            self._put_data(key_slot.key_id, attributes)
        elif key_size != 2048:
            raise ValueError("Unsupported key size!")
        resp = self._app.send_apdu(0, INS.GENERATE_ASYM, 0x80, 0x00,
                                   key_slot.crt)

        data = Tlv.parse_dict(Tlv.unpack(0x7F49, resp))
        numbers = rsa.RSAPublicNumbers(bytes2int(data[0x82]),
                                       bytes2int(data[0x81]))

        self._put_data(key_slot.gen_time, struct.pack(">I", timestamp))
        # TODO: Calculate and write fingerprint

        return numbers.public_key(default_backend())
예제 #5
0
 def _pack_tlvs(tlvs):
     header = b""
     body = b""
     for tlv in tlvs:
         header += tlv[: -tlv.length]
         body += tlv.value
     return Tlv(0x7F48, header) + Tlv(0x5F48, body)
예제 #6
0
def sign_csr_builder(
    session: PivSession,
    slot: SLOT,
    public_key: Union[rsa.RSAPublicKey, ec.EllipticCurvePublicKey],
    builder: x509.CertificateSigningRequestBuilder,
) -> x509.CertificateSigningRequest:
    """Sign a CSR."""
    key_type = KEY_TYPE.from_public_key(public_key)
    dummy_key = _dummy_key(key_type)
    csr = builder.sign(dummy_key, hashes.SHA256(), default_backend())
    seq = Tlv.parse_list(Tlv.unpack(0x30, csr.public_bytes(Encoding.DER)))

    # Replace public key
    pub_format = (PublicFormat.PKCS1 if key_type.algorithm == ALGORITHM.RSA
                  else PublicFormat.SubjectPublicKeyInfo)
    dummy_bytes = dummy_key.public_key().public_bytes(Encoding.DER, pub_format)
    pub_bytes = public_key.public_bytes(Encoding.DER, pub_format)
    seq[0] = Tlv(seq[0].replace(dummy_bytes, pub_bytes))

    sig = session.sign(
        slot,
        key_type,
        seq[0],
        hashes.SHA256(),
        padding.PKCS1v15(),  # Only used for RSA
    )

    # Replace signature, add unused bits = 0
    seq[2] = Tlv(seq[2].tag, b"\0" + sig)
    # Re-assemble sequence
    der = Tlv(0x30, b"".join(seq))

    return x509.load_der_x509_csr(der, default_backend())
예제 #7
0
 def __init__(self, raw_data=Tlv(0x80)):
     data = Tlv.parse_dict(Tlv(raw_data).value)
     self._flags = struct.unpack(">B",
                                 data[0x81])[0] if 0x81 in data else None
     self.salt = data.get(0x82)
     self.pin_timestamp = struct.unpack(
         ">I", data[0x83]) if 0x83 in data else None
예제 #8
0
 def _select_certificate(self, key_slot):
     self._app.send_apdu(
         0,
         INS.SELECT_DATA,
         3 - key_slot.index,
         0x04,
         Tlv(0, Tlv(0x60, Tlv(0x5C, b"\x7f\x21")))[1:],
     )
예제 #9
0
 def get_bytes(self) -> bytes:
     data = b""
     if self._flags is not None:
         data += Tlv(0x81, struct.pack(">B", self._flags))
     if self.salt is not None:
         data += Tlv(0x82, self.salt)
     if self.pin_timestamp is not None:
         data += Tlv(0x83, struct.pack(">I", self.pin_timestamp))
     return Tlv(0x80, data)
예제 #10
0
def is_pkcs12(data):
    """
    Tries to identify a PKCS12 container.
    The PFX PDU version is assumed to be v3.
    See: https://tools.ietf.org/html/rfc7292.
    """
    try:
        header = Tlv.parse_list(Tlv.unpack(0x30, data))[0]
        return header.tag == 0x02 and header.value == b"\x03"
    except ValueError:
        return False
예제 #11
0
def generate_chuid() -> bytes:
    """Generates a CHUID (Cardholder Unique Identifier)."""
    # Non-Federal Issuer FASC-N
    # [9999-9999-999999-0-1-0000000000300001]
    FASC_N = (b"\xd4\xe7\x39\xda\x73\x9c\xed\x39\xce\x73\x9d\x83\x68" +
              b"\x58\x21\x08\x42\x10\x84\x21\xc8\x42\x10\xc3\xeb")
    # Expires on: 2030-01-01
    EXPIRY = b"\x32\x30\x33\x30\x30\x31\x30\x31"

    return (Tlv(0x30, FASC_N) + Tlv(0x34, os.urandom(16)) + Tlv(0x35, EXPIRY) +
            Tlv(0x3E) + Tlv(TAG_LRC))
예제 #12
0
 def _select_certificate(self, key_slot):
     data = Tlv(0x60, Tlv(0x5C, b"\x7f\x21"))
     if self.version <= (
             5, 4, 3):  # These use a non-standard byte in the command.
         data = b"\x06" + data  # 6 is the length of the data.
     self._app.send_apdu(
         0,
         INS.SELECT_DATA,
         3 - key_slot.index,
         0x04,
         data,
     )
예제 #13
0
def is_pkcs12(data):
    """
    Tries to identify a PKCS12 container.
    The PFX PDU version is assumed to be v3.
    See: https://tools.ietf.org/html/rfc7292.
    """
    try:
        header = Tlv.parse_from(Tlv.unpack(0x30, data))[0]
        return header.tag == 0x02 and header.value == b"\x03"
    except ValueError as e:
        logger.debug("Unable to parse TLV", exc_info=e)
    return False
예제 #14
0
    def test_tlv(self):
        self.assertEqual(Tlv(b"\xfe\6foobar"), Tlv(0xFE, b"foobar"))

        tlv1 = Tlv(b"\0\5hello")
        tlv2 = Tlv(0xFE, b"")
        tlv3 = Tlv(0x12, b"hi" * 200)

        self.assertEqual(b"\0\5hello", tlv1)
        self.assertEqual(b"\xfe\0", tlv2)
        self.assertEqual(b"\x12\x82\x01\x90" + b"hi" * 200, tlv3)

        self.assertEqual(b"\0\5hello\xfe\0\x12\x82\x01\x90" + b"hi" * 200,
                         tlv1 + tlv2 + tlv3)
예제 #15
0
 def _select_certificate(self, key_slot):
     try:
         require_version(self.version, (5, 2, 0))
         data: bytes = Tlv(0x60, Tlv(0x5C, b"\x7f\x21"))
         if self.version <= (5, 4, 3):
             # These use a non-standard byte in the command.
             data = b"\x06" + data  # 6 is the length of the data.
         self._app.send_apdu(
             0,
             INS.SELECT_DATA,
             3 - key_slot.indx,
             0x04,
             data,
         )
     except NotSupportedError:
         if key_slot == KEY_SLOT.AUT:
             return  # Older version still support AUT, which is the default slot.
         raise
예제 #16
0
 def parse(cls, data: bytes) -> "KdfData":
     fields = Tlv.parse_dict(data)
     return cls(
         _parse_int(fields, 0x81, KdfAlgorithm, KdfAlgorithm.NONE),
         _parse_int(fields, 0x82, HashAlgorithm),
         _parse_int(fields, 0x83),
         fields.get(0x84),
         fields.get(0x85),
         fields.get(0x86),
         fields.get(0x87),
         fields.get(0x88),
     )
예제 #17
0
def _get_key_template(key, key_slot, crt=False):
    def _pack_tlvs(tlvs):
        header = b""
        body = b""
        for tlv in tlvs:
            header += tlv[: -tlv.length]
            body += tlv.value
        return Tlv(0x7F48, header) + Tlv(0x5F48, body)

    values: Tuple[Tlv, ...]

    if isinstance(key, rsa.RSAPrivateKeyWithSerialization):
        rsa_numbers = key.private_numbers()
        ln = (key.key_size // 8) // 2

        e = Tlv(0x91, b"\x01\x00\x01")  # e=65537
        p = Tlv(0x92, int2bytes(rsa_numbers.p, ln))
        q = Tlv(0x93, int2bytes(rsa_numbers.q, ln))
        values = (e, p, q)
        if crt:
            dp = Tlv(0x94, int2bytes(rsa_numbers.dmp1, ln))
            dq = Tlv(0x95, int2bytes(rsa_numbers.dmq1, ln))
            qinv = Tlv(0x96, int2bytes(rsa_numbers.iqmp, ln))
            n = Tlv(0x97, int2bytes(rsa_numbers.public_numbers.n, 2 * ln))
            values += (dp, dq, qinv, n)

    elif isinstance(key, ec.EllipticCurvePrivateKeyWithSerialization):
        ec_numbers = key.private_numbers()
        ln = key.key_size // 8

        privkey = Tlv(0x92, int2bytes(ec_numbers.private_value, ln))
        values = (privkey,)

    elif _get_curve_name(key) in ("ed25519", "x25519"):
        privkey = Tlv(
            0x92, key.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
        )
        values = (privkey,)

    return Tlv(0x4D, key_slot.crt + _pack_tlvs(values))
    def test_read_write_certificate_as_object(self, ykman_cli):
        with pytest.raises(SystemExit):
            ykman_cli("piv", "objects", "export",
                      hex(OBJECT_ID.AUTHENTICATION), "-")

        cert = generate_self_signed_certificate()
        cert_bytes_der = cert.public_bytes(encoding=serialization.Encoding.DER)

        input_tlv = Tlv(0x70, cert_bytes_der) + Tlv(0x71, b"\0") + Tlv(
            0xFE, b"")

        ykman_cli(
            "piv",
            "objects",
            "import",
            hex(OBJECT_ID.AUTHENTICATION),
            "-",
            "-m",
            DEFAULT_MANAGEMENT_KEY,
            input=input_tlv,
        )

        output1 = ykman_cli("piv", "objects", "export",
                            hex(OBJECT_ID.AUTHENTICATION), "-").stdout_bytes
        output_cert_bytes = Tlv.parse_dict(output1)[0x70]
        assert output_cert_bytes == cert_bytes_der

        output2 = ykman_cli(
            "piv",
            "certificates",
            "export",
            hex(SLOT.AUTHENTICATION),
            "-",
            "--format",
            "DER",
        ).stdout_bytes
        assert output2 == cert_bytes_der
예제 #19
0
    def generate_rsa_key(self, key_slot, key_size, timestamp=None):
        """Requires Admin PIN verification."""
        ensure_not_cve201715361_vulnerable_firmware_version(self.version)

        if timestamp is None:
            timestamp = int(time.time())

        neo = self.version < (4, 0, 0)
        if not neo:
            attributes = _format_rsa_attributes(key_size)
            self._put_data(key_slot.key_id, attributes)
        elif key_size != 2048:
            raise ValueError("Unsupported key size!")
        resp = self._app.send_apdu(0, INS.GENERATE_ASYM, 0x80, 0x00, key_slot.crt)

        data = Tlv.parse_dict(Tlv.unwrap(0x7F49, resp))
        numbers = rsa.RSAPublicNumbers(
            int_from_bytes(data[0x82], "big"), int_from_bytes(data[0x81], "big")
        )

        self._put_data(key_slot.gen_time, struct.pack(">I", timestamp))
        # TODO: Calculate and write fingerprint

        return numbers.public_key(default_backend())
예제 #20
0
    def test_parse_tlvs(self):
        tlvs = Tlv.parse_list(b"\x00\x02\xd0\x0d\xa1\x00\xfe\x04\xfe\xed\xfa\xce")
        self.assertEqual(3, len(tlvs))

        self.assertEqual(0, tlvs[0].tag)
        self.assertEqual(2, tlvs[0].length)
        self.assertEqual(b"\xd0\x0d", tlvs[0].value)

        self.assertEqual(0xA1, tlvs[1].tag)
        self.assertEqual(0, tlvs[1].length)
        self.assertEqual(b"", tlvs[1].value)

        self.assertEqual(0xFE, tlvs[2].tag)
        self.assertEqual(4, tlvs[2].length)
        self.assertEqual(b"\xfe\xed\xfa\xce", tlvs[2].value)
예제 #21
0
def generate_ccc() -> bytes:
    """Generates a CCC (Card Capability Container)."""
    return (Tlv(0xF0, b"\xa0\x00\x00\x01\x16\xff\x02" + os.urandom(14)) +
            Tlv(0xF1, b"\x21") + Tlv(0xF2, b"\x21") + Tlv(0xF3) +
            Tlv(0xF4, b"\x00") + Tlv(0xF5, b"\x10") + Tlv(0xF6) + Tlv(0xF7) +
            Tlv(0xFA) + Tlv(0xFB) + Tlv(0xFC) + Tlv(0xFD) + Tlv(TAG_LRC))
예제 #22
0
 def __init__(self, raw_data: bytes = Tlv(0x88)):
     data = Tlv.parse_dict(Tlv(raw_data).value)
     self.key = data.get(0x89)
예제 #23
0
 def get_bytes(self) -> bytes:
     data = b""
     if self.key is not None:
         data += Tlv(0x89, self.key)
     return Tlv(0x88, data)