eerimoq / asn1tools

ASN.1 parsing, encoding and decoding.
MIT License
293 stars 100 forks source link

struct.error: required argument is not an integer #27

Closed Bujko closed 6 years ago

Bujko commented 6 years ago

Hi!

Trying to run the following code but I got the following error.

File "EnrolementCredential.py", line 49, in <module>
    'signature' : ('ecdsaNistP256Signature', { 'r' : ('x-only' , xstr2_EC), 's' : ystr2_EC})
struct.error: required argument is not an integer

I got the same error if I delete the optional signature value (the last row in my code down below). I used the same values by pyasn1 but should change because of the COER encoding.

This is the ASN.1 implementation

ToBeSignedCertificate ::=
        SEQUENCE {  id                      CertificateId,
                    cracaId                 HashedId3,
                    crlSeries               CrlSeries,
                    validityPeriod          ValidityPeriod,
                    region                  GeographicRegion OPTIONAL,
                    assuranceLevel          SubjectAssurance OPTIONAL,
                    appPermissions          SequenceOfPsidSsp OPTIONAL,
                    certIssuePermissions    SequenceOfPsidGroupPermissions OPTIONAL,
                    certRequestPermissions  SequenceOfPsidGroupPermissions OPTIONAL,
                    canRequestRollover      NULL OPTIONAL,
                    encryptionKey           PublicEncryptionKey OPTIONAL,
                    verifyKeyIndicator      VerificationKeyIndicator,
                    ...
}

This is my code:

#EneolementCredential

import asn1tools
import datetime, calendar, time
from Crypto.PublicKey import ECC

issuerid  = bytes.fromhex('0101010101010101') #Example value
cracaId   = bytes.fromhex('020202') # Identifies the Certificate Revocation Authorization CA (example)
crlSeries = 5 #Example value

t = (2004, 1, 1, 0, 0, 0, 3, 1, 0) # TAI time type secs from this moment
generation_time = calendar.timegm(time.gmtime())-time.mktime(t)
#duration
duration_type = 'hours' # My choice
in_hours      = 8761 #One year (example)

f = open('myprivatekey.der','rb')
EC_key = ECC.import_key(f.read())
f.close()

x_EC = EC_key.pointQ.x  # x and y key point conversation
y_EC = EC_key.pointQ.y

xstr1_EC = str(hex(int(x_EC)))
xstr2_EC = bytes.fromhex(xstr1_EC[2:66])
ystr1_EC = str(hex(int(y_EC)))
ystr2_EC = bytes.fromhex(ystr1_EC[2:66])

xstr2_EC = b'0808080808080808080808080808080808080808080808080808080808080808'
ystr2_EC = b'0808080808080808080808080808080808080808080808080808080808080808'

Certificate = asn1tools.compile_files('EtsiTs102941MessagesItss.asn',codec='oer')

encoded = Certificate.encode('CertificateBase',{
                                                'version'   : 3,
                                                'type'      : 'explicit',
                                                'issuer'    : ('sha256AndDigest', issuerid),
                                                'toBeSigned':   {
                                                                    'id'                : ('none', ''), #Possibly none
                                                                    'cracaId'           : cracaId,
                                                                    'crlSeries'         : crlSeries,
                                                                    'validityPeriod'    :   {
                                                                                                'start'     : generation_time,
                                                                                                'duration'  : (duration_type, in_hours)
                                                                                            },
                                                                    'verifyKeyIndicator': ('verificationKey' , ('ecdsaNistP256' , ('uncompressed', {'x' : xstr2_EC  , 'y' : ystr2_EC})))
                                                                },
                                                'signature' : ('ecdsaNistP256Signature', { 'r' : ('x-only' , xstr2_EC), 's' : ystr2_EC})
                                               }
                            )

Using the ASN.1 code of the Ieee1609.2 standard

I am newbie at ASN.1. I do something wrong or something else is the problem? The COER encoding or the '...' part could be the problem?

eerimoq commented 6 years ago

Please attach EtsiTs102941MessagesItss.asn to make it possible to easily reproduce the issue.

Bujko commented 6 years ago

Here it is with the key included.

Issue.zip

eerimoq commented 6 years ago

The problem is that a floating point number is not allowed encoding an integer. Replace generation_time = calendar.timegm(time.gmtime())-time.mktime(t) with generation_time = int(calendar.timegm(time.gmtime())-time.mktime(t)) and the example works.

eerimoq commented 6 years ago

The syntax in this specification is unlike anything I've seen before. I'll do my best adding support for the missing functionality.

Bujko commented 6 years ago

Thank you so much for the fast response. It is working correctli.

eerimoq commented 6 years ago

Do you have the unmodified ASN.1-specification as well? I could use it as a test case when inplementing support for more ASN.1 syntax.

Bujko commented 6 years ago

Here are the files.

With pyasn1 I can not use this definitions (it failed on "WITH COMPONENTS" and some other specific parts). Also the import export part is not working. With pyasn1 and I could not figure out how can I do this with your library.

Hope that it is helping you and I cut of every modifications. I think that the EtsiTs103097 and the Ieee files will be enough because the EtsiTs102941 asn file includes more than one asn1 architecture.

I am working with the Ieee 1609.2 (2016) and the EtsiTs103097 v 1.3.1 standards.

These defines COER encoding.

I have try to find out, based on the documentation, that what kind of OER do you use. Is it BASIC or CANONICAL OER?

If I can help you with anything let me know about it.

Iee1609dot2_asn1_files.zip

eerimoq commented 6 years ago

Thank you very much for the files! I'll have a look at them and likely improve the parser soon.

The OER codec in asn1tools is implementing the BASIC version of the encoding. I'm continuously improving asn1tools and adding more encodings. COER is likely to be added in the future, along with CER. Feel free to start the implementation yourself and we can implement it together. We can probably reuse some of the OER implementation already in place in asn1tools/codecs/oer.py.

Adding support for more syntax in the parser is quite challenging as the parser implementation is very hard to understand. I get confused every time I open that file. I wouldn't recommend you to help out in that file.

Bujko commented 6 years ago

I have created IEEE 1609dot2 certificate and I passed it to a java code. It seems to be correct COER encoded.

eerimoq commented 6 years ago

I fixed a bug not allowing EXPORTS ALL;, and added support for types in constraints. Now only a few manual modifications have to be done to make IEEE 1609dot2 compile.

Replace this section with:

NinetyDegreeInt ::= INTEGER {
    min (-900000000),
    max (900000000),
    unknown (900000001)
} (-900000000..900000001)

KnownLatitude ::= NinetyDegreeInt (-900000000..900000000)

UnknownLatitude ::= NinetyDegreeInt (900000001) -- Minus 90 deg to +90
                                              -- deg in microdegree
                                              -- intervals

OneEightyDegreeInt ::= INTEGER {
    min (-1799999999),
    max (1800000000),
    unknown (1800000001)
} (-1799999999..1800000001)

KnownLongitude ::= OneEightyDegreeInt (-1799999999..1800000000)

UnknownLongitude ::= OneEightyDegreeInt (1800000001)

Here is a test that does similar encoding as in your example: https://github.com/eerimoq/asn1tools/blob/master/tests/test_oer.py#L685-L751