You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.1 KiB
190 lines
6.1 KiB
import binascii |
|
import base64 |
|
|
|
class UnexpectedDER(Exception): |
|
pass |
|
|
|
def encode_constructed(tag, value): |
|
return chr(0xa0+tag) + encode_length(len(value)) + value |
|
def encode_integer(r): |
|
assert r >= 0 # can't support negative numbers yet |
|
h = "%x" % r |
|
if len(h)%2: |
|
h = "0" + h |
|
s = binascii.unhexlify(h) |
|
if ord(s[0]) <= 0x7f: |
|
return "\x02" + chr(len(s)) + s |
|
else: |
|
# DER integers are two's complement, so if the first byte is |
|
# 0x80-0xff then we need an extra 0x00 byte to prevent it from |
|
# looking negative. |
|
return "\x02" + chr(len(s)+1) + "\x00" + s |
|
|
|
def encode_bitstring(s): |
|
return "\x03" + encode_length(len(s)) + s |
|
def encode_octet_string(s): |
|
return "\x04" + encode_length(len(s)) + s |
|
def encode_oid(first, second, *pieces): |
|
assert first <= 2 |
|
assert second <= 39 |
|
encoded_pieces = [chr(40*first+second)] + [encode_number(p) |
|
for p in pieces] |
|
body = "".join(encoded_pieces) |
|
return "\x06" + encode_length(len(body)) + body |
|
def encode_sequence(*encoded_pieces): |
|
total_len = sum([len(p) for p in encoded_pieces]) |
|
return "\x30" + encode_length(total_len) + "".join(encoded_pieces) |
|
def encode_number(n): |
|
b128_digits = [] |
|
while n: |
|
b128_digits.insert(0, (n & 0x7f) | 0x80) |
|
n = n >> 7 |
|
if not b128_digits: |
|
b128_digits.append(0) |
|
b128_digits[-1] &= 0x7f |
|
return "".join([chr(d) for d in b128_digits]) |
|
|
|
def remove_constructed(string): |
|
s0 = ord(string[0]) |
|
if (s0 & 0xe0) != 0xa0: |
|
raise UnexpectedDER("wanted constructed tag (0xa0-0xbf), got 0x%02x" |
|
% s0) |
|
tag = s0 & 0x1f |
|
length, llen = read_length(string[1:]) |
|
body = string[1+llen:1+llen+length] |
|
rest = string[1+llen+length:] |
|
return tag, body, rest |
|
|
|
def remove_sequence(string): |
|
if not string.startswith("\x30"): |
|
raise UnexpectedDER("wanted sequence (0x30), got 0x%02x" % |
|
ord(string[0])) |
|
length, lengthlength = read_length(string[1:]) |
|
endseq = 1+lengthlength+length |
|
return string[1+lengthlength:endseq], string[endseq:] |
|
|
|
def remove_octet_string(string): |
|
if not string.startswith("\x04"): |
|
raise UnexpectedDER("wanted octetstring (0x04), got 0x%02x" % |
|
ord(string[0])) |
|
length, llen = read_length(string[1:]) |
|
body = string[1+llen:1+llen+length] |
|
rest = string[1+llen+length:] |
|
return body, rest |
|
|
|
def remove_object(string): |
|
if not string.startswith("\x06"): |
|
raise UnexpectedDER("wanted object (0x06), got 0x%02x" % |
|
ord(string[0])) |
|
length, lengthlength = read_length(string[1:]) |
|
body = string[1+lengthlength:1+lengthlength+length] |
|
rest = string[1+lengthlength+length:] |
|
numbers = [] |
|
while body: |
|
n, ll = read_number(body) |
|
numbers.append(n) |
|
body = body[ll:] |
|
n0 = numbers.pop(0) |
|
first = n0//40 |
|
second = n0-(40*first) |
|
numbers.insert(0, first) |
|
numbers.insert(1, second) |
|
return tuple(numbers), rest |
|
|
|
def remove_integer(string): |
|
if not string.startswith("\x02"): |
|
raise UnexpectedDER("wanted integer (0x02), got 0x%02x" % |
|
ord(string[0])) |
|
length, llen = read_length(string[1:]) |
|
numberbytes = string[1+llen:1+llen+length] |
|
rest = string[1+llen+length:] |
|
assert ord(numberbytes[0]) < 0x80 # can't support negative numbers yet |
|
return int(binascii.hexlify(numberbytes), 16), rest |
|
|
|
def read_number(string): |
|
number = 0 |
|
llen = 0 |
|
# base-128 big endian, with b7 set in all but the last byte |
|
while True: |
|
if llen > len(string): |
|
raise UnexpectedDER("ran out of length bytes") |
|
number = number << 7 |
|
d = ord(string[llen]) |
|
number += (d & 0x7f) |
|
llen += 1 |
|
if not d & 0x80: |
|
break |
|
return number, llen |
|
|
|
def encode_length(l): |
|
assert l >= 0 |
|
if l < 0x80: |
|
return chr(l) |
|
s = "%x" % l |
|
if len(s)%2: |
|
s = "0"+s |
|
s = binascii.unhexlify(s) |
|
llen = len(s) |
|
return chr(0x80|llen) + s |
|
|
|
def read_length(string): |
|
if not (ord(string[0]) & 0x80): |
|
# short form |
|
return (ord(string[0]) & 0x7f), 1 |
|
# else long-form: b0&0x7f is number of additional base256 length bytes, |
|
# big-endian |
|
llen = ord(string[0]) & 0x7f |
|
if llen > len(string)-1: |
|
raise UnexpectedDER("ran out of length bytes") |
|
return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen |
|
|
|
def remove_bitstring(string): |
|
if not string.startswith("\x03"): |
|
raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % |
|
ord(string[0])) |
|
length, llen = read_length(string[1:]) |
|
body = string[1+llen:1+llen+length] |
|
rest = string[1+llen+length:] |
|
return body, rest |
|
|
|
# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) |
|
|
|
|
|
# signatures: (from RFC3279) |
|
# ansi-X9-62 OBJECT IDENTIFIER ::= { |
|
# iso(1) member-body(2) us(840) 10045 } |
|
# |
|
# id-ecSigType OBJECT IDENTIFIER ::= { |
|
# ansi-X9-62 signatures(4) } |
|
# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { |
|
# id-ecSigType 1 } |
|
## so 1,2,840,10045,4,1 |
|
## so 0x42, .. .. |
|
|
|
# Ecdsa-Sig-Value ::= SEQUENCE { |
|
# r INTEGER, |
|
# s INTEGER } |
|
|
|
# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } |
|
# |
|
# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } |
|
|
|
# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) |
|
# secp224r1 OBJECT IDENTIFIER ::= { |
|
# iso(1) identified-organization(3) certicom(132) curve(0) 33 } |
|
# and the secp384r1 is (t=06,l=05,v=2b81040022) |
|
# secp384r1 OBJECT IDENTIFIER ::= { |
|
# iso(1) identified-organization(3) certicom(132) curve(0) 34 } |
|
|
|
def unpem(pem): |
|
d = "".join([l.strip() for l in pem.split("\n") |
|
if l and not l.startswith("-----")]) |
|
return base64.b64decode(d) |
|
def topem(der, name): |
|
b64 = base64.b64encode(der) |
|
lines = ["-----BEGIN %s-----\n" % name] |
|
lines.extend([b64[start:start+64]+"\n" |
|
for start in range(0, len(b64), 64)]) |
|
lines.append("-----END %s-----\n" % name) |
|
return "".join(lines) |
|
|
|
|