|
|
# How to use the pycrate ASN.1 runtime
|
|
|
In this section, we will see how to use the ASN.1 runtime with already compiled
|
|
|
specifications from the
|
|
|
[pycrate_asn1dir/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/)
|
|
|
directory, with basic examples.
|
|
|
|
|
|
|
|
|
## Basics about the handling of values in the runtime
|
|
|
Each ASN.1 specification defines objects with specific structures made of the ASN.1
|
|
|
basic types (INTEGER, BIT STRING, ...) and constructed types (CHOICE, SEQUENCE, ...).
|
|
|
The purpose of the ASN.1 compiler is to transform an ASN.1 specification to a target
|
|
|
programming language and runtime.
|
|
|
The ASN.1 runtime is then in charge, together with the compiled specification, to
|
|
|
encode values for those ASN.1 objects to buffers, and decode buffers to values.
|
|
|
|
|
|
The pycrate ASN.1 runtime, as defined in
|
|
|
[pycrate_asn1rt](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1rt/),
|
|
|
currently implements the following encoders / decoders:
|
|
|
- the ASN.1 textual syntax, with *to_asn1()* / *from_asn1()* methods
|
|
|
- the unaligned Packed Encoding Rules (UPER), with *to_uper()* / *from_uper()* methods
|
|
|
- the aligned Packed Encoding Rules (APER), with *to_aper()* / *from_aper()* methods
|
|
|
- the Basic Encoding Rules (BER), with *to_ber()* / *from_ber()* methods
|
|
|
- the Canonical Encoding Rules (CER), with *to_cer()* / *from_cer()* methods
|
|
|
- the Distinguished Encoding Rules (DER), with *to_der()* / *from_der()* methods
|
|
|
|
|
|
When decoding a bytes' buffer for a given object with one of those *from_()* methods, or setting
|
|
|
a value in it with the *set_val()* method, the value is put in the *_val* attribute
|
|
|
of the object.
|
|
|
We must know however how the pycrate runtime handle values for each basic and constructed ASN.1 objects.
|
|
|
All those information are detailed in the doc strings of each objects, here is a summary
|
|
|
about each ASN.1 type and the corresponding Python type for values expected or returned
|
|
|
by the runtime:
|
|
|
- NULL: int value 0
|
|
|
- BOOLEAN: bool
|
|
|
- INTEGER: int
|
|
|
- ENUMERATED: str, must be a key in the enumerated content (available in the *_cont*
|
|
|
attribute)
|
|
|
- OBJECT IDENTIFIER: tuple of positive int
|
|
|
- RELATIVE-OID: tuple of positive int
|
|
|
- BIT STRING: 2-tuple of positive int (bit string uint value, bit string length),
|
|
|
alternatively a CHOICE-like value in case a CONTAINING object is defined in the
|
|
|
*_const_cont* attribute
|
|
|
- OCTET STRING: bytes, alternatively a CHOICE-like value in case a CONTAINING object
|
|
|
is defined in the *_const_cont* attribute
|
|
|
- *String (UTF8String, IA5String, ...): str
|
|
|
- UTCTime: 7-tuple of str or None (YY, MM, DD, HH, MM, [SS,] Z)
|
|
|
- GeneralizedTime: 8-tuple of str or None (YYYY, MM, DD, HH, [MM, [SS,]] [{.,}F*,] [Z])
|
|
|
- CHOICE: 2-tuple, 1st item is a str (identifier of the choice as defined in the
|
|
|
*_cont* attribute), 2nd item is the chosen object's value
|
|
|
- SEQUENCE, SET: dict, keys are str (identifier of the component as defined in the
|
|
|
*_cont* attribute), values are component's values
|
|
|
- SEQUENCE OF, SET OF: list of component's values (as defined in the the *_cont*
|
|
|
attribute)
|
|
|
- OPEN, ANY: bytes, alternatively a CHOICE-like value in case some constraints
|
|
|
are defined (use *_get_const_tr()* method to list all potential objects in those
|
|
|
constraints)
|
|
|
- EXTERNAL, EMBEDDED PDY and CHARACTER STRING are just defined like SEQUENCE objects
|
|
|
|
|
|
|
|
|
## The UMTS and LTE RRC protocols
|
|
|
|
|
|
### ASN.1 definition and modules
|
|
|
In the UMTS and LTE cellular technologies, the Radio Ressources Configuration protocol
|
|
|
used between handsets and the radio access network is defined in ASN.1. It makes use
|
|
|
of the unaligned Packed Encoding Rules (UPER) to serialize the data exchanged according
|
|
|
to the defined data model.
|
|
|
|
|
|
Pycrate provides two different modules to handle both protocols:
|
|
|
- the UMTS RRC protocol is defined in the
|
|
|
[TS 25.331](http://www.3gpp.org/DynaReport/25331.htm) 3GPP specification;
|
|
|
the ASN.1 definition is available in the directory
|
|
|
[pycrate_asn1dir/3GPP_UTRAN_RRC_25331/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/3GPP_UTRAN_RRC_25331/)
|
|
|
and the corresponding Python modules are compiled in the file
|
|
|
[pycrate_asn1dir/RRC3G.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/RRC3G.py).
|
|
|
- the LTE RRC protocol is defined in the
|
|
|
[TS 36.331](http://www.3gpp.org/DynaReport/36331.htm) 3GPP specification;
|
|
|
the ASN.1 definition is available in the directory
|
|
|
[pycrate_asn1dir/3GPP_EUTRAN_RRC_36331/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/3GPP_EUTRAN_RRC_36331/)
|
|
|
and the corresponding Python modules are compiled in the file
|
|
|
[pycrate_asn1dir/RRCLTE.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/RRCLTE.py).
|
|
|
|
|
|
Both specifications provided are from the 3GPP release 13 (0xd), however, it is possible
|
|
|
to easily upgrade or downgrade them thanks to the *extract.py* scripts provided in the ASN.1 definition
|
|
|
directories (just read those scripts to see how to do exactly).
|
|
|
|
|
|
### RRC 3G
|
|
|
We can simply load the RRC3G Python module, which will load the corresponding ASN.1 objects
|
|
|
and the ASN.1 runtime together:
|
|
|
|
|
|
>>> from pycrate_asn1dir import RRC3G # this can take few seconds
|
|
|
>>> list(RRC3G.GLOBAL.MOD.keys())
|
|
|
['Constant-definitions', 'InformationElements', 'PDU-definitions', 'Class-definitions', 'Internode-definitions']
|
|
|
|
|
|
The *GLOBAL.MOD* dictionnary contains all ASN.1 modules and objects defined in the 3GPP
|
|
|
specification. The modules in *GLOBAL.MOD* are actually dictionnaries listing all objects defined
|
|
|
in them. They are also directly available under the RRC3G module as classes and attributes,
|
|
|
with a slight adaptation of their names (all *-* are replaced with *_*).
|
|
|
|
|
|
>>> RRC3G.GLOBAL.MOD['PDU-definitions']['System-Information-Container']
|
|
|
<System-Information-Container (SEQUENCE)>
|
|
|
>>> RRC3G.PDU_definitions.System_Information_Container == RRC3G.GLOBAL.MOD['PDU-definitions']['System-Information-Container']
|
|
|
True
|
|
|
|
|
|
Each ASN.1 object is of a certain ASN.1 type (e.g. INTEGER, OCTET STRING, SEQUENCE, ...),
|
|
|
which has different attributes. Each ASN.1 object has a generous doc string which can
|
|
|
be read to get all the tiny details about the available attributes.
|
|
|
|
|
|
>>> SIC = RRC3G.PDU_definitions.System_Information_Container
|
|
|
>>> help(SIC)
|
|
|
Help on SEQ in module pycrate_asn1rt.asnobj_construct object:
|
|
|
|
|
|
class SEQ(_CONSTRUCT)
|
|
|
| ASN.1 constructed type SEQUENCE object
|
|
|
|
|
|
|
| Single value: Python dict
|
|
|
| keys are Python str, components' identifier, must be key in _cont
|
|
|
| values are ASN1Obj single value specific to components object
|
|
|
[...]
|
|
|
|
|
|
One important attribute for constructed objects is the *_cont* one, which stores the content
|
|
|
of the object:
|
|
|
|
|
|
>>> SIC._cont
|
|
|
{
|
|
|
mib: <mib (OCTET STRING)>,
|
|
|
sysInfoTypeSB1: <sysInfoTypeSB1 (OCTET STRING)>,
|
|
|
sysInfoTypeSB2: <sysInfoTypeSB2 (OCTET STRING)>,
|
|
|
sysInfoType1: <sysInfoType1 (OCTET STRING)>,
|
|
|
sysInfoType3: <sysInfoType3 (OCTET STRING)>,
|
|
|
sysInfoType5: <sysInfoType5 (OCTET STRING)>,
|
|
|
sysInfoType7: <sysInfoType7 (OCTET STRING)>,
|
|
|
sysInfoType11: <sysInfoType11 (OCTET STRING)>,
|
|
|
sysInfoType11bis: <sysInfoType11bis (OCTET STRING)>,
|
|
|
sysInfoType12: <sysInfoType12 (OCTET STRING)>,
|
|
|
vb50NonCriticalExtensions: <vb50NonCriticalExtensions (SEQUENCE)>
|
|
|
}
|
|
|
|
|
|
Here, only the first level of content is returned. The *get_proto()* method returns the complete
|
|
|
content by entering each constructed content recursively. **Be careful** about some limitations of
|
|
|
this method: its call on a recursive object will trigger a Python recursion exception... Moreover
|
|
|
it does not provides hints on optional components (those marked OPTIONAL or with a DEFAULT value),
|
|
|
neither on component in the root or extension part of the content.
|
|
|
|
|
|
>>> SIC.get_proto()
|
|
|
{
|
|
|
mib: 'OCTET STRING',
|
|
|
sysInfoTypeSB1: 'OCTET STRING',
|
|
|
sysInfoTypeSB2: 'OCTET STRING',
|
|
|
sysInfoType1: 'OCTET STRING',
|
|
|
sysInfoType3: 'OCTET STRING',
|
|
|
sysInfoType5: 'OCTET STRING',
|
|
|
sysInfoType7: 'OCTET STRING',
|
|
|
sysInfoType11: 'OCTET STRING',
|
|
|
sysInfoType11bis: 'OCTET STRING',
|
|
|
sysInfoType12: 'OCTET STRING',
|
|
|
vb50NonCriticalExtensions: {
|
|
|
system-Information-Container-vb50ext: {
|
|
|
sysInfoType22: 'OCTET STRING'
|
|
|
},
|
|
|
vc50NonCriticalExtensions: {
|
|
|
system-Information-Container-vc50ext: {
|
|
|
sysInfoType11ter: 'OCTET STRING'
|
|
|
},
|
|
|
nonCriticalExtensions: {}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Because the UMTS RRC protocol is using the UPER encoding, we will use the *from_uper()*
|
|
|
method to deserialize buffers. The corresponding value will be available in the *_val* attribute,
|
|
|
and will also be returned when calling the ASN.1 object itself. Finally, the *to_asn1()*
|
|
|
method returns a printable representation of the ASN.1 value, which conforms to the ASN.1
|
|
|
syntax (and hence would be compilable again...).
|
|
|
|
|
|
>>> from binascii import unhexlify
|
|
|
>>> pcch = RRC3G.Class_definitions.PCCH_Message
|
|
|
>>> pcch.from_uper(unhexlify('4455c803999055c601b95855aa06b09e'))
|
|
|
>>> pcch()
|
|
|
{'message': ('pagingType1', {'pagingRecordList': [...]})}
|
|
|
>>> print(pcch.to_asn1())
|
|
|
{
|
|
|
message pagingType1 : {
|
|
|
pagingRecordList {
|
|
|
cn-Identity : {
|
|
|
pagingCause terminatingInteractiveCall,
|
|
|
cn-DomainIdentity ps-domain,
|
|
|
cn-pagedUE-Identity p-TMSI-GSM-MAP : 'E401CCC8'H
|
|
|
},
|
|
|
cn-Identity : {
|
|
|
pagingCause terminatingInteractiveCall,
|
|
|
cn-DomainIdentity ps-domain,
|
|
|
cn-pagedUE-Identity p-TMSI-GSM-MAP : 'E300DCAC'H
|
|
|
},
|
|
|
cn-Identity : {
|
|
|
pagingCause terminatingInteractiveCall,
|
|
|
cn-DomainIdentity ps-domain,
|
|
|
cn-pagedUE-Identity p-TMSI-GSM-MAP : 'D503584F'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
To serialize some values, one need to set the value into the selected ASN.1 object. The method
|
|
|
*set_val()* is here for that purpose. Then, we can call the *to_uper()* method to serialize
|
|
|
the value into a bytes' buffer. With the previous example about the PCCH message, we can take
|
|
|
its value and change the first identity paging record to see the effect on the encoding:
|
|
|
|
|
|
>>> v = pcch()
|
|
|
>>> v['message'][1]['pagingRecordList'][0][1]
|
|
|
{'cn-pagedUE-Identity': ('p-TMSI-GSM-MAP', (3825323208, 32)), 'cn-DomainIdentity': 'ps-domain', 'pagingCause': 'terminatingInteractiveCall'}
|
|
|
>>> v['message'][1]['pagingRecordList'][0][1]['cn-pagedUE-Identity'] = ('p-TMSI-GSM-MAP', (0xf0000001, 32))
|
|
|
>>> v['message'][1]['pagingRecordList'][0][1]['cn-DomainIdentity'] = 'cs-domain'
|
|
|
>>> v['message'][1]['pagingRecordList'][0][1]['pagingCause'] = 'terminatingHighPrioritySignalling'
|
|
|
>>> pcch.set_val(v)
|
|
|
>>> hexlify(pcch.to_uper())
|
|
|
b'4485e000000255c601b95855aa06b09e'
|
|
|
|
|
|
We can also appreciate how the UPER encoding is more compact than the BER one.
|
|
|
This is one of the main reason why it is used by many protocols transported over
|
|
|
radio interfaces: it saves bandwidth.
|
|
|
|
|
|
>>> hexlify(pcch.to_ber())
|
|
|
b'3039a037a035a033a00f800104810100a207820500f0000001a00f800102810101a207820500e300dcaca00f800102810101a207820500d503584f'
|
|
|
|
|
|
As seen in the previous example, it is required to know the content of constructed objects
|
|
|
to be able to access their internals, and also to be able to set values. Two utility functions
|
|
|
are provided to help with that:
|
|
|
- *get_obj_at(Obj, path)*
|
|
|
- *get_val_at(Obj, path)*
|
|
|
|
|
|
Both take an ASN.1 object and the path to one of its internal component or value and return it.
|
|
|
Here are some examples with the previous *PCCH_Message* object:
|
|
|
|
|
|
>>> pcch.get_proto()
|
|
|
{
|
|
|
message: {
|
|
|
pagingType1: {
|
|
|
pagingRecordList: [{
|
|
|
[...]
|
|
|
},
|
|
|
spare: 'NULL'
|
|
|
}
|
|
|
}
|
|
|
>>> get_obj_at(pcch, ['message', 'pagingType1', 'pagingRecordList', None, 'utran-Identity'])
|
|
|
<utran-Identity (SEQUENCE)>
|
|
|
>>> # pagingRecordList being a SEQUENCE OF, no need to provide any name to select its content
|
|
|
>>> get_val_at(pcch, ['message', 'pagingType1', 'pagingRecordList', 2])
|
|
|
('cn-Identity', {'pagingCause': 'terminatingInteractiveCall', 'cn-DomainIdentity': 'ps-domain', 'cn-pagedUE-Identity': ('p-TMSI-GSM-MAP', (3573766223, 32))})
|
|
|
>>> # this returns the 3rd identity from the paging message
|
|
|
|
|
|
### RRC LTE
|
|
|
Let's see another example with an LTE RRC protocol message. The following message
|
|
|
is broadcasted by an LTE eNodeB within its downlink shared channel over the broadcast control
|
|
|
channel, it contains a SIB2 which provides many parameters of the radio interface:
|
|
|
|
|
|
>>> from pycrate_asn1dir import RRCLTE
|
|
|
>>> from binascii import unhexlify, hexlify
|
|
|
>>> sch = RRCLTE.EUTRA_RRC_Definitions.BCCH_DL_SCH_Message
|
|
|
>>> sch.from_uper(unhexlify('00800c61bc8c8cc11609ba020004100193394c52d5425c700708518b613a9690'))
|
|
|
>>> print(sch.to_asn1())
|
|
|
{
|
|
|
message c1 : systemInformation : {
|
|
|
criticalExtensions systemInformation-r8 : {
|
|
|
sib-TypeAndInfo {
|
|
|
sib2 : {
|
|
|
radioResourceConfigCommon {
|
|
|
rach-ConfigCommon {
|
|
|
preambleInfo {
|
|
|
numberOfRA-Preambles n52
|
|
|
},
|
|
|
[...]
|
|
|
},
|
|
|
intraFreqCellReselectionInfo {
|
|
|
q-RxLevMin -61,
|
|
|
p-Max 23,
|
|
|
s-IntraSearch 5,
|
|
|
presenceAntennaPort1 TRUE,
|
|
|
neighCellConfig '01'B,
|
|
|
t-ReselectionEUTRA 1
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> hexlify(sch.to_uper())
|
|
|
b'00800c61bc8c8cc11609ba020004100193394c52d5425c700708518b613a9690'
|
|
|
|
|
|
In order to understand the complexity of those RRC protocols, one can simply check
|
|
|
the structure of a dedicated signaling channel downlink message (expand your scrollback
|
|
|
buffer if you want to see it all!):
|
|
|
|
|
|
>>> RRCLTE.EUTRA_RRC_Definitions.DL_DCCH_Message.get_proto()
|
|
|
{
|
|
|
message: {
|
|
|
c1: {
|
|
|
csfbParametersResponseCDMA2000: {
|
|
|
rrc-TransactionIdentifier: 'INTEGER',
|
|
|
criticalExtensions: {
|
|
|
csfbParametersResponseCDMA2000-r8: {
|
|
|
rand: 'BIT STRING',
|
|
|
mobilityParameters: 'OCTET STRING',
|
|
|
nonCriticalExtension: {
|
|
|
lateNonCriticalExtension: 'OCTET STRING',
|
|
|
nonCriticalExtension: {}
|
|
|
}
|
|
|
},
|
|
|
criticalExtensionsFuture: {}
|
|
|
}
|
|
|
},
|
|
|
dlInformationTransfer: {
|
|
|
rrc-TransactionIdentifier: 'INTEGER',
|
|
|
criticalExtensions: {
|
|
|
c1: {
|
|
|
|
|
|
[...]
|
|
|
|
|
|
antennaInfoDedicatedPCell-r13: {
|
|
|
maxLayersMIMO-r10: 'ENUMERATED'
|
|
|
},
|
|
|
drb-ContinueROHC-r13: 'ENUMERATED',
|
|
|
lateNonCriticalExtension: 'OCTET STRING',
|
|
|
nonCriticalExtension: {}
|
|
|
},
|
|
|
spare3: 'NULL',
|
|
|
spare2: 'NULL',
|
|
|
spare1: 'NULL'
|
|
|
},
|
|
|
criticalExtensionsFuture: {}
|
|
|
}
|
|
|
},
|
|
|
spare3: 'NULL',
|
|
|
spare2: 'NULL',
|
|
|
spare1: 'NULL'
|
|
|
},
|
|
|
messageClassExtension: {}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
### RRC top level messages
|
|
|
The ASN.1 top-level messages are those which make reference to others ASN.1 objects,
|
|
|
but are not referenced themselves. They are often corresponding to the RRC messages
|
|
|
exchanged on the different transport channels over the radio interface between
|
|
|
mobile phones and the radio access network.
|
|
|
|
|
|
The function *get_top_level()* from the *pycrate_asn1rt/utils.py" module
|
|
|
works over the json files produced by the *pycrate_asn1c* compiler and returns those
|
|
|
exact top-level objects from an ASN.1 specification.
|
|
|
|
|
|
For the LTE RRC specification, we get the following objects of the *EUTRA-RRC-Definitions*
|
|
|
ASN.1 module:
|
|
|
- EUTRA-RRC-Definitions.BCCH-DL-SCH-Message
|
|
|
- EUTRA-RRC-Definitions.BCCH-DL-SCH-Message-BR
|
|
|
- EUTRA-RRC-Definitions.MCCH-Message
|
|
|
- EUTRA-RRC-Definitions.PCCH-Message
|
|
|
- EUTRA-RRC-Definitions.DL-CCCH-Message
|
|
|
- EUTRA-RRC-Definitions.UL-CCCH-Message
|
|
|
- EUTRA-RRC-Definitions.UL-DCCH-Message
|
|
|
- EUTRA-RRC-Definitions.SC-MCCH-Message-r13
|
|
|
- EUTRA-RRC-Definitions.UE-EUTRA-Capability
|
|
|
- EUTRA-RRC-Definitions.BCCH-BCH-Message
|
|
|
- EUTRA-RRC-Definitions.SL-TxPoolIdentity-r13
|
|
|
|
|
|
We can see that the ASN.1 object *EUTRA-RRC-Definitions.DL-DCCH-Message* is not a top-level one,
|
|
|
it is actually referenced by object *EUTRA-InterNodeDefinitions.HandoverCommand-r8-IEs*.
|
|
|
However, it is still to be used for decoding downlink RRC message on dedicated signaling
|
|
|
channels.
|
|
|
|
|
|
For the UMTS RRC specification, we get the following ASN.1 top-level objects of the
|
|
|
*Class-definitions* ASN.1 module:
|
|
|
- Class-definitions.DL-DCCH-Message
|
|
|
- Class-definitions.UL-DCCH-Message
|
|
|
- Class-definitions.DL-CCCH-Message
|
|
|
- Class-definitions.UL-CCCH-Message
|
|
|
- Class-definitions.PCCH-Message
|
|
|
- Class-definitions.DL-SHCCH-Message
|
|
|
- Class-definitions.UL-SHCCH-Message
|
|
|
- Class-definitions.BCCH-FACH-Message
|
|
|
- Class-definitions.BCCH-BCH-Message
|
|
|
- Class-definitions.BCCH-BCH2-Message
|
|
|
- Class-definitions.MCCH-Message
|
|
|
- Class-definitions.MSCH-Message
|
|
|
|
|
|
|
|
|
## The RANAP and S1AP RAN protocols
|
|
|
|
|
|
### ASN.1 definition and modules
|
|
|
There are many protocols defined by the 3GPP to inter-connect equipments in radio access
|
|
|
networks, and with core networks. In the 3G RAN specifications (TS 25 serie), we can find
|
|
|
the following protocols corresponding to different signaling interfaces:
|
|
|
- *NBAP* used between a NodeB and an RNC
|
|
|
- *RNSAP* used to inter-connect two RNCs
|
|
|
- *RANAP* used between an RNC and a core network (both CS and PS domains core)
|
|
|
- *PCAP* used between an RNC and a location server (for positionning services)
|
|
|
- *SABP* used between an RNC and a cell-broadcasting server (for multicast / broadcast services)
|
|
|
- *HNBAP* and *RUA* used between a femtocell and a core network
|
|
|
- *RNA* used to inter-connect two femtocells
|
|
|
|
|
|
In the LTE RAN specifications (TS 36 serie), we can find the following ones:
|
|
|
- *S1AP* used between an eNodeB and an MME
|
|
|
- *X2AP* used to inter-connect two eNodeBs
|
|
|
- *M2AP* and *M3AP* used between an eNodeB and a multicast server (for multicast / broadcast services)
|
|
|
- *LPP* and *LPPa* used between a mobile phone and a location server (for positionning services)
|
|
|
- *SLmAP* used between an eNodeB and a location server
|
|
|
- *XwAP* used between an eNodeB and a Wi-Fi access point
|
|
|
|
|
|
All these protocols are defined with ASN.1, and most of them are using the aligned
|
|
|
packed encoding rules (APER) to serialize the data exchanged according to the defined
|
|
|
data model.
|
|
|
|
|
|
### RANAP
|
|
|
RANAP is a signaling protocol used between the CS core network (MSC-VLR) or
|
|
|
the PS core network (SGSN) and the 3G radio access network (RNC). It enables merely
|
|
|
to drive mobile phones connecting to the 3G radio access network, from the core network
|
|
|
stand-point. The protocol is specified by the 3GPP standard
|
|
|
[TS 25.413](http://www.3gpp.org/DynaReport/25413.htm), the ASN.1 definition is available
|
|
|
in the directory
|
|
|
[pycrate_asn1dir/3GPP_UTRAN_RANAP_25413/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/3GPP_UTRAN_RANAP_25413/)
|
|
|
and the corresponding Python modules are in the compiled file
|
|
|
[pycrate_asn1dir/RANAP.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/RANAP.py).
|
|
|
|
|
|
Contrary to RRC protocols, there is a single RANAP ASN.1 object to encode and decode all
|
|
|
the messages that can be exchanged with it: the *RANAP-PDU* defined in the *RANAP-PDU-Descriptions*
|
|
|
ASN.1 module.
|
|
|
Also contrary to RRC protocols, where all constructed types are all defined in a straightforward way,
|
|
|
the RANAP protocol uses the ASN.1 parameterization to define modular and extendable messages,
|
|
|
and defines also procedures through sets of message types and values. In this way, it can be seen
|
|
|
as a sort of remote-procedure-call protocol.
|
|
|
|
|
|
|
|
|
>>> from pycrate_asn1dir import RANAP
|
|
|
>>> PDU = RANAP.RANAP_PDU_Descriptions.RANAP_PDU
|
|
|
>>> PDU
|
|
|
<RANAP-PDU (CHOICE)>
|
|
|
>>> help(PDU)
|
|
|
|
|
|
Help on CHOICE in module pycrate_asn1rt.asnobj_construct object:
|
|
|
|
|
|
class CHOICE(pycrate_asn1rt.asnobj.ASN1Obj)
|
|
|
| ASN.1 constructed type CHOICE object
|
|
|
|
|
|
|
| Single value: Python 2-tuple
|
|
|
| 1st item is a Python str, choice identifier, must be a key in _cont
|
|
|
| 2nd item is the ASN1Obj single value specific to the chosen object
|
|
|
[...]
|
|
|
|
|
|
>>> PDU.get_proto()
|
|
|
{
|
|
|
initiatingMessage: {
|
|
|
procedureCode: 'INTEGER',
|
|
|
criticality: 'ENUMERATED',
|
|
|
value: 'OPEN_TYPE'
|
|
|
},
|
|
|
successfulOutcome: {
|
|
|
procedureCode: 'INTEGER',
|
|
|
criticality: 'ENUMERATED',
|
|
|
value: 'OPEN_TYPE'
|
|
|
},
|
|
|
unsuccessfulOutcome: {
|
|
|
procedureCode: 'INTEGER',
|
|
|
criticality: 'ENUMERATED',
|
|
|
value: 'OPEN_TYPE'
|
|
|
},
|
|
|
outcome: {
|
|
|
procedureCode: 'INTEGER',
|
|
|
criticality: 'ENUMERATED',
|
|
|
value: 'OPEN_TYPE'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
The structure of a *RANAP-PDU* looks very simple at first sight. It is however not...
|
|
|
Each message can be one of the fourth types defined. It is then built with one of the
|
|
|
PDU content as defined in the *RANAP-PDU-Contents* ASN.1 module. Let's see how a RANAP
|
|
|
Relocation Command is made:
|
|
|
|
|
|
>>> RelCmd = RANAP.RANAP_PDU_Contents.RelocationCommand
|
|
|
>>> RelCmd
|
|
|
<RelocationCommand (SEQUENCE)>
|
|
|
>>> RelCmd.get_proto()
|
|
|
{
|
|
|
protocolIEs: [{
|
|
|
id: 'INTEGER',
|
|
|
criticality: 'ENUMERATED',
|
|
|
value: 'OPEN_TYPE'
|
|
|
}],
|
|
|
protocolExtensions: [{
|
|
|
id: 'INTEGER',
|
|
|
criticality: 'ENUMERATED',
|
|
|
extensionValue: 'OPEN_TYPE'
|
|
|
}]
|
|
|
}
|
|
|
|
|
|
Here again, things look simple, but are not... Each RANAP PDU can contain a sequence
|
|
|
of *protocolIE* and another sequence of *protocolExtension*. Those IEs and Extensions
|
|
|
definition can be found in another ASN.1 set of values, which lists every possible
|
|
|
IE, respectively Extension, defined by the specification. An ASN.1 set is a specific
|
|
|
object within the pycrate ASN.1 runtime. It has a root part and an extended part.
|
|
|
Moreover, it is callable with some built-in filtering features.
|
|
|
|
|
|
>>> RelCmdIEs = RANAP.RANAP_PDU_Contents.RelocationCommandIEs
|
|
|
>>> RelCmdIEs
|
|
|
<RelocationCommandIEs ([RANAP-PROTOCOL-IES] CLASS): ASN1Set(root=[...], ext=[])>
|
|
|
>>> RelCmdIEs().root
|
|
|
[{'Value': <Value ([Target-ToSource-TransparentContainer] OCTET STRING)>, [...]]
|
|
|
>>> RelCmdIEs().ext
|
|
|
[]
|
|
|
>>> RelCmdIEs('id') # listing all IE's id
|
|
|
[63, 14, 46, 28, 9]
|
|
|
>>> RelCmdIEs('id', 14) # filtering the content according to the id's value 14
|
|
|
{'Value': <Value ([L3-Information] OCTET STRING)>, 'presence': 'optional', 'criticality': 'ignore', 'id': 14}
|
|
|
|
|
|
To summerize, a RANAP PDU is one of the four choices possible for the *RANAP-PDU* object,
|
|
|
the *procedureCode* links to one of the procedure message defined in the *RANAP-PDU-Contents*
|
|
|
ASN.1 module, which itself links to a serie of *protocolIEs* and *protocolExtensions*.
|
|
|
We can see those links by checking the table constraints applied to specific parts of
|
|
|
those objects (OPEN types, actually):
|
|
|
|
|
|
>>> PDU._cont
|
|
|
{
|
|
|
initiatingMessage: <initiatingMessage ([InitiatingMessage] SEQUENCE)>,
|
|
|
successfulOutcome: <successfulOutcome ([SuccessfulOutcome] SEQUENCE)>,
|
|
|
unsuccessfulOutcome: <unsuccessfulOutcome ([UnsuccessfulOutcome] SEQUENCE)>,
|
|
|
outcome: <outcome ([Outcome] SEQUENCE)>
|
|
|
}
|
|
|
>>> PDU._cont['initiatingMessage']._cont
|
|
|
{
|
|
|
procedureCode: <procedureCode ([RANAP-ELEMENTARY-PROCEDURE.&procedureCode] INTEGER)>,
|
|
|
criticality: <criticality ([RANAP-ELEMENTARY-PROCEDURE.&criticality] ENUMERATED)>,
|
|
|
value: <value ([RANAP-ELEMENTARY-PROCEDURE.&InitiatingMessage] OPEN_TYPE)>
|
|
|
}
|
|
|
>>> PDU._cont['initiatingMessage']._cont['value']._const_tab
|
|
|
<_tab_RANAP-ELEMENTARY-PROCEDURE ([RANAP-ELEMENTARY-PROCEDURE] CLASS): ASN1Set(root=[...], ext=[...])
|
|
|
>>> # this ASN1Set contains all the values defined in the RANAP-PDU-Descriptions.RANAP-ELEMENTARY-PROCEDURES set
|
|
|
>>>
|
|
|
>>> RelCmd._cont
|
|
|
{
|
|
|
protocolIEs: <protocolIEs ([ProtocolIE-Container] SEQUENCE OF)>,
|
|
|
protocolExtensions: <protocolExtensions ([ProtocolExtensionContainer] SEQUENCE OF)>
|
|
|
}
|
|
|
>>> RelCmd._cont['protocolIEs']._cont._cont
|
|
|
{
|
|
|
id: <id ([RANAP-PROTOCOL-IES.&id] INTEGER)>,
|
|
|
criticality: <criticality ([RANAP-PROTOCOL-IES.&criticality] ENUMERATED)>,
|
|
|
value: <value ([RANAP-PROTOCOL-IES.&Value] OPEN_TYPE)>
|
|
|
}
|
|
|
>>> RelCmd._cont['protocolIEs']._cont._cont['value']._const_tab
|
|
|
<_tab_RANAP-PROTOCOL-IES ([RANAP-PROTOCOL-IES] CLASS): ASN1Set(root=[...], ext=None)>
|
|
|
>>> # this ASN1Set contains all the values defined in the RANAP-PDU-Contents.RelocationCommandIEs set
|
|
|
|
|
|
It is also possible to list all possible objects that can be embedded into an OPEN one
|
|
|
with the *_get_const_tr()* method:
|
|
|
|
|
|
>>> from pprint import PrettyPrinter
|
|
|
>>> PP = PrettyPrinter()
|
|
|
>>> PP.pprint(RANAP.RANAP_PDU_Descriptions.RANAP_PDU._cont['initiatingMessage']._cont['value']._get_const_tr())
|
|
|
{'CN-DeactivateTrace': <InitiatingMessage ([CN-DeactivateTrace] SEQUENCE)>,
|
|
|
'CN-InvokeTrace': <InitiatingMessage ([CN-InvokeTrace] SEQUENCE)>,
|
|
|
[...]
|
|
|
'UeRegistrationQueryRequest': <InitiatingMessage ([UeRegistrationQueryRequest] SEQUENCE)>,
|
|
|
'UplinkInformationExchangeRequest': <InitiatingMessage ([UplinkInformationExchangeRequest] SEQUENCE)>}
|
|
|
>>> PP.pprint(RelCmd._cont['protocolIEs']._cont._cont['value']._get_const_tr())
|
|
|
{'CriticalityDiagnostics': <Value ([CriticalityDiagnostics] SEQUENCE)>,
|
|
|
'L3-Information': <Value ([L3-Information] OCTET STRING)>,
|
|
|
'RAB-DataForwardingList': <Value ([RAB-DataForwardingList] SEQUENCE OF)>,
|
|
|
'RAB-RelocationReleaseList': <Value ([RAB-RelocationReleaseList] SEQUENCE OF)>,
|
|
|
'Target-ToSource-TransparentContainer': <Value ([Target-ToSource-TransparentContainer] OCTET STRING)>}
|
|
|
|
|
|
Now that we understand the structure of RANAP messages, we can simply use the runtime
|
|
|
to encode and decode them. Let's see first how to decode a bytes' buffer corresponding to
|
|
|
a downlink RANAP Security Mode Command message, with the APER codec:
|
|
|
|
|
|
>>> from binascii import unhexlify
|
|
|
>>> PDU.from_aper(unhexlify('00060035000003004b000140000b4013110800e03d3da3fc2ee693d0232a6d366a685f000c00120880a261c3cbf6b885745e95e56890586e60'))
|
|
|
>>> print(PDU.to_asn1())
|
|
|
initiatingMessage : {
|
|
|
procedureCode 6,
|
|
|
criticality reject,
|
|
|
value SecurityModeCommand: {
|
|
|
protocolIEs {
|
|
|
{
|
|
|
id 75,
|
|
|
criticality reject,
|
|
|
value KeyStatus: new
|
|
|
},
|
|
|
{
|
|
|
id 11,
|
|
|
criticality ignore,
|
|
|
value EncryptionInformation: {
|
|
|
permittedAlgorithms {
|
|
|
2 -- standard-UMTS-encryption-algorithm-UEA2 --,
|
|
|
1 -- standard-UMTS-encryption-algorith-UEA1 --,
|
|
|
0 -- no-encryption --
|
|
|
},
|
|
|
key 'E03D3DA3FC2EE693D0232A6D366A685F'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
id 12,
|
|
|
criticality reject,
|
|
|
value IntegrityProtectionInformation: {
|
|
|
permittedAlgorithms {
|
|
|
1 -- standard-UMTS-integrity-algorithm-UIA2 --,
|
|
|
0 -- standard-UMTS-integrity-algorithm-UIA1 --
|
|
|
},
|
|
|
key 'A261C3CBF6B885745E95E56890586E60'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> from pycrate_asn1rt.utils import *
|
|
|
>>> for ie in get_val_at(PDU, ['initiatingMessage', 'value', 'SecurityModeCommand', 'protocolIEs']): print(ie)
|
|
|
...
|
|
|
{'value': ('KeyStatus', 'new'), 'criticality': 'reject', 'id': 75}
|
|
|
{'value': ('EncryptionInformation', {'key': (298065051383415014526417577255883139167, 128), 'permittedAlgorithms': [2, 1, 0]}), 'criticality': 'ignore', 'id': 11}
|
|
|
{'value': ('IntegrityProtectionInformation', {'key': (215842559341980337150017521848692534880, 128), 'permittedAlgorithms': [1, 0]}), 'criticality': 'reject', 'id': 12}
|
|
|
>>> intkey = get_val_at(PDU, ['initiatingMessage', 'value', 'SecurityModeCommand', 'protocolIEs', 2, 'value', 'IntegrityProtectionInformation', 'key'])
|
|
|
>>> intkey
|
|
|
(215842559341980337150017521848692534880, 128)
|
|
|
>>> uint_to_bytes(*intkey)
|
|
|
b'\xa2a\xc3\xcb\xf6\xb8\x85t^\x95\xe5h\x90Xn`'
|
|
|
>>> uint_to_hex(*intkey)
|
|
|
'a261c3cbf6b885745e95e56890586e60'
|
|
|
|
|
|
And here is an example on how to encode an uplink RANAP Direct Transfer message:
|
|
|
|
|
|
>>> for ie in RANAP.RANAP_PDU_Contents.DirectTransferIEs().root: print(ie)
|
|
|
...
|
|
|
{'Value': <Value ([NAS-PDU] OCTET STRING)>, 'presence': 'mandatory', 'criticality': 'ignore', 'id': 16}
|
|
|
{'Value': <Value ([LAI] SEQUENCE)>, 'presence': 'optional', 'criticality': 'ignore', 'id': 15}
|
|
|
{'Value': <Value ([RAC] OCTET STRING)>, 'presence': 'optional', 'criticality': 'ignore', 'id': 55}
|
|
|
{'Value': <Value ([SAI] SEQUENCE)>, 'presence': 'optional', 'criticality': 'ignore', 'id': 58}
|
|
|
{'Value': <Value ([SAPI] ENUMERATED)>, 'presence': 'optional', 'criticality': 'ignore', 'id': 59}
|
|
|
>>> IEs = [] # let's build the list of IEs values
|
|
|
>>> IEs.append({'id': 16, 'criticality': 'ignore', 'value': ('NAS-PDU', b'\x08\x13\x00"\x00I%\xaf)\x04u\xb2\x86B')})
|
|
|
>>> IEs.append({'id': 15, 'criticality': 'ignore', 'value': ('LAI', {'pLMNidentity': b'\x00\x01\xf1', 'lAC': b'\x00\x01'})})
|
|
|
>>> IEs.append({'id': 55, 'criticality': 'ignore', 'value': ('RAC', b'\x10')})
|
|
|
>>> IEs.append({'id': 58, 'criticality': 'ignore', 'value': ('SAI', {'sAC': b'\xff\xff', 'pLMNidentity': b'\x00\x01\xf1', 'lAC': b'\x00\x01'})})
|
|
|
>>> val = ('initiatingMessage', {'procedureCode': 20, 'value': ('DirectTransfer', {'protocolIEs': IEs}), 'criticality': 'ignore'})
|
|
|
>>> PDU.set_val(val)
|
|
|
>>> print(PDU.to_asn1())
|
|
|
initiatingMessage : {
|
|
|
procedureCode 20,
|
|
|
criticality ignore,
|
|
|
value DirectTransfer: {
|
|
|
protocolIEs {
|
|
|
{
|
|
|
id 16,
|
|
|
criticality ignore,
|
|
|
value NAS-PDU: '08130022004925AF290475B28642'H
|
|
|
},
|
|
|
{
|
|
|
id 15,
|
|
|
criticality ignore,
|
|
|
value LAI: {
|
|
|
pLMNidentity '0001F1'H,
|
|
|
lAC '0001'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
id 55,
|
|
|
criticality ignore,
|
|
|
value RAC: '10'H
|
|
|
},
|
|
|
{
|
|
|
id 58,
|
|
|
criticality ignore,
|
|
|
value SAI: {
|
|
|
pLMNidentity '0001F1'H,
|
|
|
lAC '0001'H,
|
|
|
sAC 'FFFF'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> from binascii import hexlify
|
|
|
>>> hexlify(PDU.to_aper())
|
|
|
b'001440310000040010400f0e08130022004925af290475b28642000f4006000001f100010037400110003a4008000001f10001ffff'
|
|
|
|
|
|
In order to go further, we can read the source code of the ongoing effort to build a 3G
|
|
|
core network, available in the directory
|
|
|
[pycrate_corenet](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_corenet/).
|
|
|
There is a generic Python module *LinkSigProc* to help with the procedures defined
|
|
|
in this kind of RPC-like protocol:
|
|
|
[pycrate_corenet/ProcProto.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_corenet/ProcProto.py),
|
|
|
and some initial RANAP procedures defined here:
|
|
|
[pycrate_corenet/ProcCNRanap.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_corenet/ProcCNRanap.py).
|
|
|
|
|
|
### S1AP
|
|
|
S1AP is the signaling protocol used between the MME in an LTE core network (also called an EPC)
|
|
|
and eNodeBs (4G base-stations). It enables to drive the eNodeB with few specific procedures
|
|
|
and also mobile phones connecting to the 4G radio access network, from the core network stand-point.
|
|
|
The protocol is specified by the 3GPP standard
|
|
|
[TS 36.413](http://www.3gpp.org/DynaReport/36413.htm), the ASN.1 definition is available
|
|
|
in the directory
|
|
|
[pycrate_asn1dir/3GPP_EUTRAN_S1AP_36413/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/3GPP_EUTRAN_S1AP_36413/)
|
|
|
and the corresponding Python modules are in the file
|
|
|
[pycrate_asn1dir/S1AP.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/S1AP.py).
|
|
|
|
|
|
S1AP and X2AP protocols are very similar to the existing 3G protocols (like RANAP).
|
|
|
There is a single top-level object to encode and decode all protocol's messages:
|
|
|
the *S1AP-PDU* defined in the *S1AP-PDU-Descriptions* ASN.1 module.
|
|
|
The same concept of modular messages containing *protocolIEs* and *protocolExtensions*
|
|
|
elements is used.
|
|
|
|
|
|
Here are an example with the decoding of an S1 Setup Request message:
|
|
|
|
|
|
>>> from pycrate_asn1dir import S1AP
|
|
|
>>> from pycrate_asn1rt.utils import *
|
|
|
>>> from binascii import hexlify, unhexlify
|
|
|
>>> PDU.from_aper(unhexlify('00110034000004003b0008000001f100000010003c40110700656e62303030312d636f72656e657400400007000000400001f10089400140'))
|
|
|
>>> print(PDU.to_asn1())
|
|
|
initiatingMessage : {
|
|
|
procedureCode 17,
|
|
|
criticality reject,
|
|
|
value S1SetupRequest: {
|
|
|
protocolIEs {
|
|
|
{
|
|
|
id 59,
|
|
|
criticality reject,
|
|
|
value Global-ENB-ID: {
|
|
|
pLMNidentity '0001F1'H,
|
|
|
eNB-ID macroENB-ID : '00001'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
id 60,
|
|
|
criticality ignore,
|
|
|
value ENBname: "enb0001-corenet"
|
|
|
},
|
|
|
{
|
|
|
id 64,
|
|
|
criticality reject,
|
|
|
value SupportedTAs: {
|
|
|
{
|
|
|
tAC '0001'H,
|
|
|
broadcastPLMNs {
|
|
|
'0001F1'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
id 137,
|
|
|
criticality ignore,
|
|
|
value PagingDRX: v128
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> IEs = get_val_at(PDU, ['initiatingMessage', 'value', 'S1SetupRequest', 'protocolIEs'])
|
|
|
>>> for ie in IEs: print(ie['value'])
|
|
|
...
|
|
|
('Global-ENB-ID', {'pLMNidentity': b'\x00\x01\xf1', 'eNB-ID': ('macroENB-ID', (1, 20))})
|
|
|
('ENBname', 'enb0001-corenet')
|
|
|
('SupportedTAs', [{'tAC': b'\x00\x01', 'broadcastPLMNs': [b'\x00\x01\xf1']}])
|
|
|
('PagingDRX', 'v128')
|
|
|
|
|
|
And here is an example with the encoding of an S1 Initial UE message:
|
|
|
|
|
|
>>> for ie in S1AP.S1AP_PDU_Contents.InitialUEMessage_IEs().root: print(ie)
|
|
|
...
|
|
|
{'Value': <Value ([ENB-UE-S1AP-ID] INTEGER)>, 'id': 8, 'criticality': 'reject', 'presence': 'mandatory'}
|
|
|
{'Value': <Value ([NAS-PDU] OCTET STRING)>, 'id': 26, 'criticality': 'reject', 'presence': 'mandatory'}
|
|
|
{'Value': <Value ([TAI] SEQUENCE)>, 'id': 67, 'criticality': 'reject', 'presence': 'mandatory'}
|
|
|
{'Value': <Value ([EUTRAN-CGI] SEQUENCE)>, 'id': 100, 'criticality': 'ignore', 'presence': 'mandatory'}
|
|
|
{'Value': <Value ([RRC-Establishment-Cause] ENUMERATED)>, 'id': 134, 'criticality': 'ignore', 'presence': 'mandatory'}
|
|
|
{'Value': <Value ([S-TMSI] SEQUENCE)>, 'id': 96, 'criticality': 'reject', 'presence': 'optional'}
|
|
|
{'Value': <Value ([CSG-Id] BIT STRING)>, 'id': 127, 'criticality': 'reject', 'presence': 'optional'}
|
|
|
{'Value': <Value ([GUMMEI] SEQUENCE)>, 'id': 75, 'criticality': 'reject', 'presence': 'optional'}
|
|
|
{'Value': <Value ([CellAccessMode] ENUMERATED)>, 'id': 145, 'criticality': 'reject', 'presence': 'optional'}
|
|
|
{'Value': <Value ([TransportLayerAddress] BIT STRING)>, 'id': 155, 'criticality': 'ignore', 'presence': 'optional'}
|
|
|
{'Value': <Value ([RelayNode-Indicator] ENUMERATED)>, 'id': 160, 'criticality': 'reject', 'presence': 'optional'}
|
|
|
{'Value': <Value ([GUMMEIType] ENUMERATED)>, 'id': 170, 'criticality': 'ignore', 'presence': 'optional'}
|
|
|
{'Value': <Value ([TunnelInformation] SEQUENCE)>, 'id': 176, 'criticality': 'ignore', 'presence': 'optional'}
|
|
|
{'Value': <Value ([TransportLayerAddress] BIT STRING)>, 'id': 184, 'criticality': 'ignore', 'presence': 'optional'}
|
|
|
{'Value': <Value ([LHN-ID] OCTET STRING)>, 'id': 186, 'criticality': 'ignore', 'presence': 'optional'}
|
|
|
{'Value': <Value ([MME-Group-ID] OCTET STRING)>, 'id': 223, 'criticality': 'ignore', 'presence': 'optional'}
|
|
|
{'Value': <Value ([UE-Usage-Type] INTEGER)>, 'id': 230, 'criticality': 'ignore', 'presence': 'optional'
|
|
|
>>> IEs = []
|
|
|
>>> IEs.append({'id': 8, 'value': ('ENB-UE-S1AP-ID', 1202), 'criticality': 'reject'})
|
|
|
>>> IEs.append({'id': 26, 'value': ('NAS-PDU', unhexlify('0741720bf600f11040000af910512604e060c04000240205d011d1271d8080211001000010810600000000830600000000000d00000a00001000500bf600f110000101c8d595065200f11000015c0a003103e5e0341300f110400011035758a65d0100c1')), 'criticality': 'reject'})
|
|
|
>>> IEs.append({'id': 67, 'value': ('TAI', {'pLMNidentity': b'\x00\x01\xf1', 'tAC': b'\x00\x01'}), 'criticality': 'reject'})
|
|
|
>>> IEs.append({'id': 100, 'value': ('EUTRAN-CGI', {'cell-ID': (1, 28), 'pLMNidentity': b'\x00\x01\xf1'}), 'criticality': 'ignore'})
|
|
|
>>> IEs.append({'id': 134, 'value': ('RRC-Establishment-Cause', 'highPriorityAccess'), 'criticality': 'ignore'})
|
|
|
>>> val = ('initiatingMessage', {'procedureCode': 12, 'value': ('InitialUEMessage', {'protocolIEs': IEs}), 'criticality': 'ignore'})
|
|
|
>>> PDU.set_val(val)
|
|
|
>>> print(PDU.to_asn1())
|
|
|
initiatingMessage : {
|
|
|
procedureCode 12,
|
|
|
criticality ignore,
|
|
|
value InitialUEMessage: {
|
|
|
protocolIEs {
|
|
|
{
|
|
|
id 8,
|
|
|
criticality reject,
|
|
|
value ENB-UE-S1AP-ID: 1202
|
|
|
},
|
|
|
{
|
|
|
id 26,
|
|
|
criticality reject,
|
|
|
value NAS-PDU: '0741720BF600F11040000AF910512604E060C04000240205D011D1271D8080211001000010810600000000830600000000000D00000A00001000500BF600F110000101C8D595065200F11000015C0A003103E5E0341300F110400011035758A65D0100C1'H
|
|
|
},
|
|
|
{
|
|
|
id 67,
|
|
|
criticality reject,
|
|
|
value TAI: {
|
|
|
pLMNidentity '0001F1'H,
|
|
|
tAC '0001'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
id 100,
|
|
|
criticality ignore,
|
|
|
value EUTRAN-CGI: {
|
|
|
pLMNidentity '0001F1'H,
|
|
|
cell-ID '0000001'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
id 134,
|
|
|
criticality ignore,
|
|
|
value RRC-Establishment-Cause: highPriorityAccess
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> hexlify(PDU.to_aper())
|
|
|
b'000c40808e000005000800034004b2001a0065640741720bf600f11040000af910512604e060c04000240205d011d1271d8080211001000010810600000000830600000000000d00000a00001000500bf600f110000101c8d595065200f11000015c0a003103e5e0341300f110400011035758a65d0100c100430006000001f1000100644008000001f1000000100086400110'
|
|
|
|
|
|
|
|
|
## The TCAP MAP and TCAP CAP protocols
|
|
|
|
|
|
### ASN.1 definition and modules
|
|
|
The MAP (Mobile Application Part) and CAP (Camel Application Part) protocols are used within
|
|
|
mobile core networks, they were initially specified by ETSI and are now maintained by the 3GPP.
|
|
|
They are transported over the TCAP protocol, which is specified by ITU-T under the tiny name
|
|
|
Q.773; it is quite an old protocol, the initial version dates back from 1988, and the current one
|
|
|
is from 1997.
|
|
|
|
|
|
The MAP specification can be found in the 3GPP standard
|
|
|
[TS 29.002](http://www.3gpp.org/DynaReport/29002.htm), the ASN.1 definition is available
|
|
|
in the directory
|
|
|
[pycrate_asn1dir/3GPP_MAP_29002/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/3GPP_MAP_29002/)
|
|
|
and the corresponding Python modules are in the compiled file
|
|
|
[pycrate_asn1dir/MAP.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/MAP.py).
|
|
|
|
|
|
The CAP specification can be found in the 3GPP standard
|
|
|
[TS 29.078](http://www.3gpp.org/DynaReport/29078.htm), the ASN.1 definition is available
|
|
|
in the directory
|
|
|
[pycrate_asn1dir/3GPP_CAP_29078/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/3GPP_CAP_29078/)
|
|
|
and the corresponding Python modules are in the compiled file
|
|
|
[pycrate_asn1dir/CAP.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/CAP.py).
|
|
|
|
|
|
The TCAP specification can be found in the ITU-T standard
|
|
|
[Q.773](http://www.itu.int/rec/T-REC-Q.773-199706-I), the ASN.1 definition is available
|
|
|
in the directory
|
|
|
[pycrate_asn1dir/ITUT_Q773_1997-06/](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/ITUT_Q773_1997-06/)
|
|
|
and the corresponding Python modules are in the compiled file
|
|
|
[pycrate_asn1dir/TCAP.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/TCAP.py).
|
|
|
|
|
|
The *TCMessage* object which corresponds to the structure of a TCAP message transporting MAP or CAP data
|
|
|
needs actually to be parameterized with higher level protocol definition. Moreover, the EXTERNAL object
|
|
|
used within the TCAP specification is from an older ASN.1 standard (the one from 1988)
|
|
|
and does not correspond to the EXTERNAL structure defined in the pycrate ASN.1 runtime.
|
|
|
For this reasons, two custom modules have been created:
|
|
|
- [pycrate_asn1dir/Pycrate-TCAP-MAP](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/Pycrate-TCAP-MAP/)
|
|
|
with the corresponding compiled file
|
|
|
[pycrate_asn1dir/TCAP_MAP.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/TCAP_MAP.py)
|
|
|
and the top-level object *TCAP-MAP-Message*
|
|
|
- [pycrate_asn1dir/Pycrate-TCAP-CAP](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/Pycrate-TCAP-CAP/)
|
|
|
with the corresponding compiled file
|
|
|
[pycrate_asn1dir/TCAP_CAP.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/TCAP_CAP.py);
|
|
|
CAP has multiple top-level objects defined in the *CAP-gsm* and *CAP-gprs* modules.
|
|
|
|
|
|
All those protocols are using the ASN.1 BER encoding.
|
|
|
|
|
|
### TCAP MAP
|
|
|
MAP is used between mobile core network equipments (MSC/VLR, SGSN, HLR, ...) to
|
|
|
handle technical information about connected subscribers.
|
|
|
The MAP specification defines several MAP procedures in multiple ASN.1 modules,
|
|
|
all of them are grouped into a single ASN.1 set: the *MAP-Protocol.Supported-MAP-Operations* object.
|
|
|
On the TCAP side, a TCAP message is defined by the *TCAPMessages.TCMessage* object,
|
|
|
which needs to be parameterized with a set of upper layer procedures.
|
|
|
This is exactly what is accomplished with the custom object *TCAP-MAP-Messages.TCAP-MAP-Message*.
|
|
|
It can be used to encode and decode any TCAP-MAP messages.
|
|
|
|
|
|
Here is what happens when decoding the TCAP MAP message containing an USSD request,
|
|
|
as proposed on the [Wireshark wiki](https://wiki.wireshark.org/SampleCaptures), and
|
|
|
re-encoding it with a null USSD string:
|
|
|
|
|
|
>>> from pycrate_asn1dir import TCAP_MAP
|
|
|
>>> M = TCAP_MAP.TCAP_MAP_Messages.TCAP_MAP_Message
|
|
|
>>> M
|
|
|
<TCAP-MAP-Message ([TCMessage] CHOICE)>
|
|
|
>>> M.get_proto()
|
|
|
{
|
|
|
unidirectional: {
|
|
|
dialoguePortion: {
|
|
|
direct-reference: 'OBJECT IDENTIFIER',
|
|
|
indirect-reference: 'INTEGER',
|
|
|
data-value-descriptor: 'ObjectDescriptor',
|
|
|
[...]
|
|
|
single-ASN1-type: 'OPEN_TYPE',
|
|
|
octet-aligned: 'OCTET STRING',
|
|
|
arbitrary: 'BIT STRING'
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> from binascii import hexlify, unhexlify
|
|
|
>>> M.from_ber(unhexlify('626a48042f3b46026b3a2838060700118605010101a02d602b80020780a109060704000001001302be1a2818060704000001010101a00da00b80099656051124006913f66c26a12402010102013b301c04010f040eaa180da682dd6c31192d36bbdd468007917267415827f2'))
|
|
|
>>> print(M.to_asn1())
|
|
|
begin : {
|
|
|
otid '2F3B4602'H,
|
|
|
dialoguePortion {
|
|
|
direct-reference {0 0 17 773 1 1 1} -- dialogue-as-id --,
|
|
|
encoding single-ASN1-type : DialoguePDU: dialogueRequest : {
|
|
|
protocol-version '1'B -- version1 --,
|
|
|
application-context-name {0 4 0 0 1 0 19 2} -- networkUnstructuredSsContext-v2 --,
|
|
|
user-information {
|
|
|
{
|
|
|
direct-reference {0 4 0 0 1 1 1 1} -- map-DialogueAS --,
|
|
|
encoding single-ASN1-type : MAP-DialoguePDU: map-open : {
|
|
|
destinationReference '9656051124006913F6'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
components {
|
|
|
basicROS : invoke : {
|
|
|
invokeId present : 1,
|
|
|
opcode local : 59,
|
|
|
argument USSD-Arg: {
|
|
|
ussd-DataCodingScheme '0F'H,
|
|
|
ussd-String 'AA180DA682DD6C31192D36BBDD46'H,
|
|
|
msisdn '917267415827F2'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> from pycrate_asn1rt.utils import *
|
|
|
>>> get_val_at(M, ['begin', 'components', 0, 'basicROS', 'invoke', 'argument', 'USSD-Arg'])
|
|
|
{'ussd-String': b'\xaa\x18\r\xa6\x82\xddl1\x19-6\xbb\xddF', 'msisdn': b"\x91rgAX'\xf2", 'ussd-DataCodingScheme': b'\x0f'}
|
|
|
>>> get_val_at(M, ['begin', 'components', 0, 'basicROS', 'invoke', 'argument', 'USSD-Arg'])['ussd-String'] = b'\x00'
|
|
|
>>> print(M.to_asn1())
|
|
|
begin : {
|
|
|
otid '2F3B4602'H,
|
|
|
[...]
|
|
|
ussd-DataCodingScheme '0F'H,
|
|
|
ussd-String '00'H,
|
|
|
msisdn '917267415827F2'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> hexlify(M.to_ber())
|
|
|
b'625d48042f3b46026b3a2838060700118605010101a02d602b80020780a109060704000001001302be1a2818060704000001010101a00da00b80099656051124006913f66c19a11702010102013b300f04010f0401008007917267415827f2'
|
|
|
|
|
|
### TCAP CAP
|
|
|
Camel is mainly used for messaging and subscription management services in mobile
|
|
|
core networks. The CAP specification defines several CAP procedures and corresponding
|
|
|
TCAP messages in different ASN.1 modules:
|
|
|
- module *CAP-gprsSSF-gsmSCF-pkgs-contracts-acs* defines the top-level objects
|
|
|
*GenericGprsSSF-gsmSCF-PDUs* and *GenericGsmSCF-gprsSSF-PDUs*
|
|
|
- module *CAP-gsmSCF-gsmSRF-pkgs-contracts-acs* defines the top-level object *BASIC-gsmSRF-gsmSCF-PDUs*
|
|
|
- module *CAP-gsmSSF-gsmSCF-pkgs-contracts-acs* defines the top-level objects
|
|
|
*GenericSSF-gsmSCF-PDUs*, *AssistHandoffsSF-gsmSCF-PDUs* and *GenericSCF-gsmSSF-PDUs*
|
|
|
- module *CAP-smsSSF-gsmSCF-pkgs-contracts-acs* defines the top-level objects
|
|
|
*Generic-sms-PDUs*
|
|
|
|
|
|
Taking again an example from the Wireshark wiki, here is what happens when decoding
|
|
|
and re-encoding an InitialDP operation CAP message, that is handled by the ASN.1 object
|
|
|
*CAP-gsmSSF-gsmSCF-pkgs-contracts-acs.GenericSSF-gsmSCF-PDUs*:
|
|
|
|
|
|
>>> M = TCAP_CAP.CAP_gsmSSF_gsmSCF_pkgs_contracts_acs.GenericSSF_gsmSCF_PDUs
|
|
|
>>> M
|
|
|
<GenericSSF-gsmSCF-PDUs ([TCMessage] CHOICE)>
|
|
|
>>> from binascii import unhexlify, hexlify
|
|
|
>>> M.from_ber(unhexlify('628187480206f76b1e281c060700118605010101a011600f80020780a1090607040000010032016c61a15f020101020100305780012a830884111487095040f79c01029f32061487572586f9bf34148107913366020000f0a3098007313233343536379f3605a12345678f9f3707913366020000f09f3807111487085040f79f39080230900211223370'))
|
|
|
>>> print(M.to_asn1())
|
|
|
begin : {
|
|
|
otid '06F7'H,
|
|
|
dialoguePortion {
|
|
|
direct-reference {0 0 17 773 1 1 1} -- dialogue-as-id --,
|
|
|
encoding single-ASN1-type : DialoguePDU: dialogueRequest : {
|
|
|
protocol-version '1'B -- version1 --,
|
|
|
application-context-name {0 4 0 0 1 0 50 1}
|
|
|
}
|
|
|
},
|
|
|
components {
|
|
|
basicROS : invoke : {
|
|
|
invokeId present : 1,
|
|
|
opcode local : 0,
|
|
|
argument InitialDPArg: {
|
|
|
serviceKey 42,
|
|
|
callingPartyNumber '84111487095040F7'H,
|
|
|
eventTypeBCSM collectedInfo,
|
|
|
iMSI '1487572586F9'H,
|
|
|
locationInformation {
|
|
|
vlr-number '913366020000F0'H,
|
|
|
cellGlobalIdOrServiceAreaIdOrLAI cellGlobalIdOrServiceAreaIdFixedLength : '31323334353637'H -- 1234567 --
|
|
|
},
|
|
|
callReferenceNumber 'A12345678F'H,
|
|
|
mscAddress '913366020000F0'H,
|
|
|
calledPartyBCDNumber '111487085040F7'H,
|
|
|
timeAndTimezone '0230900211223370'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> from pycrate_asn1rt.utils import *
|
|
|
>>> get_val_at(M, ['begin', 'components', 0, 'basicROS', 'invoke', 'argument', 'InitialDPArg', 'locationInformation'])
|
|
|
{'vlr-number': b'\x913f\x02\x00\x00\xf0', 'cellGlobalIdOrServiceAreaIdOrLAI': ('cellGlobalIdOrServiceAreaIdFixedLength', b'1234567')}
|
|
|
>>> # let's modify the locationInformation
|
|
|
>>> get_obj_at(M, ['begin', 'components', 0, 'basicROS', 'invoke', 'argument', 'InitialDPArg', 'locationInformation', 'cellGlobalIdOrServiceAreaIdOrLAI'])
|
|
|
<cellGlobalIdOrServiceAreaIdOrLAI ([CellGlobalIdOrServiceAreaIdOrLAI] CHOICE)>
|
|
|
>>> get_obj_at(M, ['begin', 'components', 0, 'basicROS', 'invoke', 'argument', 'InitialDPArg', 'locationInformation', 'cellGlobalIdOrServiceAreaIdOrLAI'])._cont
|
|
|
{
|
|
|
cellGlobalIdOrServiceAreaIdFixedLength: <cellGlobalIdOrServiceAreaIdFixedLength ([CellGlobalIdOrServiceAreaIdFixedLength] OCTET STRING)>,
|
|
|
laiFixedLength: <laiFixedLength ([LAIFixedLength] OCTET STRING)>
|
|
|
}
|
|
|
>>> get_val_at(M, ['begin', 'components', 0, 'basicROS', 'invoke', 'argument', 'InitialDPArg', 'locationInformation'])['cellGlobalIdOrServiceAreaIdOrLAI'] = ('laiFixedLength', b'myLai01234')
|
|
|
>>> print(M.to_asn1())
|
|
|
begin : {
|
|
|
otid '06F7'H,
|
|
|
dialoguePortion {
|
|
|
direct-reference {0 0 17 773 1 1 1} -- dialogue-as-id --,
|
|
|
encoding single-ASN1-type : DialoguePDU: dialogueRequest : {
|
|
|
protocol-version '1'B -- version1 --,
|
|
|
application-context-name {0 4 0 0 1 0 50 1}
|
|
|
}
|
|
|
},
|
|
|
components {
|
|
|
basicROS : invoke : {
|
|
|
invokeId present : 1,
|
|
|
opcode local : 0,
|
|
|
argument InitialDPArg: {
|
|
|
serviceKey 42,
|
|
|
callingPartyNumber '84111487095040F7'H,
|
|
|
eventTypeBCSM collectedInfo,
|
|
|
iMSI '1487572586F9'H,
|
|
|
locationInformation {
|
|
|
vlr-number '913366020000F0'H,
|
|
|
cellGlobalIdOrServiceAreaIdOrLAI laiFixedLength : '6D794C61693031323334'H -- myLai01234 --
|
|
|
},
|
|
|
callReferenceNumber 'A12345678F'H,
|
|
|
mscAddress '913366020000F0'H,
|
|
|
calledPartyBCDNumber '111487085040F7'H,
|
|
|
timeAndTimezone '0230900211223370'H
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
>>> hexlify(M.to_ber())
|
|
|
b'62818a480206f76b1e281c060700118605010101a011600f80020780a1090607040000010032016c64a162020101020100305a80012a830884111487095040f79c01029f32061487572586f9bf34178107913366020000f0a30c810a6d794c616930313233349f3605a12345678f9f3707913366020000f09f3807111487085040f79f39080230900211223370'
|
|
|
|
|
|
|
|
|
## The X.509 digital certificate format
|
|
|
|
|
|
### ASN.1 definition and modules
|
|
|
X.509 is an ITU-T recommendation. The last version of this document
|
|
|
dates from 2016 and is unfortunately behind a paywall.
|
|
|
It is however possible to access the PDF version of 2012 for free:
|
|
|
[X.509-2012-10](https://www.itu.int/rec/T-REC-X.509-201210-S/)
|
|
|
This recommendation has actually quite a broad scope, from public-key certificate,
|
|
|
attribute certificates and authentication services, and also quite old: the 1st
|
|
|
version was published in 1988.
|
|
|
The corresponding ASN.1 specification can be found in the fomal description section of
|
|
|
this [web page](http://www.itu.int/itu-t/recommendations/rec.aspx?rec=X.509#).
|
|
|
It is a set of 8 ASN.1 modules, which actually requires many other modules from other
|
|
|
ITU-T specifications to be compiled.
|
|
|
|
|
|
The X.509 2016 ASN.1 module from ITU-T is available in the directory
|
|
|
[pycrate_asn1dir/ITUT_X509_2016-10](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/ITUT_X509_2016-10/)
|
|
|
and the corresponding Python module are in the compiled file
|
|
|
[pycrate_asn1dir/X509_2016.py](https://github.com/ANSSI-FR/pycrate/blob/master/pycrate_asn1dir/X509_2016.py)
|
|
|
Few errors have been corrected compared to the archive of ASN.1 files provided by the
|
|
|
ITU-T web site for the specification to compile correctly.
|
|
|
|
|
|
The definition of an X.509 certificate is provided by the object *Certificate*
|
|
|
in the *AuthenticationFramework* module. Certificates are known to be DER-encoded.
|
|
|
|
|
|
### X.509 in practice
|
|
|
|
|
|
Let's see how the X.509 cetificate of the ITU-T website is structured, according
|
|
|
to their ASN.1 specification. With any good web browser, we can export the certificate
|
|
|
to the PEM format: the certificate is base64-encoded between two specific lines:
|
|
|
*BEGIN CERTIFICATE* and *END CERTIFICATE*. The PEM-encoded certificate from the ITU-T
|
|
|
web site is 2718 bytes long.
|
|
|
|
|
|
Let's see what happen when we load the compiled module and decode the certificate
|
|
|
from the ITU-T website:
|
|
|
|
|
|
>>> from pycrate_asn1dir.X509_2016 import *
|
|
|
init_modules: different OID objects (id-oc, objectClass) with same OID value ((2, 5, 6))
|
|
|
init_modules: different OID objects (id-at, attributeType) with same OID value ((2, 5, 4))
|
|
|
[...]
|
|
|
>>> Cert = AuthenticationFramework.Certificate
|
|
|
>>> Cert
|
|
|
<Certificate ([SIGNED] SEQUENCE)>
|
|
|
>>> Cert.get_proto()
|
|
|
{
|
|
|
toBeSigned: {
|
|
|
version: 'INTEGER',
|
|
|
serialNumber: 'INTEGER',
|
|
|
signature: {
|
|
|
algorithm: 'OBJECT IDENTIFIER',
|
|
|
parameters: 'OPEN_TYPE'
|
|
|
},
|
|
|
issuer: {
|
|
|
rdnSequence: [[{
|
|
|
type: 'OBJECT IDENTIFIER',
|
|
|
value: 'OPEN_TYPE'
|
|
|
}]]
|
|
|
},
|
|
|
validity: {
|
|
|
notBefore: {
|
|
|
utcTime: 'UTCTime',
|
|
|
generalizedTime: 'GeneralizedTime'
|
|
|
},
|
|
|
notAfter: {
|
|
|
utcTime: 'UTCTime',
|
|
|
generalizedTime: 'GeneralizedTime'
|
|
|
}
|
|
|
},
|
|
|
subject: {
|
|
|
rdnSequence: [[{
|
|
|
type: 'OBJECT IDENTIFIER',
|
|
|
value: 'OPEN_TYPE'
|
|
|
}]]
|
|
|
},
|
|
|
subjectPublicKeyInfo: {
|
|
|
algorithm: {
|
|
|
algorithm: 'OBJECT IDENTIFIER',
|
|
|
parameters: 'OPEN_TYPE'
|
|
|
},
|
|
|
subjectPublicKey: 'BIT STRING'
|
|
|
},
|
|
|
issuerUniqueIdentifier: 'BIT STRING',
|
|
|
subjectUniqueIdentifier: 'BIT STRING',
|
|
|
extensions: [{
|
|
|
extnId: 'OBJECT IDENTIFIER',
|
|
|
critical: 'BOOLEAN',
|
|
|
extnValue: 'OCTET STRING'
|
|
|
}]
|
|
|
},
|
|
|
algorithmIdentifier: {
|
|
|
algorithm: 'OBJECT IDENTIFIER',
|
|
|
parameters: 'OPEN_TYPE'
|
|
|
},
|
|
|
signature: 'BIT STRING'
|
|
|
}
|
|
|
>>> cl = open('~/Downloads/ituint.crt').readlines()
|
|
|
>>> import base64
|
|
|
>>> cb = base64.b64decode(''.join(cl[1:-1]))
|
|
|
>>> Cert.from_der(cb)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.issuer.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 6) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.issuer.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 8) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.issuer.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 7) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.issuer.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 10) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.issuer.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.issuer.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 3) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 5) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 3) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 15) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 6) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 17) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 8) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 7) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 9) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 10) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 11) for identifier id in the table constraint)
|
|
|
SEQUENCE._decode_ber_cont_ws: Certificate.toBeSigned.subject.rdnSequence._item_._item_, unable to determine if component value is present (err 3)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 3) for identifier id in the table constraint)
|
|
|
>>> print(Cert.to_asn1())
|
|
|
{
|
|
|
toBeSigned {
|
|
|
version 2 -- v3 --,
|
|
|
serialNumber 163212335596803956569165623251943599291,
|
|
|
signature {
|
|
|
algorithm {1 2 840 113549 1 1 11} -- sha256WithRSAEncryption --,
|
|
|
parameters NULL: NULL
|
|
|
},
|
|
|
issuer rdnSequence : {
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 6} -- id-at-countryName --,
|
|
|
value '4742'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 8} -- id-at-stateOrProvinceName --,
|
|
|
value '47726561746572204D616E63686573746572'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 7} -- id-at-localityName --,
|
|
|
value '53616C666F7264'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 10} -- id-at-organizationName --,
|
|
|
value '434F4D4F444F204341204C696D69746564'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 3} -- id-at-commonName --,
|
|
|
value '434F4D4F444F2052534120457874656E6465642056616C69646174696F6E2053656375726520536572766572204341'H
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
validity {
|
|
|
notBefore utcTime : "170328000000Z" -- Tue Mar 28 00:00:00 2017 --,
|
|
|
notAfter utcTime : "190328235959Z" -- Thu Mar 28 23:59:59 2019 --
|
|
|
},
|
|
|
subject rdnSequence : {
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 5} -- id-at-serialNumber --,
|
|
|
value '5265736F6C7574696F6E204E6F20393020412F333730'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {1 3 6 1 4 1 311 60 2 1 3},
|
|
|
value '4348'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 15} -- id-at-businessCategory --,
|
|
|
value '4E6F6E2D436F6D6D65726369616C20456E74697479'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 6} -- id-at-countryName --,
|
|
|
value '4348'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 17} -- id-at-postalCode --,
|
|
|
value '31323131'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 8} -- id-at-stateOrProvinceName --,
|
|
|
value '47656EC3A87665203230'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 7} -- id-at-localityName --,
|
|
|
value '47656EC3A87665'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 9} -- id-at-streetAddress --,
|
|
|
value '506C61636520646573204E6174696F6E73'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 10} -- id-at-organizationName --,
|
|
|
value '496E7465726E6174696F6E616C2054656C65636F6D6D756E69636174696F6E7320556E696F6E202849545529'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 11} -- id-at-organizationalUnitName --,
|
|
|
value '434F4D4F444F2045562053534C'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 3} -- id-at-commonName --,
|
|
|
value '7777772E6974752E696E74'H
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
subjectPublicKeyInfo {
|
|
|
algorithm {
|
|
|
algorithm {1 2 840 113549 1 1 1} -- rsaEncryption --,
|
|
|
parameters NULL: NULL
|
|
|
},
|
|
|
subjectPublicKey '3082010A0282010100C5C4341CF4363220289A51E75B53333105216FB691124A6AB2E8D595525FA682BB9A257737F4140F06281D310DB7BB005986FF17088F61362A8A9A1727D64AF36B281E4D932E07717BF50B8305BFD36F18F73B1154BB6FF6D9E2DF53A3C71DD66F94829BE1613151C3B729482A713112F6912773A62564F551693D1310DB3076CDE24F4556A51DCADC27E537E1E2AB53354F8DCC0441A706328720126AD8AA7AA455E3D93BAD77039E9B73596A68112C83909E524DD9AF48100A610E27FD5C419AC4F4565A9E438A9B5E466E8137D9C144A1C81A93F0493C38040E7B9144F6F3EC4E6DF29ABDE2716F762856BF5D60A43645B4493FDB0754CEFCBA037EA694DB0203010001'H
|
|
|
},
|
|
|
extensions {
|
|
|
{
|
|
|
extnId {2 5 29 35} -- id-ce-authorityKeyIdentifier --,
|
|
|
extnValue '3016801439DAFFCA28148AA8741308B9E40EA9D2FA7E9D69'H
|
|
|
},
|
|
|
{
|
|
|
extnId {2 5 29 14} -- id-ce-subjectKeyIdentifier --,
|
|
|
extnValue '04144CCAF046405EC0DAF417682805C993E47B16CCEA'H
|
|
|
},
|
|
|
{
|
|
|
extnId {2 5 29 15} -- id-ce-keyUsage --,
|
|
|
critical TRUE,
|
|
|
extnValue '030205A0'H
|
|
|
},
|
|
|
{
|
|
|
extnId {2 5 29 19} -- id-ce-basicConstraints --,
|
|
|
critical TRUE,
|
|
|
extnValue '3000'H
|
|
|
},
|
|
|
{
|
|
|
extnId {2 5 29 37} -- id-ce-extKeyUsage --,
|
|
|
extnValue '301406082B0601050507030106082B06010505070302'H
|
|
|
},
|
|
|
{
|
|
|
extnId {2 5 29 32} -- id-ce-certificatePolicies --,
|
|
|
extnValue '303D303B060C2B06010401B2310102010501302B302906082B06010505070201161D68747470733A2F2F7365637572652E636F6D6F646F2E636F6D2F435053'H
|
|
|
},
|
|
|
{
|
|
|
extnId {2 5 29 31} -- id-ce-cRLDistributionPoints --,
|
|
|
extnValue '304D304BA049A0478645687474703A2F2F63726C2E636F6D6F646F63612E636F6D2F434F4D4F444F525341457874656E64656456616C69646174696F6E53656375726553657276657243412E63726C'H
|
|
|
},
|
|
|
{
|
|
|
extnId {1 3 6 1 5 5 7 1 1} -- id-pe-authorityInfoAccess --,
|
|
|
extnValue '3079305106082B060105050730028645687474703A2F2F6372742E636F6D6F646F63612E636F6D2F434F4D4F444F525341457874656E64656456616C69646174696F6E53656375726553657276657243412E637274302406082B060105050730018618687474703A2F2F6F6373702E636F6D6F646F63612E636F6D'H
|
|
|
},
|
|
|
{
|
|
|
extnId {2 5 29 17} -- id-ce-subjectAltName --,
|
|
|
extnValue '3016820B7777772E6974752E696E7482076974752E696E74'H
|
|
|
},
|
|
|
{
|
|
|
extnId {1 3 6 1 4 1 11129 2 4 2},
|
|
|
extnValue '0482016C016A007600A4B90990B418581487BB13A2CC67700A3C359804F91BDFB8E377CD0EC80DDC100000015B157198BC000004030047304502207B8D96679302EFB061C71EF0762BD9FF1938FEB604DFAB820B00684F35E90A69022100DAE8F2A04DFF20536CDF0B9FA56FCE19D4DE8227FC9BB17E0DC7383DF0F8772A0077005614069A2FD7C2ECD3F5E1BD44B23EC74676B9BC99115CC0EF949855D689D0DD0000015B1571964F0000040300483046022100D6B0C8B655FA18E02521178AD3F2AB2D41672C7177A853B4F8124218F0788270022100A9827EAAE294130A3D23D224511533B2BEAA972EF59B592EEC126A045202A978007700EE4BBDB775CE60BAE142691FABE19E66A30F7E5FB072D88300C47B897AA8FDCB0000015B1571988600000403004830460221008451A06AB517D32A0DA9408E28C50E05DB7B35576A188B80F567A6A09C4D7E49022100CE45402927D318B911F922F5F856DEB05DC36CC30B71DFCD7E12E8FCC0B86587'H
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
algorithmIdentifier {
|
|
|
algorithm {1 2 840 113549 1 1 11} -- sha256WithRSAEncryption --,
|
|
|
parameters NULL: NULL
|
|
|
},
|
|
|
signature '4ABE511D378ECD7FB8B69BED9588E11B1550D84E626E0391C56D6780B15819AE18248259399A72DAEFADE86B6C26F6C47ACCC44E43B4D142EFEB55A14C478056204A902A2759CF0F1CE5CD74F08D257055A89BCA627A5B9D19DDA1E516346E323E43F7B613935E4605BA8AFCB6DFB52D7ADC4CCB0029A7E9FFD62ED19738D8531B048C97ECA51082FD50974958165D9E6C1D5279DDE1F0019C80E773E2E801C8FAF7FE72FBFB8FA4AEBFE96CB38B1E88940255BCB1C8E578DAE9A188AD5F38D626E73A61E37DC63686789066CAF732FE4C3B1CCDF98B80F5A0BFC54B4DDB5B7041F471DC795941816CD5949E8ED0D47C127BE9D1B182EE8C4B42CD711CC6EA89'H
|
|
|
}
|
|
|
|
|
|
We get lots of warnings from the runtime. This is because the X.509 specification
|
|
|
makes use of many OPEN types (e.g. for attributes, extensions, algorithm parameters),
|
|
|
but the ITU-T specification does not define complete look-up tables to resolve all those
|
|
|
open types.
|
|
|
For instance, in the *AuthenticationFramework* module, the set of said supported
|
|
|
algorithms is empty:
|
|
|
|
|
|
SupportedAlgorithms ALGORITHM ::= {...}
|
|
|
|
|
|
This means that, when parsing a certificate, the runtime will not be able to link
|
|
|
an algorithm OID to the type of its parameters.
|
|
|
|
|
|
This has been manually completed in the pycrate X509 ASN.1 specification,
|
|
|
with the list of basic algorithms defined in the *AlgorithmObjectIdentifiers*,
|
|
|
to create the set *AllAlgorithmsOID*.
|
|
|
Thanks to this, the runtime is actually capable to link algorithm OID to their
|
|
|
corresponding parameters (which are all NULL, in our case). You can see it here:
|
|
|
|
|
|
>>> get_val_at(Cert, ['toBeSigned', 'signature'])
|
|
|
{'algorithm': (1, 2, 840, 113549, 1, 1, 11), 'parameters': ('NULL', 0)}
|
|
|
|
|
|
>>> get_val_at(Cert, ['toBeSigned', 'subjectPublicKeyInfo', 'algorithm'])
|
|
|
{'algorithm': (1, 2, 840, 113549, 1, 1, 1), 'parameters': ('NULL', 0)}
|
|
|
|
|
|
>>> get_val_at(Cert, ['algorithmIdentifier'])
|
|
|
{'algorithm': (1, 2, 840, 113549, 1, 1, 11), 'parameters': ('NULL', 0)}
|
|
|
|
|
|
If you want to translate OID tuple values into their names, you can have a look
|
|
|
at the GLOBAL.OID dictionnary created when loading the Python module:
|
|
|
|
|
|
>>> GLOBAL.OID
|
|
|
{(0, 9, 2342, 19200300, 100): 'cosine',
|
|
|
[...]
|
|
|
(2, 16, 840, 1, 101, 3, 4, 2): 'hashAlgs',
|
|
|
(2, 16, 840, 1, 101, 3, 4, 2, 1): 'id-sha256',
|
|
|
(2, 16, 840, 1, 101, 3, 4, 2, 2): 'id-sha384',
|
|
|
(2, 16, 840, 1, 101, 3, 4, 2, 3): 'id-sha512',
|
|
|
(2, 16, 840, 1, 101, 3, 4, 2, 4): 'id-sha224',
|
|
|
(2, 16, 840, 1, 101, 3, 4, 2, 5): 'id-sha512-224',
|
|
|
(2, 16, 840, 1, 101, 3, 4, 2, 6): 'id-sha512-256',
|
|
|
[...]
|
|
|
}
|
|
|
|
|
|
This same problem happens also with the set *SupportedAttributes* defined in the
|
|
|
module *InformationFramework*, which is very incomplete. See this extract from the
|
|
|
ITU-T ASN.1 specification:
|
|
|
|
|
|
-- Definition of the following information object set is deferred, perhaps to
|
|
|
-- standardized profiles or to protocol implementation conformance statements. The set
|
|
|
-- is required to specify a table constraint on the values component of Attribute, the
|
|
|
-- value component of AttributeTypeAndValue, and the assertion component of
|
|
|
-- AttributeValueAssertion.
|
|
|
|
|
|
SupportedAttributes ATTRIBUTE ::= {objectClass | aliasedEntryName, ...}
|
|
|
|
|
|
This is why all the certificate's issuer information are not decoded properly (and the
|
|
|
runtime prints warnings).
|
|
|
This is again the case with the set *ExtensionSet* defined in the module *AuthenticationFramework*,
|
|
|
which is completely empty:
|
|
|
|
|
|
EXTENSION ::= {...}
|
|
|
|
|
|
For this reason, the runtime is unable to decode properly the list of extensions in
|
|
|
the certificate.
|
|
|
It seems that finally the ITU-T X.509 ASN.1 definition is not a readily usable specification
|
|
|
for decoding certificates.
|
|
|
|
|
|
Let's see what is provided by the IETF.
|
|
|
|
|
|
### ASN.1 definition of digital certificates in IETF
|
|
|
**Lots of RFC** have been published by the IETF about public key infrastructures, which
|
|
|
contain many ASN.1 specifications about tons of structures to deal with digital certificate,
|
|
|
cryptographic signature and so on... Two majors RFC for ASN.1 definitions are RFC 5911
|
|
|
and 5912, which gather ASN.1 definitions from several others RFC.
|
|
|
|
|
|
The digital certificate object, equivalent to the one we just used from the ITU-T documents, is
|
|
|
located in the *PKIX1Explicit-2009*, taken from the RFC 5912. Let's see how it decodes the previous
|
|
|
certificate blob:
|
|
|
|
|
|
>>> Cert = PKIX1Explicit_2009.Certificate
|
|
|
>>> Cert
|
|
|
<Certificate ([SIGNED] SEQUENCE)>
|
|
|
>>> Cert.from_der(cb)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.signature.parameters, unable to retrieve an object in the table constraint (Certificate.toBeSigned.signature.parameters: non-existent value (1, 2, 840, 113549, 1, 1, 11) for identifier id in the table constraint)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 3) for identifier id in the table constraint)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 15) for identifier id in the table constraint)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 17) for identifier id in the table constraint)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.subject.rdnSequence._item_._item_.value, unable to retrieve an object in the table constraint (Certificate.toBeSigned.subject.rdnSequence._item_._item_.value: non-existent value (2, 5, 4, 9) for identifier id in the table constraint)
|
|
|
OPEN._decode_ber_cont: Certificate.toBeSigned.extensions._item_._cont_extnValue, unable to retrieve an object in the table constraint (Certificate.toBeSigned.extensions._item_._cont_extnValue: non-existent value (1, 3, 6, 1, 4, 1, 11129, 2, 4, 2) for identifier id in the table constraint)
|
|
|
OPEN._decode_ber_cont: Certificate.algorithmIdentifier.parameters, unable to retrieve an object in the table constraint (Certificate.algorithmIdentifier.parameters: non-existent value (1, 2, 840, 113549, 1, 1, 11) for identifier id in the table constraint)
|
|
|
BIT_STR.__from_ber_buf: signature, CONTAINING object decoding failed
|
|
|
>>> print(Cert.to_asn1())
|
|
|
{
|
|
|
toBeSigned {
|
|
|
version 2 -- v3 --,
|
|
|
serialNumber 163212335596803956569165623251943599291,
|
|
|
signature {
|
|
|
algorithm {1 2 840 113549 1 1 11} -- sha256WithRSAEncryption --,
|
|
|
parameters ''H
|
|
|
},
|
|
|
issuer rdnSequence : {
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 6} -- id-at-countryName --,
|
|
|
value PrintableString: "GB"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 8} -- id-at-stateOrProvinceName --,
|
|
|
value DirectoryString: printableString : "Greater Manchester"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 7} -- id-at-localityName --,
|
|
|
value X520LocalityName: printableString : "Salford"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 10} -- id-at-organizationName --,
|
|
|
value DirectoryString: printableString : "COMODO CA Limited"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 3} -- id-at-commonName --,
|
|
|
value X520CommonName: printableString : "COMODO RSA Extended Validation Secure Server CA"
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
validity {
|
|
|
notBefore utcTime : "170328000000Z" -- Tue Mar 28 00:00:00 2017 --,
|
|
|
notAfter utcTime : "190328235959Z" -- Thu Mar 28 23:59:59 2019 --
|
|
|
},
|
|
|
subject rdnSequence : {
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 5} -- id-at-serialNumber --,
|
|
|
value PrintableString: "Resolution No 90 A/370"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {1 3 6 1 4 1 311 60 2 1 3},
|
|
|
value '4348'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 15} -- id-at-businessCategory --,
|
|
|
value '4E6F6E2D436F6D6D65726369616C20456E74697479'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 6} -- id-at-countryName --,
|
|
|
value PrintableString: "CH"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 17} -- id-at-postalCode --,
|
|
|
value '31323131'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 8} -- id-at-stateOrProvinceName --,
|
|
|
value DirectoryString: uTF8String : "Genève 20"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 7} -- id-at-localityName --,
|
|
|
value X520LocalityName: uTF8String : "Genève"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 9} -- id-at-streetAddress --,
|
|
|
value '506C61636520646573204E6174696F6E73'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 10} -- id-at-organizationName --,
|
|
|
value DirectoryString: printableString : "International Telecommunications Union (ITU)"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 11} -- id-at-organizationalUnitName --,
|
|
|
value DirectoryString: printableString : "COMODO EV SSL"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
{
|
|
|
type {2 5 4 3} -- id-at-commonName --,
|
|
|
value X520CommonName: printableString : "www.itu.int"
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
subjectPublicKeyInfo {
|
|
|
algorithm {
|
|
|
algorithm {1 2 840 113549 1 1 1} -- rsaEncryption --,
|
|
|
parameters NULL: NULL
|
|
|
},
|
|
|
subjectPublicKey '3082010A0282010100C5C4341CF4363220289A51E75B53333105216FB691124A6AB2E8D595525FA682BB9A257737F4140F06281D310DB7BB005986FF17088F61362A8A9A1727D64AF36B281E4D932E07717BF50B8305BFD36F18F73B1154BB6FF6D9E2DF53A3C71DD66F94829BE1613151C3B729482A713112F6912773A62564F551693D1310DB3076CDE24F4556A51DCADC27E537E1E2AB53354F8DCC0441A706328720126AD8AA7AA455E3D93BAD77039E9B73596A68112C83909E524DD9AF48100A610E27FD5C419AC4F4565A9E438A9B5E466E8137D9C144A1C81A93F0493C38040E7B9144F6F3EC4E6DF29ABDE2716F762856BF5D60A43645B4493FDB0754CEFCBA037EA694DB0203010001'H
|
|
|
},
|
|
|
extensions {
|
|
|
{
|
|
|
extnID {2 5 29 35} -- id-ce-authorityKeyIdentifier --,
|
|
|
extnValue EXTENSION: AuthorityKeyIdentifier: {
|
|
|
keyIdentifier '39DAFFCA28148AA8741308B9E40EA9D2FA7E9D69'H
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
extnID {2 5 29 14} -- id-ce-subjectKeyIdentifier --,
|
|
|
extnValue EXTENSION: KeyIdentifier: '4CCAF046405EC0DAF417682805C993E47B16CCEA'H
|
|
|
},
|
|
|
{
|
|
|
extnID {2 5 29 15} -- id-ce-keyUsage --,
|
|
|
critical TRUE,
|
|
|
extnValue EXTENSION: KeyUsage: '101'B -- digitalSignature | keyEncipherment --
|
|
|
},
|
|
|
{
|
|
|
extnID {2 5 29 19} -- id-ce-basicConstraints --,
|
|
|
critical TRUE,
|
|
|
extnValue EXTENSION: BasicConstraints: { }
|
|
|
},
|
|
|
{
|
|
|
extnID {2 5 29 37} -- id-ce-extKeyUsage --,
|
|
|
extnValue EXTENSION: ExtKeyUsageSyntax: {
|
|
|
{1 3 6 1 5 5 7 3 1} -- id-kp-serverAuth --,
|
|
|
{1 3 6 1 5 5 7 3 2} -- id-kp-clientAuth --
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
extnID {2 5 29 32} -- id-ce-certificatePolicies --,
|
|
|
extnValue EXTENSION: CertificatePolicies: {
|
|
|
{
|
|
|
policyIdentifier {1 3 6 1 4 1 6449 1 2 1 5 1},
|
|
|
policyQualifiers {
|
|
|
{
|
|
|
policyQualifierId {1 3 6 1 5 5 7 2 1} -- id-qt-cps --,
|
|
|
qualifier CPSuri: "https://secure.comodo.com/CPS"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
extnID {2 5 29 31} -- id-ce-cRLDistributionPoints --,
|
|
|
extnValue EXTENSION: CRLDistributionPoints: {
|
|
|
{
|
|
|
distributionPoint fullName : {
|
|
|
uniformResourceIdentifier : "http://crl.comodoca.com/COMODORSAExtendedValidationSecureServerCA.crl"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
extnID {1 3 6 1 5 5 7 1 1} -- id-pe-authorityInfoAccess --,
|
|
|
extnValue EXTENSION: AuthorityInfoAccessSyntax: {
|
|
|
{
|
|
|
accessMethod {1 3 6 1 5 5 7 48 2} -- id-ad-caIssuers --,
|
|
|
accessLocation uniformResourceIdentifier : "http://crt.comodoca.com/COMODORSAExtendedValidationSecureServerCA.crt"
|
|
|
},
|
|
|
{
|
|
|
accessMethod {1 3 6 1 5 5 7 48 1} -- id-ad-ocsp --,
|
|
|
accessLocation uniformResourceIdentifier : "http://ocsp.comodoca.com"
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
extnID {2 5 29 17} -- id-ce-subjectAltName --,
|
|
|
extnValue EXTENSION: GeneralNames: {
|
|
|
dNSName : "www.itu.int",
|
|
|
dNSName : "itu.int"
|
|
|
}
|
|
|
},
|
|
|
{
|
|
|
extnID {1 3 6 1 4 1 11129 2 4 2},
|
|
|
extnValue EXTENSION: '016A007600A4B90990B418581487BB13A2CC67700A3C359804F91BDFB8E377CD0EC80DDC100000015B157198BC000004030047304502207B8D96679302EFB061C71EF0762BD9FF1938FEB604DFAB820B00684F35E90A69022100DAE8F2A04DFF20536CDF0B9FA56FCE19D4DE8227FC9BB17E0DC7383DF0F8772A0077005614069A2FD7C2ECD3F5E1BD44B23EC74676B9BC99115CC0EF949855D689D0DD0000015B1571964F0000040300483046022100D6B0C8B655FA18E02521178AD3F2AB2D41672C7177A853B4F8124218F0788270022100A9827EAAE294130A3D23D224511533B2BEAA972EF59B592EEC126A045202A978007700EE4BBDB775CE60BAE142691FABE19E66A30F7E5FB072D88300C47B897AA8FDCB0000015B1571988600000403004830460221008451A06AB517D32A0DA9408E28C50E05DB7B35576A188B80F567A6A09C4D7E49022100CE45402927D318B911F922F5F856DEB05DC36CC30B71DFCD7E12E8FCC0B86587'H
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
algorithmIdentifier {
|
|
|
algorithm {1 2 840 113549 1 1 11} -- sha256WithRSAEncryption --,
|
|
|
parameters ''H
|
|
|
},
|
|
|
signature '4ABE511D378ECD7FB8B69BED9588E11B1550D84E626E0391C56D6780B15819AE18248259399A72DAEFADE86B6C26F6C47ACCC44E43B4D142EFEB55A14C478056204A902A2759CF0F1CE5CD74F08D257055A89BCA627A5B9D19DDA1E516346E323E43F7B613935E4605BA8AFCB6DFB52D7ADC4CCB0029A7E9FFD62ED19738D8531B048C97ECA51082FD50974958165D9E6C1D5279DDE1F0019C80E773E2E801C8FAF7FE72FBFB8FA4AEBFE96CB38B1E88940255BCB1C8E578DAE9A188AD5F38D626E73A61E37DC63686789066CAF732FE4C3B1CCDF98B80F5A0BFC54B4DDB5B7041F471DC795941816CD5949E8ED0D47C127BE9D1B182EE8C4B42CD711CC6EA89'H
|
|
|
}
|
|
|
|
|
|
|
|
|
This is a little bit *better*... but not entirely satisfying ! The specification from
|
|
|
the RFC5912 enables to decode properly all issuer information. There are still some
|
|
|
subject and extension information which are not decoded entirely. The algorithm
|
|
|
parameters are not decoded too.
|
|
|
|
|
|
To conclude here, we see that we will need in anyway to extend manually the ASN.1 specification
|
|
|
in order to include structures describing all existing issuer, subject, extension and
|
|
|
algorithm parameters' structures, to be able to use the pycrate ASN.1 runtime to
|
|
|
properly decode X.509 certificate.
|
|
|
|
|
|
|