Skip to content

Commit

Permalink
Credv2: Added proper subject fields instead of just commonName for LP…
Browse files Browse the repository at this point in the history
…S creds

The initial implementation for LPS-enabled credentials was just allowing the commonName OID instead of the full set of X.500 Distinguished Names.

This should fix #35, by reusing as much as possible the X509 layer already in Scapy. Added some test cases as well.
  • Loading branch information
martingalloar committed May 11, 2021
1 parent 57ce0a7 commit c6ed6b7
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 20 deletions.
54 changes: 42 additions & 12 deletions pysap/SAPCredv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@
from binascii import unhexlify
# External imports
from scapy.packet import Packet
from scapy.compat import plain_str
from scapy.asn1packet import ASN1_Packet
from scapy.fields import (ByteField, ByteEnumField, ShortField, StrField, StrFixedLenField)
from scapy.asn1.asn1 import ASN1_IA5_STRING, ASN1_Codecs
from scapy.layers.x509 import (X509_RDN, X509_AttributeTypeAndValue,
_attrName_mapping, _attrName_specials)
from scapy.asn1.asn1 import (ASN1_IA5_STRING, ASN1_Codecs, ASN1_PRINTABLE_STRING,
ASN1_OID)
from scapy.asn1fields import (ASN1F_SEQUENCE, ASN1F_SEQUENCE_OF, ASN1F_BIT_STRING,
ASN1F_IA5_STRING, ASN1F_INTEGER, ASN1F_SET, ASN1F_OID,
ASN1F_PRINTABLE_STRING, ASN1F_UTF8_STRING, ASN1F_optional)
ASN1F_IA5_STRING, ASN1F_INTEGER, ASN1F_UTF8_STRING,
ASN1F_optional)
# Import needed to initialize conf.mib
from scapy.asn1.mib import conf # noqa: F401

Expand Down Expand Up @@ -286,26 +290,52 @@ def derive_key(key, header, salt, username):
return SAPCredv2_Cred_Plain(plain)


_default_subject = [
X509_RDN(),
X509_RDN(
rdn=[X509_AttributeTypeAndValue(
type=ASN1_OID("2.5.4.10"),
value=ASN1_PRINTABLE_STRING("pysap"))]),
X509_RDN(
rdn=[X509_AttributeTypeAndValue(
type=ASN1_OID("2.5.4.3"),
value=ASN1_PRINTABLE_STRING("pysap Default Subject"))])
]


class SAPCredv2_Cred_LPS(ASN1_Packet):
"""SAP Credv2 Credential with LPS definition"""
ASN1_codec = ASN1_Codecs.BER
ASN1_root = ASN1F_SEQUENCE(
ASN1F_INTEGER("version", 2),
ASN1F_SEQUENCE(
ASN1F_SET(
ASN1F_SEQUENCE(
ASN1F_OID("oid", "2.5.4.3"),
ASN1F_PRINTABLE_STRING("value", None)
)
)
),
ASN1F_SEQUENCE_OF("subject", _default_subject, X509_RDN),
ASN1F_UTF8_STRING("pse_path", None),
ASN1F_BIT_STRING("cipher", None),
)

def get_subject(self):
attrs = self.subject
attrsDict = {}
for attr in attrs:
# we assume there is only one name in each rdn ASN1_SET
attrsDict[attr.rdn[0].type.oidname] = plain_str(attr.rdn[0].value.val) # noqa: E501
return attrsDict

@property
def common_name(self):
return self.value.val
"""This reassembles the issuer construction from Scapy's X.509 Certificate class.
"""
name_str = ""
attrsDict = self.get_subject()
for attrType, attrSymbol in _attrName_mapping:
if attrType in attrsDict:
name_str += "/" + attrSymbol + "="
name_str += attrsDict[attrType]
for attrType in sorted(attrsDict):
if attrType not in _attrName_specials:
name_str += "/" + attrType + "="
name_str += attrsDict[attrType]
return name_str

