Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is dual licensed under the terms of the Apache License, Version 

2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 

3# for complete details. 

4 

5import typing 

6 

7from cryptography.utils import int_to_bytes 

8 

9 

10# This module contains a lightweight DER encoder and decoder. See X.690 for the 

11# specification. This module intentionally does not implement the more complex 

12# BER encoding, only DER. 

13# 

14# Note this implementation treats an element's constructed bit as part of the 

15# tag. This is fine for DER, where the bit is always computable from the type. 

16 

17 

18CONSTRUCTED = 0x20 

19CONTEXT_SPECIFIC = 0x80 

20 

21INTEGER = 0x02 

22BIT_STRING = 0x03 

23OCTET_STRING = 0x04 

24NULL = 0x05 

25OBJECT_IDENTIFIER = 0x06 

26SEQUENCE = 0x10 | CONSTRUCTED 

27SET = 0x11 | CONSTRUCTED 

28PRINTABLE_STRING = 0x13 

29UTC_TIME = 0x17 

30GENERALIZED_TIME = 0x18 

31 

32 

33class DERReader(object): 

34 def __init__(self, data): 

35 self.data = memoryview(data) 

36 

37 def __enter__(self): 

38 return self 

39 

40 def __exit__(self, exc_type, exc_value, tb): 

41 if exc_value is None: 

42 self.check_empty() 

43 

44 def is_empty(self): 

45 return len(self.data) == 0 

46 

47 def check_empty(self): 

48 if not self.is_empty(): 

49 raise ValueError("Invalid DER input: trailing data") 

50 

51 def read_byte(self) -> int: 

52 if len(self.data) < 1: 

53 raise ValueError("Invalid DER input: insufficient data") 

54 ret = self.data[0] 

55 self.data = self.data[1:] 

56 return ret 

57 

58 def read_bytes(self, n) -> memoryview: 

59 if len(self.data) < n: 

60 raise ValueError("Invalid DER input: insufficient data") 

61 ret = self.data[:n] 

62 self.data = self.data[n:] 

63 return ret 

64 

65 def read_any_element(self) -> typing.Tuple[int, "DERReader"]: 

66 tag = self.read_byte() 

67 # Tag numbers 31 or higher are stored in multiple bytes. No supported 

68 # ASN.1 types use such tags, so reject these. 

69 if tag & 0x1F == 0x1F: 

70 raise ValueError("Invalid DER input: unexpected high tag number") 

71 length_byte = self.read_byte() 

72 if length_byte & 0x80 == 0: 

73 # If the high bit is clear, the first length byte is the length. 

74 length = length_byte 

75 else: 

76 # If the high bit is set, the first length byte encodes the length 

77 # of the length. 

78 length_byte &= 0x7F 

79 if length_byte == 0: 

80 raise ValueError( 

81 "Invalid DER input: indefinite length form is not allowed " 

82 "in DER" 

83 ) 

84 length = 0 

85 for i in range(length_byte): 

86 length <<= 8 

87 length |= self.read_byte() 

88 if length == 0: 

89 raise ValueError( 

90 "Invalid DER input: length was not minimally-encoded" 

91 ) 

92 if length < 0x80: 

93 # If the length could have been encoded in short form, it must 

94 # not use long form. 

95 raise ValueError( 

96 "Invalid DER input: length was not minimally-encoded" 

97 ) 

98 body = self.read_bytes(length) 

99 return tag, DERReader(body) 

100 

101 def read_element(self, expected_tag: int) -> "DERReader": 

102 tag, body = self.read_any_element() 

103 if tag != expected_tag: 

104 raise ValueError("Invalid DER input: unexpected tag") 

105 return body 

106 

107 def read_single_element(self, expected_tag: int) -> "DERReader": 

108 with self: 

109 return self.read_element(expected_tag) 

110 

111 def read_optional_element( 

112 self, expected_tag: int 

113 ) -> typing.Optional["DERReader"]: 

114 if len(self.data) > 0 and self.data[0] == expected_tag: 

115 return self.read_element(expected_tag) 

116 return None 

117 

118 def as_integer(self) -> int: 

119 if len(self.data) == 0: 

120 raise ValueError("Invalid DER input: empty integer contents") 

121 first = self.data[0] 

122 if first & 0x80 == 0x80: 

123 raise ValueError("Negative DER integers are not supported") 

124 # The first 9 bits must not all be zero or all be ones. Otherwise, the 

125 # encoding should have been one byte shorter. 

126 if len(self.data) > 1: 

127 second = self.data[1] 

128 if first == 0 and second & 0x80 == 0: 

129 raise ValueError( 

130 "Invalid DER input: integer not minimally-encoded" 

131 ) 

132 return int.from_bytes(self.data, "big") 

133 

134 

135def encode_der_integer(x: int) -> bytes: 

136 if not isinstance(x, int): 

137 raise ValueError("Value must be an integer") 

138 if x < 0: 

139 raise ValueError("Negative integers are not supported") 

140 n = x.bit_length() // 8 + 1 

141 return int_to_bytes(x, n) 

142 

143 

144def encode_der(tag: int, *children: bytes) -> bytes: 

145 length = 0 

146 for child in children: 

147 length += len(child) 

148 chunks = [bytes([tag])] 

149 if length < 0x80: 

150 chunks.append(bytes([length])) 

151 else: 

152 length_bytes = int_to_bytes(length) 

153 chunks.append(bytes([0x80 | len(length_bytes)])) 

154 chunks.append(length_bytes) 

155 chunks.extend(children) 

156 return b"".join(chunks)