Split ASN.1 utilities out into their own file.
This commit is contained in:
+11
-99
@@ -1,21 +1,3 @@
|
|||||||
# This software is provided 'as-is', without any express or implied
|
|
||||||
# warranty. In no event will the author be held liable for any damages
|
|
||||||
# arising from the use of this software.
|
|
||||||
#
|
|
||||||
# Permission is granted to anyone to use this software for any purpose,
|
|
||||||
# including commercial applications, and to alter it and redistribute it
|
|
||||||
# freely, subject to the following restrictions:
|
|
||||||
#
|
|
||||||
# 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
# claim that you wrote the original software. If you use this software
|
|
||||||
# in a product, an acknowledgment in the product documentation would be
|
|
||||||
# appreciated but is not required.
|
|
||||||
# 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
# misrepresented as being the original software.
|
|
||||||
# 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2008 Greg Hewgill http://hewgill.com
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
@@ -23,6 +5,17 @@ import time
|
|||||||
|
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
|
|
||||||
|
from dkim.asn1 import (
|
||||||
|
asn1_build,
|
||||||
|
asn1_parse,
|
||||||
|
BIT_STRING,
|
||||||
|
INTEGER,
|
||||||
|
SEQUENCE,
|
||||||
|
OBJECT_IDENTIFIER,
|
||||||
|
OCTET_STRING,
|
||||||
|
NULL,
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Simple",
|
"Simple",
|
||||||
"Relaxed",
|
"Relaxed",
|
||||||
@@ -208,13 +201,6 @@ def validate_signature_fields(sig, debuglog=None):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
INTEGER = 0x02
|
|
||||||
BIT_STRING = 0x03
|
|
||||||
OCTET_STRING = 0x04
|
|
||||||
NULL = 0x05
|
|
||||||
OBJECT_IDENTIFIER = 0x06
|
|
||||||
SEQUENCE = 0x30
|
|
||||||
|
|
||||||
ASN1_Object = [
|
ASN1_Object = [
|
||||||
(SEQUENCE, [
|
(SEQUENCE, [
|
||||||
(SEQUENCE, [
|
(SEQUENCE, [
|
||||||
@@ -246,80 +232,6 @@ ASN1_RSAPrivateKey = [
|
|||||||
])
|
])
|
||||||
]
|
]
|
||||||
|
|
||||||
def asn1_parse(template, data):
|
|
||||||
"""Parse a data structure according to ASN.1 template.
|
|
||||||
|
|
||||||
@param template: A list of tuples comprising the ASN.1 template.
|
|
||||||
@param data: A list of bytes to parse.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
r = []
|
|
||||||
i = 0
|
|
||||||
for t in template:
|
|
||||||
tag = ord(data[i])
|
|
||||||
i += 1
|
|
||||||
if tag == t[0]:
|
|
||||||
length = ord(data[i])
|
|
||||||
i += 1
|
|
||||||
if length & 0x80:
|
|
||||||
n = length & 0x7f
|
|
||||||
length = 0
|
|
||||||
for j in range(n):
|
|
||||||
length = (length << 8) | ord(data[i])
|
|
||||||
i += 1
|
|
||||||
if tag == INTEGER:
|
|
||||||
n = 0
|
|
||||||
for j in range(length):
|
|
||||||
n = (n << 8) | ord(data[i])
|
|
||||||
i += 1
|
|
||||||
r.append(n)
|
|
||||||
elif tag == BIT_STRING:
|
|
||||||
r.append(data[i:i+length])
|
|
||||||
i += length
|
|
||||||
elif tag == NULL:
|
|
||||||
assert length == 0
|
|
||||||
r.append(None)
|
|
||||||
elif tag == OBJECT_IDENTIFIER:
|
|
||||||
r.append(data[i:i+length])
|
|
||||||
i += length
|
|
||||||
elif tag == SEQUENCE:
|
|
||||||
r.append(asn1_parse(t[1], data[i:i+length]))
|
|
||||||
i += length
|
|
||||||
else:
|
|
||||||
raise KeyFormatError("Unexpected tag in template: %02x" % tag)
|
|
||||||
else:
|
|
||||||
raise KeyFormatError("Unexpected tag (got %02x, expecting %02x)" % (tag, t[0]))
|
|
||||||
return r
|
|
||||||
|
|
||||||
def asn1_length(n):
|
|
||||||
"""Return a string representing a field length in ASN.1 format."""
|
|
||||||
assert n >= 0
|
|
||||||
if n < 0x7f:
|
|
||||||
return chr(n)
|
|
||||||
r = ""
|
|
||||||
while n > 0:
|
|
||||||
r = chr(n & 0xff) + r
|
|
||||||
n >>= 8
|
|
||||||
return r
|
|
||||||
|
|
||||||
def asn1_build(node):
|
|
||||||
"""Build an ASN.1 data structure based on pairs of (type, data)."""
|
|
||||||
if node[0] == OCTET_STRING:
|
|
||||||
return chr(OCTET_STRING) + asn1_length(len(node[1])) + node[1]
|
|
||||||
if node[0] == NULL:
|
|
||||||
assert node[1] is None
|
|
||||||
return chr(NULL) + asn1_length(0)
|
|
||||||
elif node[0] == OBJECT_IDENTIFIER:
|
|
||||||
return chr(OBJECT_IDENTIFIER) + asn1_length(len(node[1])) + node[1]
|
|
||||||
elif node[0] == SEQUENCE:
|
|
||||||
r = ""
|
|
||||||
for x in node[1]:
|
|
||||||
r += asn1_build(x)
|
|
||||||
return chr(SEQUENCE) + asn1_length(len(r)) + r
|
|
||||||
else:
|
|
||||||
raise InternalError("Unexpected tag in template: %02x" % node[0])
|
|
||||||
|
|
||||||
# These values come from RFC 3447, section 9.2 Notes, page 43.
|
# These values come from RFC 3447, section 9.2 Notes, page 43.
|
||||||
HASHID_SHA1 = "\x2b\x0e\x03\x02\x1a"
|
HASHID_SHA1 = "\x2b\x0e\x03\x02\x1a"
|
||||||
HASHID_SHA256 = "\x60\x86\x48\x01\x65\x03\x04\x02\x01"
|
HASHID_SHA256 = "\x60\x86\x48\x01\x65\x03\x04\x02\x01"
|
||||||
|
|||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
# This software is provided 'as-is', without any express or implied
|
||||||
|
# warranty. In no event will the author be held liable for any damages
|
||||||
|
# arising from the use of this software.
|
||||||
|
#
|
||||||
|
# Permission is granted to anyone to use this software for any purpose,
|
||||||
|
# including commercial applications, and to alter it and redistribute it
|
||||||
|
# freely, subject to the following restrictions:
|
||||||
|
#
|
||||||
|
# 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
# claim that you wrote the original software. If you use this software
|
||||||
|
# in a product, an acknowledgment in the product documentation would be
|
||||||
|
# appreciated but is not required.
|
||||||
|
# 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
# misrepresented as being the original software.
|
||||||
|
# 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2008 Greg Hewgill http://hewgill.com
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'asn1_build',
|
||||||
|
'asn1_parse',
|
||||||
|
'ASN1FormatError',
|
||||||
|
'BIT_STRING',
|
||||||
|
'INTEGER',
|
||||||
|
'SEQUENCE',
|
||||||
|
'OBJECT_IDENTIFIER',
|
||||||
|
'OCTET_STRING',
|
||||||
|
'NULL',
|
||||||
|
]
|
||||||
|
|
||||||
|
INTEGER = 0x02
|
||||||
|
BIT_STRING = 0x03
|
||||||
|
OCTET_STRING = 0x04
|
||||||
|
NULL = 0x05
|
||||||
|
OBJECT_IDENTIFIER = 0x06
|
||||||
|
SEQUENCE = 0x30
|
||||||
|
|
||||||
|
|
||||||
|
class ASN1FormatError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def asn1_parse(template, data):
|
||||||
|
"""Parse a data structure according to ASN.1 template.
|
||||||
|
|
||||||
|
@param template: A list of tuples comprising the ASN.1 template.
|
||||||
|
@param data: A list of bytes to parse.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
r = []
|
||||||
|
i = 0
|
||||||
|
for t in template:
|
||||||
|
tag = ord(data[i])
|
||||||
|
i += 1
|
||||||
|
if tag == t[0]:
|
||||||
|
length = ord(data[i])
|
||||||
|
i += 1
|
||||||
|
if length & 0x80:
|
||||||
|
n = length & 0x7f
|
||||||
|
length = 0
|
||||||
|
for j in range(n):
|
||||||
|
length = (length << 8) | ord(data[i])
|
||||||
|
i += 1
|
||||||
|
if tag == INTEGER:
|
||||||
|
n = 0
|
||||||
|
for j in range(length):
|
||||||
|
n = (n << 8) | ord(data[i])
|
||||||
|
i += 1
|
||||||
|
r.append(n)
|
||||||
|
elif tag == BIT_STRING:
|
||||||
|
r.append(data[i:i+length])
|
||||||
|
i += length
|
||||||
|
elif tag == NULL:
|
||||||
|
assert length == 0
|
||||||
|
r.append(None)
|
||||||
|
elif tag == OBJECT_IDENTIFIER:
|
||||||
|
r.append(data[i:i+length])
|
||||||
|
i += length
|
||||||
|
elif tag == SEQUENCE:
|
||||||
|
r.append(asn1_parse(t[1], data[i:i+length]))
|
||||||
|
i += length
|
||||||
|
else:
|
||||||
|
raise ASN1FormatError(
|
||||||
|
"Unexpected tag in template: %02x" % tag)
|
||||||
|
else:
|
||||||
|
raise ASN1FormatError(
|
||||||
|
"Unexpected tag (got %02x, expecting %02x)" % (tag, t[0]))
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def asn1_length(n):
|
||||||
|
"""Return a string representing a field length in ASN.1 format."""
|
||||||
|
assert n >= 0
|
||||||
|
if n < 0x7f:
|
||||||
|
return chr(n)
|
||||||
|
r = ""
|
||||||
|
while n > 0:
|
||||||
|
r = chr(n & 0xff) + r
|
||||||
|
n >>= 8
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def asn1_build(node):
|
||||||
|
"""Build an ASN.1 data structure based on pairs of (type, data)."""
|
||||||
|
if node[0] == OCTET_STRING:
|
||||||
|
return chr(OCTET_STRING) + asn1_length(len(node[1])) + node[1]
|
||||||
|
if node[0] == NULL:
|
||||||
|
assert node[1] is None
|
||||||
|
return chr(NULL) + asn1_length(0)
|
||||||
|
elif node[0] == OBJECT_IDENTIFIER:
|
||||||
|
return chr(OBJECT_IDENTIFIER) + asn1_length(len(node[1])) + node[1]
|
||||||
|
elif node[0] == SEQUENCE:
|
||||||
|
r = ""
|
||||||
|
for x in node[1]:
|
||||||
|
r += asn1_build(x)
|
||||||
|
return chr(SEQUENCE) + asn1_length(len(r)) + r
|
||||||
|
else:
|
||||||
|
raise ASN1FormatError("Unexpected tag in template: %02x" % node[0])
|
||||||
Reference in New Issue
Block a user