@property
def pse_file_path(self):
Expand Down
Binary file not shown.
62 changes: 54 additions & 8 deletions tests/sapcredv2_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import sys
import unittest
# External imports
from scapy.asn1.asn1 import ASN1_PRINTABLE_STRING, ASN1_OID
from scapy.layers.x509 import X509_RDN, X509_AttributeTypeAndValue
# Custom imports
from tests.utils import data_filename
from pysap.SAPCredv2 import (SAPCredv2, SAPCredv2_Cred_Plain,
Expand All @@ -33,6 +35,13 @@ class PySAPCredV2Test(unittest.TestCase):
decrypt_pin = "1234567890"
cert_name = "CN=PSEOwner"
common_name = "PSEOwner"
subject_str = "/CN=PSEOwner"
subject = [
X509_RDN(rdn=[
X509_AttributeTypeAndValue(type=ASN1_OID("2.5.4.3"),
value=ASN1_PRINTABLE_STRING(common_name))
])
]
pse_path = "/secudir/pse-v2-noreq-DSA-1024-SHA1.pse"
pse_path_win = "C:\\secudir\\pse-v2-noreq-DSA-1024-SHA1.pse"

Expand Down Expand Up @@ -139,14 +148,15 @@ def test_cred_v2_lps_on_int_aes256(self):
self.assertEqual(len(creds), 1)

cred = creds[0].cred
self.assertEqual(cred.common_name, self.common_name)
self.assertEqual(cred.common_name, self.subject_str)
self.assertEqual(cred.subject, self.subject)
self.assertEqual(cred.subject[0].rdn[0].type.val, "2.5.4.3")
self.assertEqual(cred.subject[0].rdn[0].value.val, self.common_name)

self.assertEqual(cred.pse_file_path, self.pse_path)
self.assertEqual(cred.lps_type, 0)
self.assertEqual(cred.cipher_format_version, 2)

self.assertEqual(cred.version.val, 2)
self.assertEqual(cred.oid.val, "2.5.4.3")
self.assertEqual(cred.value.val, self.common_name)
self.assertEqual(cred.pse_path, self.pse_path)

def test_cred_v2_lps_on_int_aes256_decrypt(self):
Expand All @@ -169,16 +179,52 @@ def test_cred_v2_lps_on_dp_aes256(self):
self.assertEqual(len(creds), 1)

cred = creds[0].cred
self.assertEqual(cred.common_name, self.common_name)
self.assertEqual(cred.common_name, self.subject_str)
self.assertEqual(cred.subject, self.subject)
self.assertEqual(cred.subject[0].rdn[0].type.val, "2.5.4.3")
self.assertEqual(cred.subject[0].rdn[0].value.val, self.common_name)

self.assertEqual(cred.pse_file_path, self.pse_path_win)
self.assertEqual(cred.lps_type, 1)
self.assertEqual(cred.cipher_format_version, 2)

self.assertEqual(cred.version.val, 2)
self.assertEqual(cred.oid.val, "2.5.4.3")
self.assertEqual(cred.value.val, self.common_name)
self.assertEqual(cred.pse_path, self.pse_path_win)

def test_cred_v2_lps_on_int_aes256_composed_subject(self):
"""Test parsing of a AES256 encrypted credential with LPS on, INT type, and
pointing to a PSE with a composed subject
"""

with open(data_filename("cred_v2_lps_on_int_aes256_composed_subject"), "rb") as fd:
s = fd.read()

c = SAPCredv2(s)
creds = SAPCredv2(s).creds
self.assertEqual(len(creds), 1)

subject_str = "/C=AR/CN=PSEOwner"
subject = [
X509_RDN(rdn=[
X509_AttributeTypeAndValue(type=ASN1_OID("2.5.4.6"),
value=ASN1_PRINTABLE_STRING("AR"))]),
X509_RDN(rdn=[
X509_AttributeTypeAndValue(type=ASN1_OID("2.5.4.3"),
value=ASN1_PRINTABLE_STRING(self.common_name))]),
]
cred = creds[0].cred
self.assertEqual(cred.common_name, subject_str)
self.assertEqual(cred.subject, subject)
self.assertEqual(cred.subject[0].rdn[0].type.val, "2.5.4.6")
self.assertEqual(cred.subject[0].rdn[0].value.val, "AR")
self.assertEqual(cred.subject[1].rdn[0].type.val, "2.5.4.3")
self.assertEqual(cred.subject[1].rdn[0].value.val, self.common_name)

self.assertEqual(cred.lps_type, 0)
self.assertEqual(cred.cipher_format_version, 2)
self.assertEqual(cred.version.val, 2)
self.assertEqual(cred.pse_file_path, "/home/martin/sec/test.pse")
self.assertEqual(cred.pse_path, "/home/martin/sec/test.pse")


def test_suite():
loader = unittest.TestLoader()
Expand Down

0 comments on commit c6ed6b7

Please sign in to comment.