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.

199 lines
6.9 KiB

4 years ago
  1. from __future__ import division
  2. import binascii
  3. import base64
  4. from .six import int2byte, b, integer_types, text_type
  5. class UnexpectedDER(Exception):
  6. pass
  7. def encode_constructed(tag, value):
  8. return int2byte(0xa0+tag) + encode_length(len(value)) + value
  9. def encode_integer(r):
  10. assert r >= 0 # can't support negative numbers yet
  11. h = ("%x" % r).encode()
  12. if len(h) % 2:
  13. h = b("0") + h
  14. s = binascii.unhexlify(h)
  15. num = s[0] if isinstance(s[0], integer_types) else ord(s[0])
  16. if num <= 0x7f:
  17. return b("\x02") + int2byte(len(s)) + s
  18. else:
  19. # DER integers are two's complement, so if the first byte is
  20. # 0x80-0xff then we need an extra 0x00 byte to prevent it from
  21. # looking negative.
  22. return b("\x02") + int2byte(len(s)+1) + b("\x00") + s
  23. def encode_bitstring(s):
  24. return b("\x03") + encode_length(len(s)) + s
  25. def encode_octet_string(s):
  26. return b("\x04") + encode_length(len(s)) + s
  27. def encode_oid(first, second, *pieces):
  28. assert first <= 2
  29. assert second <= 39
  30. encoded_pieces = [int2byte(40*first+second)] + [encode_number(p)
  31. for p in pieces]
  32. body = b('').join(encoded_pieces)
  33. return b('\x06') + encode_length(len(body)) + body
  34. def encode_sequence(*encoded_pieces):
  35. total_len = sum([len(p) for p in encoded_pieces])
  36. return b('\x30') + encode_length(total_len) + b('').join(encoded_pieces)
  37. def encode_number(n):
  38. b128_digits = []
  39. while n:
  40. b128_digits.insert(0, (n & 0x7f) | 0x80)
  41. n = n >> 7
  42. if not b128_digits:
  43. b128_digits.append(0)
  44. b128_digits[-1] &= 0x7f
  45. return b('').join([int2byte(d) for d in b128_digits])
  46. def remove_constructed(string):
  47. s0 = string[0] if isinstance(string[0], integer_types) else ord(string[0])
  48. if (s0 & 0xe0) != 0xa0:
  49. raise UnexpectedDER("wanted constructed tag (0xa0-0xbf), got 0x%02x"
  50. % s0)
  51. tag = s0 & 0x1f
  52. length, llen = read_length(string[1:])
  53. body = string[1+llen:1+llen+length]
  54. rest = string[1+llen+length:]
  55. return tag, body, rest
  56. def remove_sequence(string):
  57. if not string.startswith(b("\x30")):
  58. n = string[0] if isinstance(string[0], integer_types) else ord(string[0])
  59. raise UnexpectedDER("wanted sequence (0x30), got 0x%02x" % n)
  60. length, lengthlength = read_length(string[1:])
  61. endseq = 1+lengthlength+length
  62. return string[1+lengthlength:endseq], string[endseq:]
  63. def remove_octet_string(string):
  64. if not string.startswith(b("\x04")):
  65. n = string[0] if isinstance(string[0], integer_types) else ord(string[0])
  66. raise UnexpectedDER("wanted octetstring (0x04), got 0x%02x" % n)
  67. length, llen = read_length(string[1:])
  68. body = string[1+llen:1+llen+length]
  69. rest = string[1+llen+length:]
  70. return body, rest
  71. def remove_object(string):
  72. if not string.startswith(b("\x06")):
  73. n = string[0] if isinstance(string[0], integer_types) else ord(string[0])
  74. raise UnexpectedDER("wanted object (0x06), got 0x%02x" % n)
  75. length, lengthlength = read_length(string[1:])
  76. body = string[1+lengthlength:1+lengthlength+length]
  77. rest = string[1+lengthlength+length:]
  78. numbers = []
  79. while body:
  80. n, ll = read_number(body)
  81. numbers.append(n)
  82. body = body[ll:]
  83. n0 = numbers.pop(0)
  84. first = n0//40
  85. second = n0-(40*first)
  86. numbers.insert(0, first)
  87. numbers.insert(1, second)
  88. return tuple(numbers), rest
  89. def remove_integer(string):
  90. if not string.startswith(b("\x02")):
  91. n = string[0] if isinstance(string[0], integer_types) else ord(string[0])
  92. raise UnexpectedDER("wanted integer (0x02), got 0x%02x" % n)
  93. length, llen = read_length(string[1:])
  94. numberbytes = string[1+llen:1+llen+length]
  95. rest = string[1+llen+length:]
  96. nbytes = numberbytes[0] if isinstance(numberbytes[0], integer_types) else ord(numberbytes[0])
  97. assert nbytes < 0x80 # can't support negative numbers yet
  98. return int(binascii.hexlify(numberbytes), 16), rest
  99. def read_number(string):
  100. number = 0
  101. llen = 0
  102. # base-128 big endian, with b7 set in all but the last byte
  103. while True:
  104. if llen > len(string):
  105. raise UnexpectedDER("ran out of length bytes")
  106. number = number << 7
  107. d = string[llen] if isinstance(string[llen], integer_types) else ord(string[llen])
  108. number += (d & 0x7f)
  109. llen += 1
  110. if not d & 0x80:
  111. break
  112. return number, llen
  113. def encode_length(l):
  114. assert l >= 0
  115. if l < 0x80:
  116. return int2byte(l)
  117. s = ("%x" % l).encode()
  118. if len(s)%2:
  119. s = b("0")+s
  120. s = binascii.unhexlify(s)
  121. llen = len(s)
  122. return int2byte(0x80|llen) + s
  123. def read_length(string):
  124. num = string[0] if isinstance(string[0], integer_types) else ord(string[0])
  125. if not (num & 0x80):
  126. # short form
  127. return (num & 0x7f), 1
  128. # else long-form: b0&0x7f is number of additional base256 length bytes,
  129. # big-endian
  130. llen = num & 0x7f
  131. if llen > len(string)-1:
  132. raise UnexpectedDER("ran out of length bytes")
  133. return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen
  134. def remove_bitstring(string):
  135. num = string[0] if isinstance(string[0], integer_types) else ord(string[0])
  136. if not string.startswith(b("\x03")):
  137. raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num)
  138. length, llen = read_length(string[1:])
  139. body = string[1+llen:1+llen+length]
  140. rest = string[1+llen+length:]
  141. return body, rest
  142. # SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING)
  143. # signatures: (from RFC3279)
  144. # ansi-X9-62 OBJECT IDENTIFIER ::= {
  145. # iso(1) member-body(2) us(840) 10045 }
  146. #
  147. # id-ecSigType OBJECT IDENTIFIER ::= {
  148. # ansi-X9-62 signatures(4) }
  149. # ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
  150. # id-ecSigType 1 }
  151. ## so 1,2,840,10045,4,1
  152. ## so 0x42, .. ..
  153. # Ecdsa-Sig-Value ::= SEQUENCE {
  154. # r INTEGER,
  155. # s INTEGER }
  156. # id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 }
  157. #
  158. # id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 }
  159. # I think the secp224r1 identifier is (t=06,l=05,v=2b81040021)
  160. # secp224r1 OBJECT IDENTIFIER ::= {
  161. # iso(1) identified-organization(3) certicom(132) curve(0) 33 }
  162. # and the secp384r1 is (t=06,l=05,v=2b81040022)
  163. # secp384r1 OBJECT IDENTIFIER ::= {
  164. # iso(1) identified-organization(3) certicom(132) curve(0) 34 }
  165. def unpem(pem):
  166. if isinstance(pem, text_type):
  167. pem = pem.encode()
  168. d = b("").join([l.strip() for l in pem.split(b("\n"))
  169. if l and not l.startswith(b("-----"))])
  170. return base64.b64decode(d)
  171. def topem(der, name):
  172. b64 = base64.b64encode(der)
  173. lines = [("-----BEGIN %s-----\n" % name).encode()]
  174. lines.extend([b64[start:start+64]+b("\n")
  175. for start in range(0, len(b64), 64)])
  176. lines.append(("-----END %s-----\n" % name).encode())
  177. return b("").join(lines)